【ECMAScript】原型链及原型链继承总结

目录

1. 前言

2. 原型链

3. 继承方式

a. 原型链继承

b. 盗用构造函数(经典继承)

c. 组合继承(伪经典继承)

d. 原型式继承

e. 寄生式继承

f. 寄生式组合继承


1. 前言

        JavaScript的面向对象世界里,是基于原型链继承的,比如调用对象instance的print方法。

  • 首先,在对象instance中找;
  • 如果找不到,接着往下一个节点对象instance.__proto__中找(即Person.prototype);
  • 如果还是找不到,继续在下一个节点对象instance.__proto__.__proto__中找(即Object.prototype);
  • 直到找到null,最后一个节点对象指向null。

如果了解数据结构的同学,立马知道这是一个单向链表结构。本文梳理一下原型链、构造函数、原型链继承知识点。

2. 原型链

        示例代码定义了构造函数Person(是一个函数对象),并使用new操作符示例化Person,得到示例对象instance。

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

const instance = new Person('xiao ming');

原型链和构造函数的指向关系,如图所示:

【ECMAScript】原型链及原型链继承总结_第1张图片

对于实例化对象instance、函数对象Person、函数对象Function、函数对象Object,原型链的结构(指向关系)为:

  • instance -> Person.prototype -> Object.prototype -> null
  • Person -> Function.prototype -> Object.prototype -> null
  • Function -> Function.prototype -> Object.prototype -> null
  • Object -> Function.prototype -> Object.prototype -> null

实例化对象instance、函数对象Person、函数对象Function、函数对象Object,此四个对象均有一个指向构造函数的原型对象的隐式属性__proto__(PS:此处构造函数是指,实例化instance、Person、Function、Object这4个对象的各个构造函数)

  • instance.__proto__===  Person.prototype
  • Person.__proto__ === Function.prototype
  • Function.__proto__ === Function.prototype
  • Object.__proto__ === Function.prototype

一个指向构造函数的属性constructor

  • instance.constructor ===  Person
  • Person.constructor === Function
  • Function.constructor === Function
  • Object.constructor === Function

        函数对象Person、Function、Object同时也是作为构造函数的函数对象(简称构造函数对象),会比构造函数对象多出一个属性:原型对象prototype,并且原型对象prototype的constructor属性指回构造函数对象

  • Person.prototype.constructor === Person
  • Function.prototype.constructor === Function
  • Object.prototype.constructor === Object
3. 继承方式
a. 原型链继承

        通过修改构造函数的原型对象,将父构造函数的实例化对象赋值给prototype

function Animal() {
   this.animalName = 'animal';
}

Animal.prototype.getAnimalName = function() {
   return this.animalName;
}

function Cat() {
    this.catName = 'mimi';
}

// 通过修改构造函数对象Cat的原型对象prototype(设置为Animal的一个示例对象),实现原型链继承
Cat.prototype = new Animal();

Cat.prototype.constructor = Cat;

// 并在原型对象prototype新增加一个方法getCatName方法
Cat.prototype.getCatName = function() {
    return this.catName;
}

let cat = new Cat();

console.log(cat.getAnimalName());

构成的原型链:cat -> Cat.prototype(Animal的实例化对象) -> Animal.prototype -> Object.prototype -> null

如果一个实例对象的原型链中出现过相应的构造函数,使用instanceof运算符,会返回true。

  • cat instanceof Cat === true
  • cat instanceof Animal === true
  • cat instanceof Object === true

存在两个问题

  • this.animalName被所有Cat的实例化对象共享了
  • 无法传参给父构造函数Animal
b. 盗用构造函数(经典继承)

        将父构造函数当成普通函数执行,仅修改父构造函数this指向

function Animal() {
   this.animalName = 'animal';
}

Animal.prototype.getAnimalName = function() {
   return this.animalName;
}

function Cat() {
// 盗用构造函数:父构造函数属性不会被子构造函数实例化对象共享了,并且可以传参给父构造函数
    Animal.call(this, arg1, arg2, ...);
    this.catName = 'mimi';
}


// 并在原型对象prototype新增加一个方法getCatName方法
Cat.prototype.getCatName = function() {
    return this.catName;
}

let cat = new Cat();

console.log(cat.getAnimalName());

构成的原型链:cat -> Cat.prototype -> Object.prototype -> null

 存在问题

  • 无法继承父构造函数的原型对象上的方法/属性,破坏了原型链继承
c. 组合继承(伪经典继承)

        组合原型链继承和盗用构造函数(经典继承)。

function Animal() {
   this.animalName = 'animal';
}

Animal.prototype.getAnimalName = function() {
   return this.animalName;
}

function Cat() {
// 盗用构造函数:父构造函数属性不会被子构造函数实例化对象共享了,并且可以传参给父构造函数
// 继承属性,第二次调用
    Animal.call(this, arg1, arg2, ...);
    this.catName = 'mimi';
}

// 通过修改构造函数对象Cat的原型对象prototype(设置为Animal的一个示例对象),实现原型链继承
// 继承方法,第一次调用
Cat.prototype = new Animal();

// 更改构造函数指向
Cat.prototype.constructor = Cat;

// 并在原型对象prototype新增加一个方法getCatName方法
Cat.prototype.getCatName = function() {
    return this.catName;
}

let cat = new Cat();

console.log(cat.getAnimalName());

构成的原型链:cat(拥有了Animal的属性) -> Cat.prototype(Animal的实例化对象) -> Animal.prototype -> Object.prototype -> null

存在问题:

  • 调用了两次父构造函数
  • 子构造函数的原型对象,多出了父构造函数中的属性
d. 原型式继承
function object(o) {
    function F() {}
    F.prototype = o;
    return new F(); 
}


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

let anotherPerson = object(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");

let yetAnotherPerson = object(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");
console.log(person.friends); // "Shelby,Court,Van,Rob,Barbie"

构成的原型链:

anotherPerson -> peson(F.prototype) -> Object.prototype -> null

yetAnotherPerson -> peson(F.prototype) -> Object.prototype -> null

object生成的对象共享传入的对象的属性/方法

Object.create(o, {name: { value: 'gg' }})不传第二项参数时,就是原型式继承。

适用场景:不需要创建构造函数,但需要对象之间需要共享数据和方法

e. 寄生式继承
function object(o) {
    function F() {}
    F.prototype = o;
    return new F(); 
}

function create(original) {
    let clone = object(original);
    // 比原型式继承多了给实例对象新增方法
    clone.say = function() {
        console.log('hello');
    };
    return clone;
}


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

let anotherPerson = create(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");
anotherPerson.say();

比原型式继承多了给实例对象新增方法。

f. 寄生式组合继承

       “盗用构造函数”无法继承父构造函数的原型对象上的方法/属性,”组合式继承“为解决“盗用构造函数”的问题,调用了两次父构造函数,子构造函数的原型对象多出了父构造函数的属性。

        “寄生组合式继承”避免使用父构造函数实例化一个对象作为子构造函数的原型,而是设置一个空的构造函数F,将父构造函数的原型对象赋值给空的构造函数F,并返回new F()实例化对象作为子构造函数的原型。

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

function inheritProtype(Cat, Animal) {
    Cat.prototype = object(Animal.prototype);
    // 构造函数的原型对象的contructor属性,需要指回构造函数本身
    Cat.prototype.constructor = Cat;
}

function Animal() {
   this.animalName = 'animal';
}

Animal.prototype.getAnimalName = function() {
   return this.animalName;
}

function Cat() {
// 盗用构造函数:父构造函数属性不会被子构造函数实例化对象共享了,并且可以传参给父构造函数
// 继承属性,第二次调用
    Animal.call(this, arg1, arg2, ...);
    this.catName = 'mimi';
}

inheritProtype(Cat, Animal);


// 并在原型对象prototype新增加一个方法getCatName方法
Cat.prototype.getCatName = function() {
    return this.catName;
}

let cat = new Cat();

console.log(cat.getAnimalName());

 原型链结构:cat -> Cat.prototype -> F.prototype(Animal.prototype) -> Object.prototype -> null

注:以上,如有不合理之处,还请帮忙指出,大家一起交流学习~

你可能感兴趣的:(ecmascript,javascript,开发语言)