JavaScript几种常见的继承方法

Posted ourener

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaScript几种常见的继承方法相关的知识,希望对你有一定的参考价值。

1、call() 方法

call() 方法是与经典的对象冒充方法最相似的方法。它的第一个参数用作 this 的对象。其他参数都直接传递给函数自身

function Huster(name,idNum,college)
    {
        this.name = name;        
        this.idNum = idNum;
        this.college = college;
        this.course = new Array();    
        
        this.addCourse = function(course)//这个方法不能用prototype来定义,如果用的话,子类无法继承该方法
        {                   //用原型prototype定义的方法可以用原型链来继承,call()方法和apply()方法都无法继承
            this.course.push(course);
            console.log(this.course);
        };    
            
    }    
    
    function Autoer(name,idNum)
    {
        this.college = ‘自动化‘;
        Huster.call(this,name,idNum,this.college);//Autoer使用call()方法来继承 Huster
     if (typeof Autoer._initialized == "undefined") 
     {
      Autoer.prototype.sayHobby
= function() //自己的方法可以用原型链prototype定义
      {
        alert(
this.college+‘人喜欢撸代码!‘);     
      };
      Autoer._initialized
= true;
     }
  }
   
  var autoer1
= new Autoer(‘偶人儿‘,‘U123456789‘); //声明一个实例autoer1
  console.log(autoer1.name,autoer1.idNum,autoer1.college,autoer1.course);
  autoer1.addCourse(‘logistics‘);//调用Huster的方法
  autoer1.sayHobby();    //调用自身的方法
 

2、apply() 方法

apply() 方法与call()方法几乎一样,唯一的区别就是apply()方法只有两个参数,第一个用作 this 的对象,第二个是要传递给函数的参数的数组。也就是说apply()方法把call()方法的若干个参数放到一个数组里,传递给父类

function Huster(name,idNum,college)
{
    this.name = name;        
    this.idNum = idNum;
    this.college = college;
    this.course = new Array();    
    
    this.addCourse = function(course)//这个方法不能用prototype来定义,如果用的话,子类无法继承该方法
    {                   //用原型prototype定义的方法可以用原型链来继承,call()方法和apply()方法都无法继承
        this.course.push(course);
        console.log(this.course);
    };    
            
}    
    
    function Autoer(name,idNum)
    {
        this.college = ‘自动化‘;
        Huster.apply(this,new Array(name,idNum,this.college));//Autoer使用apply()方法来继承 Huster
     if (typeof Autoer._initialized == "undefined") 
     { 
      Autoer.prototype.sayHobby = function() //自己的方法可以用原型链prototype定义
      { 
        alert(this.college+‘人喜欢撸代码!‘);     
      }; 
      Autoer._initialized = true; 
     } 
  } 
   
  var autoer1 = new Autoer(‘偶人儿‘,‘U123456789‘); //声明一个实例autoer1
  console.log(autoer1.name,autoer1.idNum,autoer1.college,autoer1.course); 
  autoer1.addCourse(‘logistics‘);//调用Huster的方法
  autoer1.sayHobby();    //调用自身的方法

3、原型链法(prototype chaining)

当父类的属性或方法是用原型链定义的时候,子类要用原型链法进行继承,因为父类的属性和方法是在父类的原型上面,如果用call()或者apply()方法的话,访问不到父类的属性和方法,并会报出Uncaught TypeError错误:Uncaught TypeError: autoer1.addCourse(所调用的父类的方法) is not a function;如果是属性的话,则显示undefined

下面是使用原型法继承的例子(来源于w3school教程http://www.w3school.com.cn/js/pro_js_inheritance_implementing.asp

function ClassA() {
}

ClassA.prototype.color = "blue";
ClassA.prototype.sayColor = function () {
    alert(this.color);
};

function ClassB() {
}

ClassB.prototype = new ClassA();

ClassB.prototype.name = "";
ClassB.prototype.sayName = function () {
    alert(this.name);
};
var objA = new ClassA();
var objB = new ClassB();
objA.color = "blue";
objB.color = "red";
objB.name = "John";
objA.sayColor();
objB.sayColor();
objB.sayName();

注意:调用 ClassA 的构造函数,没有给它传递参数。这在原型链中是标准做法。要确保构造函数没有任何参数。

子类的所有属性和方法都必须出现在 prototype 属性被赋值后,因为在它之前赋值的所有方法都会被删除。为什么?因为 prototype 属性被替换成了新对象,添加了新方法的原始对象将被销毁。

此外,在原型链中,instanceof 运算符的运行方式也很独特。对 ClassB 的所有实例,instanceof 为 ClassA 和 ClassB 都返回 true。例如:

var objB = new ClassB();
alert(objB instanceof ClassA);    //输出 "true"
alert(objB instanceof ClassB);    //输出 "true"

原型链的弊端是不支持多重继承。记住,原型链会用另一类型的对象重写类的 prototype 属性。

4、混合方式

这种继承方式使用构造函数定义类,并非使用任何原型。对象冒充的主要问题是必须使用构造函数方式,这不是最好的选择。不过如果使用原型链,就无法使用带参数的构造函数了。开发者如何选择呢?答案很简单,两者都用。

在前一章,我们曾经讲解过创建类的最好方式是用构造函数定义属性,用原型定义方法。这种方式同样适用于继承机制,用对象冒充继承构造函数的属性,用原型链继承 prototype 对象的方法。用这两种方式重写前面的例子,代码如下:

 

function Huster(name,idNum,college)
    {
        this.name = name;        
        this.idNum = idNum;
        this.college = college;
        this.course = new Array();    
        if (typeof Autoer._initialized == "undefined") //动态原型法定义addCourse()方法
        {
               Huster.prototype.addCourse = function(course)
            {
                this.course.push(course);
                console.log(this.course);
            };    
           }
    }    
    
    function Autoer(name,idNum)
    {
        this.college = ‘自动化‘;
        Huster.call(this,name,idNum,this.college);//使用call()方法继承Huster的属性
        
        if (typeof Autoer._initialized == "undefined") 
        {    
            Autoer.prototype.sayHobby = function()
            {
                alert(this.college+‘人喜欢撸代码!‘);
            };
            Autoer._initialized = true;            
        }
        
    }
    Autoer.prototype = new Huster(this.name);//使用原型法继承Huster的方法
    
    var autoer1 = new Autoer(‘偶人儿‘,‘U123456789‘);    
    autoer1.addCourse(‘logistics‘);
    console.log(autoer1.name,autoer1.idNum,autoer1.college,autoer1.course);    
    autoer1.sayHobby();

 

在此例子中,继承机制由两行突出显示的红色代码实现。在第一行突出显示的代码中,在Autoer()构造函数中,用call()方法继承 Huster 类的 idNum、college、course 属性。在第二行突出显示的代码中,用原型链继承 Huster类的方法。由于这种混合方式使用了原型链,所以 instanceof 运算符仍能正确运行。

 
















以上是关于JavaScript几种常见的继承方法的主要内容,如果未能解决你的问题,请参考以下文章

关于常见继承的几种方法

JavaScript继承的几种方法

JavaScript继承的几种实现

JavaScript 常见的六种继承方式

javascript高级程序设计的几种经典继承

JavaScript 的几种继承方式