1.前言
本文完整代码指路我的GitHub,欢迎star。js中的继承方式有很多种,以下仅列出部分。
2.原型链继承
代码如下:
function Parent() {
this.name = 'jchermy';
}
Parent.prototype.getName = function() {
console.log(this.name);
}
function Child() {
}
Child.prototype = new Parent();
var child1 = new Child();
console.log(child1.getName()); //jchermy
这样看来貌似可以完美完成继承,然后当属性换成引用类型时,就会出现问题了,如下:
function Parent() {
this.names = ["aa", "bb"]; //引用类型值
}
function Child() {
}
Child.prototype = new Parent();
var child1 = new Child();
child1.names.push("cc");
console.log(child1.names); //["aa","bb","cc"]
var child2 = new Child();
console.log(child2.names); //["aa","bb","cc"]
child2.names.push("dd");
console.log(child1.names) //["aa", "bb", "cc", "dd"]
console.log(child2.names);//["aa", "bb", "cc", "dd"]
var p = new Parent();
console.log(p.names); //["aa", "bb"]
由此我们可以得出原型链继承的缺点:
- 引用类型的属性被所有实例共享
- 在创建Child实例时,不能向Parent传参
2.借用构造函数继承
function Parent() {
this.names = ["aa", "bb"];
}
function Child() {
Parent.call(this);
}
var child1 = new Child();
child1.names.push("cc");
console.log(child1.names);//["aa", "bb", "cc"]
var child2 = new Child();
console.log(child2.names);//["aa", "bb"]
child2.names.push("dd");
console.log(child1.names); //["aa", "bb", "cc"]
console.log(child2.names); //["aa", "bb", "dd"]
var p = new Parent();
p.names; //["aa", "bb"]
可以看出,借用构造函数继承避免了一下原型链继承的问题,主要体现在:
- 避免了引用类型的属性被所有实例共享
- 可以在Child中向Parent传参
然而,借用构造函数继承也有缺点。
function Parent(name) {
this.name = "parent "+name;
}
function Child(name) {
this.name = "child"+name;
Parent.call(this, name);
}
var child1 = new Child('hemin');
console.log(chil1.name); //"parent hemin"
var child2 = new Child("aa");
console.log(child2.name); //"parent aa"
缺点:方法都在构造函数中定义,每次创建实例都会创建一遍方法
3.组合继承
组合继承融合原型链继承和构造函数的优点,是JavaScript中最常用的继承模式
function Parent(name) {
this.name = name;
this.colors = ["red", "blue"];
}
Parent.prototype.getName = function() {
console.log(this.name);
}
function Child(name, age) {
Parent.call(this, name);
this.age = age;
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;
var child1 = new Child("aa", 18);
child1.colors.push("black");
child1.name; //"aa"
child1.age; //18
child1.colors; //["red", "blue","black"]
var child2 = new Child("bb", 20);
child2.name; //"bb"
child2.age; //20
child2.colors; //["red", "blue"]
然而组合继承也有一个缺点,就是会调用两次父构造函数。
如下:
Child.prototype = new Parent();
var child1 = new Child('aa', '18');
所以,在这个例子中,如果我们打印 child1 对象,我们会发现 Child.prototype 和 child1 都有一个属性为colors,属性值为['red', 'blue']。
这个问题我们在下面再讨论。
4.原型式继承
function createObj(o) {
function F() {}
F.prototype = o;
return new F();
}
var person = {
name: 'jchermy',
friends: ["aa", "bb"]
}
var person1 = createObj(person);
var person2 = createObj(person);
//注意:修改person1.name的值,person2.name的值并未发生改变,
//并不是因为person1和person2有独立的 name 值,而是因为person1.name = 'person1',给person1添加了 name 值,并非修改了原型上的 name 值。
person1.name = "xiaomi";
console.log(person2.name); //"jchermy"
person2.friends.push("cc");
console.log(person1.friends); //["aa", "bb", "cc"]
缺点:包含引用类型的属性值始终会共享相应的值,与原型链继承一样
5.寄生式继承
创建一个仅用于封装继承过程的函数,该函数在内部以某种形式做增强对象,最后返回对象
function createObj(o) {
var clone = Object.create(o);
clone.sayName = function () {
console.log("hi");
}
return clone;
}
var person = {
name: "jchermy",
friends: ["aa", "bb"]
};
var person1 = createObj(person);
var person2 = createObj(person);
person1.name = "xiaomi";
console.log(person1.name); //"xiaomi"
console.log(person2.name); //"jchermy"
person1.friends.push("xxx");
console.log(person1.friends); // ["aa", "bb", "xxx"]
console.log(person2.friends); // ["aa", "bb", "xxx"]
缺点:
- 跟借用构造函数模式一样,每次创建对象都会创建一遍方法
- 包含引用类型的属性值始终会共享相应的值
6.寄生组合式继承
还记得组合继承中提到的那些问题吗,那么我们该如何精益求精,避免这一次重复调用呢?
如果我们不使用 Child.prototype = new Parent() ,而是间接的让 Child.prototype 访问到 Parent.prototype 呢?可以这样实现:
function Parent(name) {
this.name = name;
this.colors = ["red", "blue", "green"];
}
Parent.prototype.getName = function () {
console.log(this.name);
}
function Child(name, age) {
Parent.call(this, name);
this.age = age;
}
//关键的三步
var F = function(){};
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.prototype.constructor = Child;
var child1 = new Child('xiaomi', 18);
var child2 = new Child2('aa', 24);
console.log(child1.name); //xiaomi
console.log(child2.name); //aa
child1.colors.push("black");
child1.colors; //["red", "blue", "green", "black"]
child2.colors; //["red", "blue", "green"];
这种方式的高效率体现它只调用了一次 Parent 构造函数,并且因此避免了在 Parent.prototype 上面创建不必要的、多余的属性。与此同时,原型链还能保持不变;因此,还能够正常使用 instanceof 和 isPrototypeOf。开发人员普遍认为寄生组合式继承是引用类型最理想的继承范式。
7.结语
如果文章对你有帮助的话,欢迎点赞和收藏!!有错误和不合理的地方欢迎大家指正!GitHub给个star就最好啦!=(//▽//)感谢大家~
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。