JS - 六种继承方式

一、构造函数
  1. 原型对象
funciton A(name) {
    this.name = name;
    this.say = function() {
        console.log(this.name)
    }
}

所有的实例是共享同一个原型对象,因此有别于实例方法或属性,原型对象仅有一份。而实例有很多份,且实例属性和方法是独立的。
在构造函数中,应当保持属性的私有性、以及方法的复用、共享。
所以:

  1. 将属性封装在构造函数中
  2. 将方法定义在原型对象上
let test1 = new A('test1');
let test2 = new A('test'2);

test1.say () // test1
test1.say () // test2
test1.say === test.say //false

如果将函数放在构造函数中,虽然可以在每个实例对象中实现,但是每个实例都用的是同一套函数逻辑,这就造成了性能的浪费

推荐的写法

funcion A(name) {
    this.name = name;
}
A.prototype.say = function() {
        console.log(this.name)
}
二、各种继承方式
1. 原型链继承
function Parent(name) {
	this.name = name;
	this.colors = ['red', 'yellow', 'green'];
}
Parent.prototype.say = function() {
    console.log(this.name)
}
function Child() {}
Child.prototype = new Praent();
// 所有涉及到原型链继承的继承方式都要修改子类构造函数的指向,否则子类实例的构造函数会指向SuperType。
Child.prototype.constructor = Child;
let child1 = new Child('child1');
let child2 = new Child('child2');

child1.say()   //  undefined
child2.say()   // undefined
child1.say === child2.say // true

child1.colors.push('blue');
child1.colors;   // ['red', 'yellow', 'green', 'blue'];
child2.colors;    // ['red', 'yellow', 'green', 'blue'];

优点:

  1. 父类方法可以复用

缺点:

  1. 父类的引用属性会被所有子类实例共享 2. 子类构建实例时不能向父类传递参数
2. 构造函数继承
function Parent(name) {
	this.name = name;
	this.colors = ['red', 'yellow', 'green'];
}
Parent.prototype.say = function() {
    console.log(this.name)
}
function Child(name) {
	Parent.call(this, name)
}
Child.prototype.constructor = Child;
let child1 = new Child('child1');
let child2 = new Child('child2');
child1.say()   //  // child1.say is not a function

child1.colors.push('blue');
child1.colors;   // ['red', 'yellow', 'green', 'blue'];
child2.colors;    // ['red', 'yellow', 'green'];

在这里插入图片描述
优点:

  1. 父类的引用属性不会被共享 2. 子类构建实例时可以向父类传递参数

缺点:

  1. 不能继承父类原型上的方法
3. 组合继承
function Parent(name) {
	this.name = name;
	this.colors = ['red', 'yellow', 'green'];
}
Parent.prototype.say = function() {
    console.log(this.name)
}
function Child(name) {
	Parent.call(this, name);  // 第二次调用
	this.age = 18;
}
Child.prototype = new Parent(); // 第一次调用
Child.prototype.constructor = Child;  // 注意修复构造函数指向
let child1 = new Child('child1');
let child2 = new Child('child2');

child1.say()   // 'child1'
child2.say()   // 'child2'
child1.say === child2.say // true

child1.colors.push('blue');
child1.colors;   // ['red', 'yellow', 'green', 'blue'];
child2.colors;    // ['red', 'yellow', 'green'];

在这里插入图片描述

优点:

  1. 父类的方法可以被复用
  2. 父类的引用属性不会被共享
  3. 子类构建实例时可以向父类传递参数

缺点:

  1. 调用了两次父类的构造函数
4. 原型式继承

核心:

  1. 原型式继承的object方法本质上是对参数对象的一个浅复制。

优点:

  1. 父类方法可以复用

缺点:

  1. 父类的引用属性会被所有子类实例共享
  2. 子类构建实例时不能向父类传递参数
function object(o){
  function F(){}
  F.prototype = o;
  return new F();
}

var parent = {
    name: "parent",
    colors:['red', 'yellow', 'green']
};
let child1 = object(parent);
let child2 = object(parent);

child1.name = 'child1';  // 'child1'
child1.colors.push('blue')
child1.colors;   // ['red', 'yellow', 'green', 'blue'];

在这里插入图片描述

5. 寄生继承

使用原型式继承获得一个目标对象的浅复制,然后增强这个浅复制的能力。

function createAnother(original){ 
    var clone=object(original);    //通过调用函数创建一个新对象
    clone.say= function(){      //以某种方式来增强这个对象
        console.log(this.name);
    };
    return clone;                  //返回这个对象
}

var person = {
    name: "Nicholas",
    friends: ["Shelby", "Court", "Van"]
};

var anotherPerson = createAnother(person);
anotherPerson.sayHi(); //"hi"
6. 寄生组合继承

这是一种完美的继承方式。

function Parent(name) {
    this.name = name; 
    this.colors = ['red', 'yellow', 'green'];
}
Parent.prototype.say = function() { 
    console.log(this.name)
}
function Child(name,age) {
    Parent.call(this,name,age)  
    this.age = age;
}
Child.prototype = Object.create(Parent.prototype)
Child.prototype.constructor = Child
let child1 = new Child('child1');
let child2 = new Child('child2');

child1.say()   // 'child1'
child2.say()   // 'child2'
child1.say === child2.say // true

child1.colors.push('blue');
child1.colors;   // ['red', 'yellow', 'green', 'blue'];
child2.colors;    // ['red', 'yellow', 'green'];

在这里插入图片描述

7. ES6 继承

ES6继承的结果和寄生组合继承相似,本质上,ES6继承是一种语法糖。但是,寄生组合继承是先创建子类实例this对象,然后再对其增强;而ES6先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。

ES6实现继承的具体原理

class A {
}

class B {
}

Object.setPrototypeOf = function (obj, proto) {
  obj.__proto__ = proto;
  return obj;
}

// B 的实例继承 A 的实例
Object.setPrototypeOf(B.prototype, A.prototype);

// B 继承 A 的静态属性
Object.setPrototypeOf(B, A);

使用

class A {}

class B extends A {
  constructor() {
    super();
  }
}

ES6继承与ES5继承的异同:
相同点:

  1. 本质上ES6继承是ES5继承的语法糖

不同点:

  1. ES6继承中子类的构造函数的原型链指向父类的构造函数,ES5中使用的是构造函数复制,没有原型链指向。
  2. ES6子类实例的构建,基于父类实例,ES5中不是。
三、其他
1.Object.create()
Object.create() 的内部原理:
// 其中,o 是新创建对象的原型(对象)
function object(o) {
    function F() {}
    F.prototype = o
    return new F()
}
2. new 的过程
  1. 创建一个空对象obj

    let obj = new Object()
    
  2. 设置原型链

    obj.__proto__ = Func.prototype
    就是:将新对象的__proto__ 指向构造函数的prototype
    
  3. 将构造函数Func的this指向obj,并执行构造函数Func

    let result = Func.call(obj)
    就是:使用call或apply,将构造函数的this绑定到新对象,并执行构造函数
    
  4. 判断构造函数Func的返回值类型

    如果是引用类型,就返回这个引用类型的对象。如果是值类型或没有return,则返回空对象obj。
    if (typeof(result) === "object"){  
      func=result;  
    }  
    else{  
       func=obj; // 默认返回
    }
    注意:js中的构造函数,是不需要有返回值的,所以默认返回的是新创建的空对象obj
    

链接

js继承、构造函数继承、原型链继承、组合继承、组合继承优化、寄生组合继承
JS中的继承
一篇文章理解JS继承
六种Js中常见的继承方式(图解)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值