深入理解Javascript中原型链机制

原型链机制prototype chain是Javascript设计中较为重要的内容,对象之间通过原型链建立彼此之间的关系。在Kyle Simpson’s所著的《You Don’t Know JS》一书中介绍了两种Javascript中组织代码的方式:OLOO(Object Linked to Other Object)以及Prototype Design Pattern。两种设计模式均围绕Javascript中的原型链机制给出了不同思想的解释。因此正确理解Javascript中的原型链机制至关重要。

一、_proto_

Object.prototype的__proto__属性是一个访问器属性(一个getter函数和一个setter函数),它公开访问它的对象的内部[[Prototype]](对象或null)。

目前__proto_属性已经被大多数浏览器厂商所支持,并且成为ES6的标准规范。下文中所做的演示仅仅是想说明原型链的原理,在生产环境以及平时的编码中尽量避免对 _proto 的直接操作,因为这可能涉及到性能问题。

[[Prototype]]是实现JS中两个对象建立联系(或者说实现继承)的关键,通过[[Prototype]]可以建立多个对象之间的联系,从而对象之间根据彼此之间的联系成为链状,称之为原型链。

通俗的讲,通过[[Prototype]]可以获得与该对象相关联对象的相关属性。但是通过[[Prototype]]可以获得相关联对象的哪些属性呢,又是如何建立两个对象之间连接的呢?

通过以下三种方式可以直接建立两个对象的链接:

  • new构造函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    function Person(name, age, gender) {
    this.name = name;
    this.age = age;
    this.gender = gender;
    }
    let p1 = new Person('monster1935', '25', 'male');

    Person.__proto__ === Function.prototype;// true
    p1.__proto__ === Person.prototype;// true

    如上面的代码所示,这种方式建立了实例对象与构造函数对象(或者暂且称之为类)的关系。实例对象p1通过__proto__可以访问Person对象的prototype对象中的属性。

  • Object.create()

    1
    2
    3
    4
    5
    6
    7
    let obj2 = {
    name: 'monster1935'
    };
    let obj1 = Object.create(obj2);
    obj1.name; // monster1935
    obj1.__proto__ === obj2;// true

    如上面的代码所示, Object.create(obj2)以obj2为原型,创建了obj1与obj2的联系。

  • Object.setPrototypeOf()

    设置一个指定的对象的原型 ( 例如,内置的 [[Prototype]]属性)到另一个对象或 null。和设置__proto__一样会带来性能问题,尽量使用Object.create()来动态设置对象的原型。

二、prototype

prototype属性代表原型对象。并非所有的对象都有该属性,在Javascript中创建对象的几种方式讨论一文中给出过关于Javascript中对象的分类,Javascript中的内部对象都是默认含有prototype属性的,自定义对象是没有该属性的。

三、深入理解原型链

如上图所示,图中给出了Javascript中原型链的图示。

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
28
29
30
31
Object.prototype.__proto__ === null;

Function.prototype.__proto__ === Object.prototype;// true
String.prototype.__proto__ === Object.prototype; // true
Array.prototype.__proto__ === Object.prototype; // true
Boolean.prototype.__proto__ === Object.prototype; // true
Number.prototype.__proto__ === Object.prototype; // true
Date.prototype.__proto__ === Object.prototype; // true
Error.prototype.__proto__ === Object.prototype; // true

// 内置对象都是有Function类制造的
Function.__proto__ === Function.prototype;// true;
Object.__proto__ === Function.prototype; // true
String.__proto__ === Function.prototype;// true
Array.__proto__ === Function.prototype;// true
Number.__proto__ === Function.prototype;// true
Boolean.__proto__ === Function.prototype;// true
Date.__proto__ === Function.prototype;// true
Error.__proto__ === Function.prototype;// true

function Person(name, age, gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
let p1 = new Person('monster1935', '25', 'male');

Person.__proto__ === Function.prototype;// true
p1.__proto__ === Person.prototype;// true


如上所示,

  • 所有原型对象的(除Object外)均指向Object.prototype,因为他们的本质也是对象。所有对象均继承自Object.prototype
  • Object.prototype指向null,为原型链的终点。
  • 所有内部对象的原型均指Function.prototype,即他们都是继承自Function,**Function的原型也指向自己的原型对象即Function.__proto__ === Function.prototype**。
  • 构造函数对象默认含有prototype属性,实例对象的原型默认指向构造函数对象的原型对象即p1.__proto__ === Person.prototype;

参考文章:

JavaScript 世界万物诞生记

作者

monster1935

发布于

2017-03-20

更新于

2024-09-25

许可协议