梳理一下 JavaScript 中的继承

梳理 JavaScript 中的继承问题,则不得不先理解 Js 中的原型链,因为 ECMAScript 主要是基于原型链实现继承的。

原型链

在 Js 中,每个函数都有一个 prototype 属性,其指向该函数的原型对象。而函数的原型对象中,有一个 constructor 属性,指回向该函数。当函数被当作构造函数,使用 new 运算符生成实例时,在生成的实例对象中有一个内部属性 __proto__ 属性,该属性也指向函数的原型对象。在原型对象上有 __proto__ 指向原型对象的原型对象,依次传递,直到指向 Object.prototype 对象为止,即构成了原型链。如下图所示:

原型链继承

function Animal(name) {
    this.name = name;
    this.colors = ['black', 'red', 'pink'];
}
Animal.prototype.run = function (){
    console.log('running');
}
function Cat(age) {
    this.age = age;
}
Cat.prototype = new Animal('cat'); // 实现原型链继承

var cat1 = new Cat(1); 

console.log(cat1);  // Cat {
                    //    age: 1,
                    //    __proto__: Animal {
                    //    colors: (4) ["black", "red", "pink", "blue"],
                    //    name: "cat",
                    //    __proto__: Object
                    // }}
console.log(cat1 instanceof Cat); // true
console.log(cat1 instanceof Animal); // true
cat1.run(); // running

console.log(cat1.colors); // ['black', 'red', 'pink']
cat1.colors.push('blue');

var cat2 = new Cat(2);
console.log(cat2.colors); // ['black', 'red', 'pink','blue']
// Cat的所有实例会共享 原型对象上的属性,其中一个实例的修改,会影响到其他实例。

借助构造函数实现继承

function Animal(name) {
    this.name = name;
    this.colors = ['black', 'red', 'pink'];
}
Animal.prototype.run = function (){
    console.log('running');
}

function Cat(age) {
    Animal.apply(this, ['cat']); // 借助构造函数实现继承
    this.age = age;
}

var cat1 = new Cat(1);
console.log(cat1); // Cat {
                   //    age: 1,
                   //    colors:(4) ["black", "red", "pink", "blue"],
                   //    name: "cat",
                   //    __proto__: Object
                   //  }
console.log(cat1 instanceof Cat); // true
console.log(cat1 instanceof Animal); // false

cat1.colors.push('blue');
console.log(cat1.colors); // ['black', 'red', 'pink','blue']

var cat2 = new Cat(2);
console.log(cat2.colors); // ['black', 'red', 'pink']

cat1.run(); // Uncaught TypeError: cat1.run is not a function

// 无法使用在原型对象上的函数,即无法复用函数

组合继承

function Animal(name) {
    this.name = name;
    this.colors = ['black', 'red', 'pink'];
}
Animal.prototype.run = function (){
    console.log('running');
}

function Cat(age) {
    Animal.apply(this); // 借助构造函数实现 实例属性的继承
    this.age = age;
}
Cat.prototype = new Animal('cat'); // 借助原型链实现 原型属性和方法的继承
Cat.prototype.constructor = Cat; // 设置原型对象的 constructor 指向

var cat1 = new Cat(1);
console.log(cat1); // Cat {
                   //   name: undefined, 
                   //   colors: ["black", "red", "pink"],
                   //   age: 1,
                   //   __proto__: Animal{
                   //       colors: (3) ["black", "red", "pink"],
                   //       name: "cat",
                   //       __proto__: {
                   //            run: ƒ ()
                   //            ...
                   //       }
                   //     }
                   //  }
console.log(cat1 instanceof Cat); // true
console.log(cat1 instanceof Animal); // true
cat1.run(); // running

// 无论什么情况下,都会调用两次超类型构造函数

优化继承

// Object.create() 方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。
// 即借助原型基于已有的对象创建新对象,同时不必因此创建自定义类型。
// 从本质上讲,object 函数对传入的对象进行了一次浅复制。
Object.create()

function object(o) {
    function F() {};
    F.prototype = o;
    return new F();
}

// 优化组合继承
function Animal(name) {
    this.name = name;
    this.colors = ['black', 'red', 'pink'];
}
Animal.prototype.run = function (){
    console.log('running');
}

function Cat(age) {
    Animal.apply(this); // 借助构造函数实现 实例属性的继承
    this.age = age;
}
Cat.prototype = Object.create(Animal.prototype); // 浅拷贝 原型属性方法
Cat.prototype.constructor = Cat;

var cat1 = new Cat(1); 

console.log(cat1); 
console.log(cat1 instanceof Cat); // true
console.log(cat1 instanceof Animal); // true
cat1.run(); // running

console.log(cat1.colors); // ['black', 'red', 'pink']

cat1.colors.push('blue');

console.log(cat1.colors); // ['black', 'red', 'pink', 'blue']

var cat2 = new Cat(2);
console.log(cat2.colors); // ['black', 'red', 'pink']

ES6 继承

class Animal {
    constructor (name) {
        this.name = name;
    }

    run() {
        console.log('running');
    }
}

class Cat extends Animal{
    constructor (name, age) {
        super(name);
        this.age = age;
    }
}

let cat1 = new Cat('cat', 1);
cat1.run(); // running
console.log(cat1);

猜你喜欢

转载自www.cnblogs.com/horizon-jens/p/11937290.html