一、关于继承我所知道的分类
继承在java中可能更能一目了然一些,class代表类,extends代表继承,es6中将这种模式的继承已经加了进去。所以以后我们也可以用完整且易懂的继承了。这里我按照会不会使用Object.create()方法来划分。
不使用Object.create()的继承。
原型继承 、构造函数继承 、 组合继承
使用Object.create()的继承。
原生式继承 、 寄生式继承 、寄生组合继承 、 es6的class继承
二、继承类型详解
1.原型继承
原型继承,将父类的实例对象作为子类的原型。
SubType.prototype = new SupType();
//修正子类构造函数的指向,不修改则指向SupType
SubType.prototype.constructor = SubType
优点:父类方法可以复用
缺点:(1) 父类的引用属性会被所有子类实例共享. (2) 子类构建实例时不能向父类传递参数
2.构造函数继承
构造函数继承则是使用call函数,引用执行父类函数,将父类构造函数的内容复制给了子类的构造函数。
function SupType() {
this.name = "zhangsan";
}
function SubType(name, age) {
SubType.call(this, name);
this.age = age;
}
优点:父类的引用属性不会被共享,子类构建实例时可以向父类传递参数
缺点:父类的方法不能复用,子类实例的方法每次都是单独创建的。
3.组合继承
组合继承是将原型继承和构造函数继承的优点糅合在一起,组成了组合继承,核心代码如下。
function SupType() {
this.name = "zhangsan";
}
function SubType(name, age) {
SupType.call(this, name);
this.age = age;
}
SubType.prototype = new SupType();
优点:父类的引用属性不会被共享,子类构建实例时可以向父类传递参数,父类的方法可以被复用
缺点:两次调用父类构造函数。第一次 SupType.call(this, name)第二次new SupType() 造成性能上的浪费。
4.原型式继承
原型式继承的主要原理是创建一个临时的函数,将函数的prototype修改为父类的复制对象,具体代码如下:
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
var person = {
name: "zhangsan",
friends: ["lisi", "wangwu"]
};
var anotherPerson = object(person);
anotherPerson.name = "zhangsan1";
anotherPerson.friends.push("Bob");
var yetAnotherPerson = object(person);
yetAnotherPerson.name = "zhangsan2";
yetAnotherPerson.friends.push("Mary");
console.log(person.friends); //"lisi", "wangwu", "Bob", "Mary
console.log(anotherPerson.friends); //"lisi", "wangwu", "Bob", "Mary
console.log(yetAnotherPerson.friends); //"lisi", "wangwu", "Bob", "Mary
(ps:这里说明下Object.create(obj) 其实和Object(obj)是完全相同的,对一个对象进行复制)
优点:父类方法可以复用
缺点:(1)父类的引用属性会被所有子类实例共享 。(2)子类构建实例时不能向父类传递参数
4.寄生继承
使用原型式继承获得一个目标对象的浅复制,然后增强这个浅复制的能力。即给浅复制出来的对象增加自定义方法。
function createAnother(original){
var clone=object(original); //通过调用函数创建一个新对象
clone.sayHi = function(){ //以某种方式来增强这个对象
alert("hi");
};
return clone; //返回这个对象
}
5.寄生组合继承
寄生组合继承顾名思义,将原型式继承和组合继承结合起来。省去了创建两次对象的性能问题还解决了父类复用及可传参的问题,寄生组合继承可以说是比较完美的了。
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);
}
6.class继承
class A {}
class B extends A {
constructor() {
super();
}
}
原理如下:
class A {
}
class B {
}
Object.setPrototypeOf = function (obj, proto) {
obj.__proto__ = proto;
return obj;
}
// B 的实例继承 A 的实例
Object.setPrototypeOf(B.prototype, A.prototype);
// B 继承 A 的静态属性
Object.setPrototypeOf(B, A);
ES6的class相当于是ES5继承的语法糖的,ES6继承中子类的构造函数的原型链指向父类的构造函数了。ES5中其实是复制构造函数,没有原型链的指向。
三、总结
JS的继承除了构造函数继承之外都基于原型链构建的
可以用寄生组合继承实现ES6中class extends式的继承,但是不是完全等于的