JavaScript基础之继承「下」 | 8月更文挑战

139 阅读2分钟

前言

本文接 JavaScript基础之继承「上」 | 8月更文挑战 ,继续介绍 JS 继承的方式。

1.原型链继承

2.借用构造函数(经典继承)

3.组合继承

4.原型式继承

原型式继承就是 ES5Object.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 的值不会发生改变,并不是因为 person1person2 有独有的 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 上面创建不必要的多余的属性。与此同时,原型链还能保持不变;因此,还能够正常使用 instanceofisPrototypeOf

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);

结语

文章如有错误之处,希望在评论区指正🙏🙏。如果这篇文章帮到了你,欢迎点赞👍和关注⭐️