JavaScript 继承的实现方式

JavaScript 的继承是通过原型链实现的,要实现继承需要理解原型和原型链的概念

原型和原型链

  1. 函数(除了箭头函数)上(存在 prototype 属性(这是一个显式原型属性),其指向原型对象,原型对象上存在两个属性:constructor 和 [[prototype]] (无法访问,部分浏览器可通过 __proto__ 访问,这是一个隐式的原型属性)

    • constructor :指向构造函数,即是函数自身
    • [[prototype]](__proto__):指向构造函数的原型对象,即 Object ,Object.__proto__ = null
  2. 当通过 new 调用这个函数时,得到的对象会存在 [[prototype]](__proto__),指向的是函数的 prototype 属性

  3. 所有的对象上都存在 [[prototype]](__proto__)属性

  4. 这种通过 __proto__ 在各对象间建立链接的工具就是原型链,通过原型链可以访问到其他对象的属性和方法

继承的实现方式

继承指一个对象直接使用另一对象的属性和方法。JavaScript 的继承是通过原型链实现的。实现属性继承的核心代码是 SuperFn.call(this),实现方法继承的核心代码是 SubFn.prototype.__proto__ = SuperFn.prototype,具体实现方式有以下6种:

ES6 class类 extends 继承

通过ES6 新增的语法糖实现继承

class SuperClass {
    constructor(name) {
        this.name = name || 'super';
        this.arr = [1, 2];
    }
    sayName(){
        console.log(this.name)
    }
}

class SubClass extends SuperClass{
    constructor(){
        super();
    }
}

const sub0 = new SubClass();
const sub00 = new SubClass();
sub00.name = 'sub11';
sub00.arr.push(3);

sub0.sayName(); // 'super'
console.log(sub0.name, sub0.arr, 
sub0.arr === sub00.arr, 
sub0 instanceof SubClass, 
sub0.constructor ===SubClass); // 'super'  [1,2]  false true true

基于原型继承

原型赋值SubFn.prototype = new SuperFn();
问题点:

  • 原型对象的 constructor 属性值变为 Object
  • 原型对象中包含引用值,值操作会同时反应到所有继承对象上
  • 子类型在实例化时不能给父类型的构造函数传参
function SuperFn(name) {
    this.name = name || 'super';
    this.arr = [1, 2];
}
SuperFn.prototype.sayName = function () {
    console.log(this.name)
}
/*  基于原型继承: SubFn1.prototype = new SuperFn();
    问题点:
    - 原型对象的 constructor 属性值变为 Object
    - 原型对象中包含引用值,值操作会同时反应到所有继承对象上
    - 子类型在实例化时不能给父类型的构造函数传参
*/
function SubFn1() {}
// 继承属性和方法:SubFn1 的原型指向 SuperFn 的实例和方法
// 带来的影响:
//   - SubFn1 的构造函数变为 Object
//   - SuperFn 的实例对象中包含引用值,值操作会同时反应到所有继承对象上
SubFn1.prototype = new SuperFn();

const sub1 = new SubFn1();
const sub11 = new SubFn1();
sub11.name = 'sub11';
sub11.arr.push(3);

sub1.sayName(); // 'super'
console.log(sub1.name, sub1.arr,
sub1.arr === sub11.arr, 
sub1 instanceof SubFn1, 
sub1.constructor ===SubFn1); // 'super'  [1,2,3] true true  false

借用构造函数

构造函数内部执行 SuperFn.call(this),只能继承属性,不能继承原型链上的方法
问题点:

  • 只能继承父类的实例属性和方法,不能继承原型属性/方法
  • 字类构造函数无法实现复用,每个子类都有父类实例函数的副本
/* 借用构造函数:SuperFn.call(this)
   问题点:
    - 只能继承父类的实例属性和方法,不能继承原型属性/方法
    - 字类构造函数无法实现复用,每个子类都有父类实例函数的副本
*/
function SubFn2() {
   // 继承属性:子类构造函数中调用父类构造函数并绑定 this ,
   // 每次创建实例的过程,函数都会重新执行,同一个属性每次都会新建
   SuperFn.call(this, ...arguments)
}

const sub2 = new SubFn2('sub2');
const sub22 = new SubFn2();
sub22.name = 'sub22';
sub22.arr.push(3);

// sub2.sayName(); // 报错
console.log(sub2.name, sub2.arr,
sub2.arr === sub22.arr, 
sub2 instanceof SubFn2, 
sub2.constructor ===SubFn2); // 'sub2'  [1,2]  false true  true

组合继承

构造函数内部执行 SuperFn.call(this),原型赋值 SubFn.prototype.__proto__ = SuperFn.prototype;

/* 组合继承:SuperFn.call(this)  SubFn3.prototype.__proto__ = SuperFn.prototype;
   问题点:属性重复建立
*/
function SubFn3() {
   // 继承属性:子类构造函数中调用父类构造函数并绑定 this ,
   //每次创建实例的过程,函数都会重新执行,同一个属性每次都会新建
   SuperFn.call(this, ...arguments)
}
// 继承方法
SubFn3.prototype.__proto__ = SuperFn.prototype;

const sub3 = new SubFn3('sub3');
const sub33 = new SubFn3();
sub33.name = 'sub33';
sub33.arr.push(3);

sub3.sayName(); // 'sub3'
console.log(sub3.name, sub3.arr,
sub3.arr === sub33.arr, 
sub3 instanceof SubFn3, 
sub3.constructor ===SubFn3); // 'sub3'  [1,2]  false true true 

原型式继承

此种方式和 subObj = Object.create(superObj)实现的功能相同

/* 原型式继承:此种方式和 Object.create() 实现的功能相同
   问题点:
    - 原型对象中包含引用值,值操作会同时反应到所有继承对象上
    - 无法传递参数
*/

// 此函数用来创建一个对象,对象的原型指向传递的参数(继承的对象)
let Fn;
function CreateObj(obj) {
   // 函数内部定义构造函数
   function F() {}
   Fn = F;
   // 构造函数的原型对象指向传递的进来的对象
   F.prototype = obj;
   // 创建实例化对象并返回,此实例对象的 __proto__ = F.prototype = obj
   return new F();
}
const SuperObj = {
   name: 'super',
   arr: [1, 2],
   sayName() {
       console.log(this.name)
   }
}

const sub4 = CreateObj(SuperObj); // 等价于 sub4 = Object.create(SuperObj) sub4.__proto__ = SuperObj
const sub44 = CreateObj(SuperObj);

sub44.name = 'sub44';
sub44.arr.push(3);

sub4.sayName(); // 'super'
console.log(sub4.name, sub4.arr, 
sub4.arr === sub44.arr,
sub4 instanceof Fn, 
sub4.constructor === Object); // 'super'  [1,2,3] true true true  

寄生式组合继承

构造函数内部执行 SuperFn.call(this),原型赋值 SubFn.prototype = Object.create(SuperFn.prototype);

/* 5.寄生式组合继承
*/

function inheritPrototype(SuperFn, SubFn) {
   //此函数实现的目的如下  SubFn.prototype.__proto__ = SuperFn.prototype
   SubFn.prototype = Object.create(SuperFn.prototype);
   SubFn.prototype.constructor = SubFn;
}

function SubFn5() {
   SuperFn.call(this, ...arguments);
}
inheritPrototype(SuperFn, SubFn5);

const sub5 = new SubFn5();
const sub55 = new SubFn5();
sub55.name = 'sub55';
sub55.arr.push(3);

sub5.sayName(); // 'super'
console.log(sub5.name, sub5.arr,
sub5.arr === sub55.arr, 
sub5 instanceof SubFn5,
sub5.constructor ===SubFn5); // 'super'  [1,2]  false true true 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值