js继承的七种方式

1、原型链继承 – 重写prototype

实现:直接重写构造函数的原型,将构造函数的原型赋值为想要继承的父级构造函数的示例对象。
缺点:通过实例对象改变某个 key 值,所有实例相应的值都发生改变。

举个🌰

function Parent() {
    this.name = 'parent';
    this.play = [1, 2, 3]
}

function Child() {
    this.type = 'child';
}
// 重写Child的原型对象
Child.prototype = new Parent();
// Parent实例上没有constructor,需要手动挂上构造器,指向自己的构造函数
Child.prototype.constructor = Child;

var child1 = new Child();
var child2 = new Child();
// 改变Child某个实例对象key(Child原型上的)
child1.play.push(4);
// 所有实例对象上的相应的key值都发生了变化
console.log(child1.play); // [1, 2, 3, 4]
console.log(child2.play); // [1, 2, 3, 4]
console.log(new Child().play); // [1, 2, 3, 4]
console.log(new Parent().play); // [1, 2, 3]

2、构造函数继承 – Parent.call(this)

实现:在构造函数内将父级构造函数的 this 指向当前构造函数的 this

缺点:只能继承父级实例上的属性和方法,不能继承父级原型对象 prototype 上的属性和方法。

举个🌰

 function Parent(){
    this.name = 'parent';
}
Parent.prototype.getName = function () {
    return this.name;
}

function Child(){
    Parent.call(this);
    this.type = 'child'
}

let child = new Child();
console.log(child);  // 没问题
console.log(child.getName());  // 会报错 Uncaught TypeError: child.getName is not a function

3、组合继承 – 重写prototype + Parent.call(this)

实现:重写构造函数的原型为父级构造函数的实例对象,同时在构造函数内将父级构造函数的 this 指向当前的构造函数。
缺点Parent 会被调用两次

举个🌰

function Parent () {
    this.name = 'parent';
    this.play = [1, 2, 3];
}
Parent.prototype.getName = function () {
    return this.name;
}

function Child() {
    // 第二次调用 Parent()
    Parent.call(this);
    this.type = 'child';
}
// 第一次调用 Parent()
Child.prototype = new Parent();
// 手动挂上构造器,指向自己的构造函数
Child.prototype.constructor = Child;

console.log(new Child());

var child1 = new Child();
var child2 = new Child();
child1.play.push(4);
console.log(child1.play); // [1, 2, 3, 4]
console.log(child2.play); // [1, 2, 3]
console.log('child1 getName...', child1.getName()); // 正常输出'parent'
console.log('child2 getName...', child2.getName()); // 正常输出'parent'

4、原型式继承 – Object.create(Parent),不添加额外的属性和方法

实现:利用 Object.create 方法实现普通对象的继承。
缺点:原型指向同一个对象,通过某个实例改变的原型上的 key 会导致所有实例读取到的值都是被修改后的 key 值(Object.create 方法实现的是浅拷贝,多个实例的引用类型属性指向相同的内存)。

举个🌰

let parent = {
    name: "parent",
    friends: ["p1", "p2", "p3"],
    getName: function() {
        return this.name;
    }
};

// 声明一个过渡对象
function clone(original) {
    let clone = Object.create(original);
    return clone;
}

let person1 = clone(parent);
person1.name = "tom";
person1.friends.push("jerry");

let person2 = clone(parent);
person2.friends.push("lucy");

console.log(person1.name); // tom
console.log(person1.name === person1.getName()); // true
console.log(person2.name); // parent
console.log(person1.friends); // ["p1", "p2", "p3","jerry","lucy"]
console.log(person2.friends); // ["p1", "p2", "p3","jerry","lucy"]

5、寄生式继承 – Object.create(Parent),添加额外的属性和方法

跟原型式继承一样一样的,就是多加了一些额外的属性和方法。缺点也一样。

实现:利用 Object.create 方法实现普通对象的继承,并添加额外的属性和方法。
缺点:原型指向同一个对象,通过某个实例改变的原型上的 key 会导致所有实例读取到的值都是被修改后的 key 值(Object.create 方法实现的是浅拷贝,多个实例的引用类型属性指向相同的内存)。

举个🌰

let parent = {
    name: "parent",
    friends: ["p1", "p2", "p3"],
    getName: function() {
        return this.name;
    }
};

// 声明一个过渡对象
function clone(original) {
    let clone = Object.create(original);
    clone.getFriends = function() {
        return this.friends;
    };
    return clone;
}

let person1 = clone(parent);
person1.name = "tom";
person1.friends.push("jerry");

let person2 = clone(parent);
person2.friends.push("lucy");

console.log(person1.name); // tom
console.log(person1.name === person1.getName()); // true
console.log(person2.name); // parent
console.log(person1.friends); // ["p1", "p2", "p3","jerry","lucy"]
console.log(person2.friends); // ["p1", "p2", "p3","jerry","lucy"]

6、寄生组合式继承 – Object.create(Parent) + Parent.call(this)

实现:重写构造函数的原型为父级构造函数的实例对象,同时在构造函数内将父级构造函数的 this 指向当前的构造函数。
优点Parent 只会调用1次,是较优的继承方案。

举个🌰

function Parent() {
    console.log('Parent....');
    this.name = 'parent';
    this.play = [1, 2, 3]
}

function Child() {
    // 2、在构造函数内部将父级构造函数this指向当前函数的this
    Parent.call(this);
    this.type = 'child';
    this.friends = 'lucy';
}

// 1、重写原型对象,还原构造器
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;

Child.prototype.getFriends = function () {
    return this.friends;
}

var child = new Child();
var child1 = new Child();
child1.play.push(44);
console.log('child play...', child.play);// child play... (3) [1, 2, 3]
console.log('child1 play...', child1.play);// child1 play... (4) [1, 2, 3, 44]

7、ES6类的继承 – extends + super(props)

利用 ES6class,结合 extends 关键字 和 super(props) 方法实现对父类属性和方法的继承。

举个🌰

class Person {
    constructor(money) {
        this.money = money;
    }
    getMoney() {
        console.log(this.name + " get Person's money $" + this.money)
    }
}
class Child extends Person {
    constructor(money, name) {
        // 子类中存在构造函数,则需要在使用“this”之前首先调用 super()。
        super(money);
        this.name = name;
    }
}
const child = new Child(1000, 'child');
child.getMoney();// child get Person's money $1000
转载请说明出处内容投诉
CSS教程_站长资源网 » js继承的七种方式

发表评论

欢迎 访客 发表评论

一个令你着迷的主题!

查看演示 官网购买