前言
本文接 JavaScript基础之继承「上」 | 8月更文挑战 ,继续介绍 JS 继承的方式。
1.原型链继承
2.借用构造函数(经典继承)
3.组合继承
4.原型式继承
原型式继承就是 ES5 中 Object.create() 的模拟实现,将传入的对象作为创建的对象的原型。
原型式继承说白了,就是将父类型作为一个对象,直接变成子类型的原型对象。
function createObj(o){
function F() { }
F.prototype = o;
return new F();
}
缺点:
包含 引用类型的属性值 始终都会共享相应的值,这点和原型链继承一样。
var person = {
name: 'Kobe', // 非引用类型的属性值
friends: ['Tom', 'Jarry'], // 引用类型的属性值
}
var person1 = createObj(person);
var person2 = createObj(person);
person1.name = 'person1';
console.log( person2.name ); // Kobe
person1.friends.push( 'uzi' );
console.log(person2.friends); // ['Tom', 'Jarry', 'uzi'] person1和person2共享引用类型的属性值
注意
修改 person1.name 的值,person2.name 的值不会发生改变,并不是因为 person1 和 person2 有独有的 name 值,而是因为 person1.name = 'person1',给person1添加了 name值,并非修改了原型上的name值。
5.寄生式继承
创建一个仅用于封装继承过程的函数,该函数在内部以某种形式来做增强对象,最后返回对象。
function createObj ( o ){
var clone = Object.create( o );
clone.say = function () {
console.log( 'hello' );
}
return clone;
}
缺点
跟借用构造函数模式一样。每次创建对象都会创建一次方法。
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;
}
// 关键的一步,使用一个`中间件` F
var F = function () { };
F.prototype = Parent.protptype;
Child.prototype = new F();
换个写法:
Child.prototype = Object.create(Parent.prototype) // **核心** 通过创建中间对象,子类原型和父类原型,就会隔离开。不是同一个啦,有效避免了方式5的缺点。
Child.prototype.constructor = Child; // 修复构造函数指向
var child1 = new Child("Jarry", "16");
console.log(child1); // {name: "Jarry", colors: Array(3), age: "16"}
如果封装一下这个继承方法:
function object (o) {
function F() {};
F.prototype = o;
return new F();
}
function prototype (child, parent) {
var prototype = object( parent.prototype );
prototype.constructor = child;
child.prototype = prototype;
}
// 当我们使用的时候:
prototype(Child, Parent);
《JavaScript高级程序设计》中对寄生组合式继承的夸赞: 这种方式的高效率体现它只调用了一次
Parent构造函数,并且因此避免了再Parent.prototype上面创建不必要的多余的属性。与此同时,原型链还能保持不变;因此,还能够正常使用instanceof和isPrototypeOf。
ES6实现继承的原理
class extends
class Parent {
}
class Child {
}
Object.setPrototypeOf = function (obj, proto) {
obj.__proto__ = proto;
return obj;
}
// B 的实例继承 A 的实例
Object.setPrototypeOf(Child.prototype, parent.prototype);
// B 继承 A 的静态属性
Object.setPrototypeOf(Child, Parent);
结语
文章如有错误之处,希望在评论区指正🙏🙏。如果这篇文章帮到了你,欢迎点赞👍和关注⭐️