# 继承

记录几个继承方法

# 原型链继承

function Car () {
    this.name = "car";
}

Car.prototype.sayName = function () {
    console.log(this.name);
}

function BenTian() { }

BenTian.prototype = new Car();

var bentian = new BenTian();
bentian.sayName();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  • 基于原型链的继承,这样引用类型的值会被所有的实例所共享。
  • 在创建子类的实例的时候,不能够在不影响其他实例的情况下,传参数(super)

# 使用构造函数继承

function Car () {
    this.name = "car";
    this.say = function () {
        console.log(this.name);
    }
}

Car.prototype.sayName = function () {
    console.log(this.name);
}

function BenTian() {
    Car.call(this);
}

function FengTian() {
    Car.call(this);
}

var bentian = new BenTian();
bentian.name = 'bentian';
bentian.say(); // bentian
// bentian.sayName(); // Uncaught TypeError: bentian.sayName is not a function

var bentian = new FengTian();
bentian.say(); // car
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

这种方式的继承的缺点也是很明显,虽然解决了原型链继承的问题,但是方法都必须在constructor中定义,函数并不能被复用,在创建实例的时候都会被创建一遍。

# 组合继承(伪经典继承)

function Car (name) {
    this.name = name;
}

Car.prototype.sayName = function () {
    console.log(this.name);
}

function Ri(name, color) {
  Car.call(this, name);
  this.color = color;
}

Ri.prototype = new Car();
Ri.prototype.constructor = Car;

var bentian = new Ri("bentian", "red");
var fengtian = new Ri("fengtian", "black");

console.log(bentian.name) // bentian
console.log(bentian.color) // red

console.log(fengtian.name) // fengtian
console.log(fengtian.color) // black

bentian.sayName(); // bentian
fengtian.sayName(); // fengtian
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

这种继承方式解决了原型链继承和构造函数继承的弊端,也是成功的继承了父类,但是这种继承方式本身也是存在着一些问题,比如多次调用了父类的constructor

# 原型式继承

var car = {
    name: "car",
    color: ['red', 'blue', 'black']
}

var car1 = Object.create(car);
car1.__proto__ === car;

var car2 = Object.create(car);
car2.__proto__ === car;

car1.name = "bentian";
car2.name = "fengtian";

car1.color.push('white')
console.log(car2.color) // ["red", "blue", "black", "white"]

// 引申
function createObj(o) {
    function fn() {};
    fn.prototype = o;
    return new fn();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

由此可见,原型式继承并不会重新创建引用类型的值,引用类型的值会被所有的实例共享。

如果理解不了,那你需要更多的去了解按值传递和按址(引用)传递的区别了

# 寄生式继承

function createObj (o) {
    var clone = Object.create(o);
    clone.sayName = function () {
        console.log('zhangsan');
    }
    return clone;
}
1
2
3
4
5
6
7

寄生式继承,和使用构造函数模式一样,都会在创建实例的时候重新创建父类。(也就是相当于复制出来一份 然后基于这个复制项来创建。 这样来保证两两互不干预)

# 寄生组合式继承


function inheritPrototype(Child, Parent) {
  var prototype = Object.create(Parent.prototype);
  prototype.constructor = Child;
  Child.prototype = prototype;
}

function Car (name) {
    this.name = name;
}

Car.prototype.sayName = function () {
    console.log(this.name);
}

function Ri(name, color) {
    // 传值
    Car.call(this, name);
    this.color = color;
}

inheritPrototype(Ri, Car);

var bentian = new Ri("bentian", "red");
var fengtian = new Ri("fengtian", "black");

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

保证了原型链链路的正确性,没有多余的属性等优点,所以最为理想的ES5继承

# 组合式寄生式继承 简约版

function Car (name) {
    this.name = name;
}

Car.prototype.sayName = function () {
    console.log(this.name);
}

function Ri(name, color) {
    Car.call(this, name);
    this.color = color;
}

Ri.prototype = Object.create(Car.prototype, {
    constructor: {
        value: Ri
    }
})

var bentian = new Ri("bentian", "red");
var fengtian = new Ri("fengtian", "black");

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

巧妙的利用了Object.create的第二个参数,实现修改原型的constructor的值。

# ES6继承

class Car {
    constructor(name) {
        this.name = name;
    }
    sayName() {
        console.log(this.name);
    }
}

class BenTian extends Car {
    constructor(name, color) {
        super(name);
        this.color = color;
    }
    sayName() {
        //继承父类方法
        super.sayName()
        // 子类自己的
        console.log(this.color);
    } 
}

const bentian = new BenTian('bentian', 'red');
console.log(bentian.name); // bentian
console.log(bentian.color); // red
bentian.sayName(); // bentian red
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
  • 在语法糖下代码量明显减少
  • ES5的继承是首先在子类中创建自己的this指向,最后将方法添加到this中,例如:
BenTian.prototype = new Car() || Car.apply(this) || Car.call(this)
1
  • ES6继承,子类使用super关键字先创建父类实例的this,最后在子类中修改自己的this

# 致谢

感谢大家阅读我的文章,如果对我感兴趣可以点击页面右上角,帮我点个star。

作者:前端小然子

链接: https://xiaoranzife.com/guide/jichu/%E7%BB%A7%E6%89%BF.html

来源:前端小然子的博客

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

上次更新: 2019-11-19 4:44:56 ├F10: PM┤