js没有类的概念,js里万物皆是对象,对象是无序属性的集合,其属性可以包含基本值,对象或者函数
曾经一段时间因为js关于类实现继承的不规范,导致各种各样实现继承的代码;而实际上不管代码怎么变,继承都基于两种方式:
- 通过原型链,即子类的原型指向父类的实例从而实现原型共享。
- 借用构造函数,即通过js的
apply、call实现子类调用父类的构造方法;
-
原型链继承
function SuperType(){
this.colors = ["red", "blue", "green"];
}
function SubType(){
}
//继承了 SuperType
SubType.prototype = new SuperType();
var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
var instance2 = new SubType();
alert(instance2.colors); //"red,blue,green,black"
原理:子类构造函数的原型对象指向父类的实例
缺点:
- 因为子类共享父类的属性,如上例父类属性为引用类型时,一个子类更改数据,会反应到其他子类。
- 创建子类型的实例时,不能向超类型的构造函数中传递参数。
-
借用构造器函数
function SuperType(name){
this.name = name;
this.colors = ["red", "blue", "green"];
}
function SubType(name){
//继承了 SuperType
SuperType.call(this, name);
}
var instance1 = new SubType('name');
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
var instance2 = new SubType();
alert(instance2.colors); //"red,blue,green"
原理:通过在子类中调用父类的构造方法,这样每个子类都能拥有父类的属性副本。 优点: 解决上原型链继承的所有缺点。 缺点: 显而易见,无法继承父类原型链上的属性。
-
组合继承
function SuperType(name){
this.name = name;
this.colors = ["red", "blue", "green"];
}
function SubType(name){
//继承了 SuperType
SuperType.call(this, name);
}
SubType.prototype = new SuperType();
var instance1 = new SubType('cjz');
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
var instance2 = new SubType();
alert(instance2.colors); //"red,blue,green"
原理:结合上面两种模式,仍有明显缺点,一般直接用最后一种寄生组合式继承。
缺点:
- 调用了两次父类的构造函数,浪费内存。
- 子类实例的原型链上有重复属性,比如上述例子子类中有属性
name,父类上也有属性name。
-
原型式继承
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"
原理:用一个函数包装一个对象,然后返回这个函数的调用,这个函数就变成了个可以随意增添属性的实例或对象。object.create()就是这个原理。简单了解即可,一般不会单独使用。
-
寄生式继承
function object(o){
function F(){}
F.prototype = o;
return new F();
}
function createAnother(original) {
let clone = objectCopy(original);
clone.getName = function () {
console.log(this.name);
};
return clone;
}
let person = {
name: "yhd",
friends: ["rose", "tom", "jack"]
}
let person1 = createAnother(person);
person1.friends.push("lily");
console.log(person1.friends);
person1.getName(); // yhd
let person2 = createAnother(person);
console.log(person2.friends); // ["rose", "tom", "jack", "lily"]
原理:创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真地是它做了所有工作一样返回对象。简单了解即可,一般不会单独使用。
-
寄生组合式继承
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){
this.name = name;
this.colors = ["red", "blue", "green"];
}
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);
};
只调用了一次SuperType构造函数,并且因此避免了在SubType.prototype上面创建不必要的、多余的属性。与此同时,原型链还能保持不变;因此,还能够正常使用instanceof 和 isPrototypeOf()。开发人员普遍认为寄生组合式继承是引用类型最理想的继承范式。