js的几种继承方法以及其优缺点

128 阅读4分钟

//1、原型链继承

     function father(){
            this.colors = ['pink','red','yellow'];
        }
        father.prototype.getname = function(){
            return this.colors;
        }
        function son(name,age){
            this.name = name;
            this.age = age;
        }
        father.prototype.songetname = function(){
            return this.colors;
        }
        son.prototype = new father();
        var base1 = new son();
        base1.colors.push('blue')
        console.log(base1.colors);   //["pink", "red", "yellow", "blue"]
        var base2 = new son();
        console.log(base2.colors);   //["pink", "red", "yellow", "blue"]
        console.log(base2.getname()); //["pink", "red", "yellow", "blue"]

优缺分析: 1、优点 a、较构造继承来说,可以实现原型函数的共享 2、缺点 a、虽然说实现了原型函数的继承,一个实例改变了其从原型那里继承来的引用属性值时,其它继承自这个原型属性的值都将被改变。


2、构造函数继承

 function father(name) {
            this.name = name;
        }
        father.prototype.getnaem = function(){
            return this.name;
        }
        function son(name,age){
            father.call(this,name);
            this.age = age;
        }
        var base = new son("jick",22);
        console.log(base.name,base.age);   // jick 22
        var base2 = new son("lusi",23);
        console.log(base2.name,base2.age); // lusi 23
        console.log(base.getname());      //报错

1、优点 a、可以看到每个实例的属性都各自独立 2、缺点 a、但是不能继承原型的方法,----报错

总结:可以看到以上两种传统的继承方法都有缺陷,所以我们用组合继承来综合以上的两种继承方法,(原型链继承,构造函数继承)


3、组合继承

function SuperType(name) {
            this.name = name;
            this.colors = ['red', 'blue', 'green']
        }
        SuperType.prototype.sayName = function() {
            console.log(this.name);
        }

        function SubType(name, age) {
            SuperType.call(this, name);
            this.age = age;
        }
        SubType.prototype = new SuperType();
        SubType.prototype.sayAge = function() {
            console.log(this.age);
        }
        var instance1 = new SubType('Nicholas', 29);
        instance1.colors.push('black');
        console.log(instance1.colors);    // ["red", "blue", "green", "black"]
        instance1.sayName();		// Nicholas
        instance1.sayAge();		// 29
        var instance2 = new SubType('Greg', 27);
        console.log(instance2.colors);  //  ["red", "blue", "green"]
        instance2.sayName();		//Greg
        instance2.sayAge();		// 27

1、优点 a、可以看到每个实例的属性都各自独立,原型上面的方法也都能用 2、缺点 a、我们在实现属性独立的时候不希望,原型prototype上面有太多的属性,很多余。 b、要使用此方法的话,最少要调用两次超类型的构造函数,浪费内存,影响性能。

所以还是不行,这里可以参考《JavaScript高级程序设计》里面的寄生式组合继承,将实例化继承换成浅拷贝继承就能实现。


4、原型式继承 其借用了这个函数来进行:

function object(o){ 
 function F(){} 
 F.prototype = o; 
 return new F(); 
}
具体使用:
var person = { 
 name: "Nicholas", 
 friends: ["Shelby", "Court", "Van"] 
}; 
var anotherPerson = object(person); 
anotherPerson.name = "Greg"; 
anotherPerson.friends.push("Rob"); 
var yetAnotherPerson = object(person); 
yetAnotherPerson.name = "Linda"; 
yetAnotherPerson.friends.push("Barbie"); 
alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"

可以看出,效果和原型链继承差不多,都是共享属性; 然后ECMAscript5,新出了一个Objeck.create()方法,不需要再写Object()函数,直接调用就行了,具体使用如下:

var person = { 
 name: "Nicholas", 
 friends: ["Shelby", "Court", "Van"] 
}; 
var anotherPerson = Object.create(person); 
anotherPerson.name = "Greg"; 
anotherPerson.friends.push("Rob"); 
 
var yetAnotherPerson = Object.create(person); 
yetAnotherPerson.name = "Linda"; 
yetAnotherPerson.friends.push("Barbie"); 
alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"
这里Objeck.create()与Object()不同,可以传第二个参数,
var anotherPerson = Object.create(person, { 
 name: { 
 value: "Greg" 
 } 
}); 
alert(anotherPerson.name); //"Greg"

5、寄生式继承 这里需要借用到object()函数,

function object(o){ 
 function F(){} 
 F.prototype = o; 
 return new F(); 
}
--------------------------------------
function createAnother(original){ 
 var clone = object(original); //通过调用函数创建一个新对象
 clone.sayHi = function(){ //以某种方式来增强这个对象
 alert("hi"); 
 }; 
 return clone; //返回这个对象
}
-----------------------------------------------
var person = { 
 name: "Nicholas", 
 friends: ["Shelby", "Court", "Van"] 
}; 
var anotherPerson = createAnother(person); 
anotherPerson.sayHi(); //"hi"

这样anotherPerson就继承了person的属性,以及拥有自己的方法sayHi() 但是使用寄生式继承来为对象添加函数,会由于不能做到函数复用而降低效率;这一 点与构造函数模式类似。

6、寄生组合继承 这个的话是目前公认最合适的一种继承方式,但是是在ECMAscript6之前, 需要用到函数

function object(o){ 
 function F(){} 
 F.prototype = o; 
 return new F(); 
}
function inheritPrototype(subType, superType){ 
 var prototype = object(superType.prototype); //创建对象
 prototype.constructor = subType; //增强对象
 subType.prototype = prototype; //指定对象
}

使用方式

function SuperType(name){ 
if( this instanceof SuperType){   //这里我用if语句来判断父构造函数的作用域是否安全,功能不受影响
        this.name = name; 			//保证作用域安全在多人同页面开发中很有必要
        this.colors = ["red", "blue", "green"]; 
    }else{
        return new  SuperType;
    }
} 
SuperType.prototype.sayName = function(){ 
 alert(this.name); 
}; 
function SubType(name, age){ 
 SuperType.call(this, name); 
 
 this.age = age; 
} 
inheritPrototype(SubType, SuperType); 
SubType.prototype.sayAge = function(){ 
 alert(this.age); 
};

7、类继承

class Father {
  constructor(x, y) {
    this.x = x;
    this.y = y;
    console.log('father 里面的this = ',this) // 这个this在16行调用的时候 为 son这个对象
  }
  sum() {
    console.log('father 里面的sum方法: ', this.x + this.y);
  }
}

// super必须在 子类的 this之前调用, 就是说必须先调用父类的构造函数

class Son extends Father {
  constructor(x, y) {
    super(x, y); //调用了父类中的构造函数
    console.log('Son 里面的this = ',this); // 这个this在16行调用的时候  也是 son这个对象
  }
  say() {
    super.sum() // super即可以调用父类的构造函数,也可以调用普通函数
  }
}
var son = new Son(1, 2);
var son1 = new Son(11, 22);
son.sum();
son1.say();

总结,使用的时候注意共有属性和方法一定要加this使用 constructor中的this指向的是 new 出来的实例对象 自定义的方法 也指向 new出来的实例对象 ES6 中没有 变量提升, 必须向定义类, 然后才能通过类 实例化对象 子类的this对象一定要在super对象后面使用