原型链

原型链

背景

大部分面向对象的编程语言,都是通过“类”(class)来实现对象的继承。JavaScript 语言的继承则是通过“原型对象”(prototype)。

概述

构造函数

  • 定义:

  • 为什么用:

    • 在使用对象字面量创建一系列同一类型的对象时,这些对象可能具有一些相似的特征(属性)和行为(方法),此时会产生很多重复的代码,而使用构造函数就可以实现代码的复用。

  • e.g.

function Animal(color) {
  this.color = color;
}
  • 缺点

    • 构造函数类似于一个模板,固定输入会得到固定输出,但是同一个构造函数的多个实例之间,是无法共享属性的。

function Cat(name, color) {
  this.name = name;
  this.color = color;
  this.meow = function() {
    console.log('喵喵');
  };
}

var cat1 = new Cat('大毛', '白色');
var cat2 = new Cat('二毛', '黑色');

cat1.meow === cat2.meow;
// false

protortype / __proto__

  • 每个函数都有一个 prototype 属性,指向一个对象,对于构造函数实例化的时候,该对象就成为了实例对象的原型。

  • 实现思路:所有实例都能共享原型对象(prototype)的属性和方法。

  • 好处:

    • 节省内存空间;

    • 共享数据,建立实例间的联系。

function Student() {}
Student.prototype.class = 'class A';
var student1 = new Student();
var student2 = new Student();
student1.class; // class A
student2.class; // class A

Student.prototype.class = 'class B';
student1.class; // class B
student2.class; // class B
  • 特殊:当实例自身拥有某个属性或方法的时候,就不会从原型上去寻找该属性或方法了。

  • 每个对象拥有一个__proto__属性,指向的就是构造函数的原型对象(prototype)。

原型链

  • 每个对象有拥有一个自己的原型,而每个原型也都是一个对象,也拥有自己的原型,因此会行程一条链路——原型链

  • 所有对象网上追溯都会追溯到Object.protptype,即为 Object 构造函数的 prototype 属性。

  • Object.prototype的原型是null

    • Object.getPrototypeOf(Object.prototype) // null

    • Object.getPrototypeOf方法返回参数对象的原型

  • 过程:

    • 读取对象的某个属性时,JavaScript 引擎先寻找对象本身的属性,如果找不到,就到它的原型去找,如果还是找不到,就到原型的原型去找。如果直到最顶层的 Object.prototype 还是找不到,则返回 undefined。如果对象自身和它的原型,都定义了一个同名属性,那么优先读取对象自身的属性,这叫做“覆盖”(overriding)

    • 注意:如果寻找层级过深,有可能会影响性能。

constructor

  • prototype 对象内有一个 constructor 属性,默认指向的是 prototype 所在的构造函数。它是从原型对象到构造函数关联的一个桥梁。

  • 存在的意义:

    • 可以得知某个实例对象是由哪个构造函数所创建的

    • 可以通过一个实例去创建另一个实例

      function Fun() {}
      var f = new Fun();
      var f2 = new f.constructor();
      f2 instanceof Fun; // true
  • 修改原型对象的时候切记要修改对应的 constructor,防止引用错误(e.g.直接修改**.prototype = {})

    • good:(保证instanceof不会失真)

          function Fun(){}
          Fun.prototype.** = function(){} // better
          Fun.prototype = {  // good
              constructor: Fun,
              **: **,
          }
    • bad:

          function Fun(){}
          Fun.prototype = {
              **: **
          }
  • 可以通过*.contructor.name 获取对应构造函数的名称

instanceof

  • 用于判断某个实例是否由某个构造函数所创建的。

  • 判断值的类型(用于判断最上层构造函数)

    • 例如

      var arr = [];
      arr instanceof Array; // true
      arr instanceof Object; // true
  • 只能用于对象的判断,不适用于原始值类型。

  • 对于 undefined 和 null,instanceOf 运算符总是返回 false。

  • 检查的是整个原型链上的构造函数。

    function A() {}
    var a = new A();
    a instanceof A; // true
    a instanceof Object; // true
    
    A instanceof Function; // true
    A instanceof Object; // true
  • a instanceof A等价于A.prototype.isPrototypeOf(a)

Object.create

  • 接收一个对象参数并返回一个对象结果,传入的参数为返回值的原型

  • 特殊:当传入 null 时,返回的对象是没有原型的,此时 instanceof 会失真

    • image

继承

js 继承的几种方式

  1. 原型链继承

  2. 构造函数继承(经典继承)

  3. 实例继承(原型式继承)

  4. 组合继承

  5. 寄生组合继承

  6. ES6 继承

和原型相关的一些主要方法

Object 自身的方法:

  1. Object.getPrototypeOf(获取指定对象原型)

    • Object.getPrototypeOf(instance) === instance.__proto__

    • Object.getPrototypeOf(Object)不是Object.prototype,而是Function.prototype

    • Object.getPrototypeOf(Object.prototype) // null

Object.prototype 上的方法:

  1. Object.prototype.isPrototypeOf(测试一个对象是否存在于另一个对象的原型链上)

    • Object.prototype.isPrototypeOf(instance); // true

    • instance instanceof Object; // true

    • isPrototypeOf() 与 instanceof 运算符不同。在表达式 "object instanceof AFunction"中,object 的原型链是针对 AFunction.prototype 进行检查的,而不是针对 AFunction 本身。

  2. Object.prototype.hasOwnProperty(判断自身是否有某属性,不包含原型)

  3. Object.prototype.propertyIsEnumerable(表示指定的属性是否可枚举,即属性是否可以被 for...in 循环枚举,注意:在原型链上 propertyIsEnumerable 不被考虑)

Last updated

Was this helpful?