本文完整代码指路个人GitHub,欢迎star。js中的继承方式有不少种,如下仅列出部分。git
代码以下:github
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"]
由此咱们能够得出原型链继承的缺点:this
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"]
能够看出,借用构造函数继承避免了一下原型链继承的问题,主要体如今:spa
然而,借用构造函数继承也有缺点。prototype
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"
缺点:方法都在构造函数中定义,每次建立实例都会建立一遍方法code
组合继承融合原型链继承和构造函数的优势,是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"]
然而组合继承也有一个缺点,就是会调用两次父构造函数。blog
以下:继承
Child.prototype = new Parent(); var child1 = new Child('aa', '18');
因此,在这个例子中,若是咱们打印 child1 对象,咱们会发现 Child.prototype 和 child1 都有一个属性为colors,属性值为['red', 'blue']。
这个问题咱们在下面再讨论。
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"]
缺点:包含引用类型的属性值始终会共享相应的值,与原型链继承同样
建立一个仅用于封装继承过程的函数,该函数在内部以某种形式作加强对象,最后返回对象
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"]
缺点:
还记得组合继承中提到的那些问题吗,那么咱们该如何精益求精,避免这一次重复调用呢?
若是咱们不使用 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。开发人员广泛认为寄生组合式继承是引用类型最理想的继承范式。
若是文章对你有帮助的话,欢迎点赞和收藏!!有错误和不合理的地方欢迎你们指正!GitHub给个star就最好啦!=(//▽//)感谢你们~