js原型链

73 阅读2分钟

总结: 在 JavaScript 中,只有函数对象才有 prototype 属性。我们可以通过构造函数(如 new Person())来创建一个实例对象,该对象的 [[Prototype]](也可以通过 __proto__ 访问)会指向构造函数的 prototype 属性,从而形成原型链。

image.png

image.png

每一个对象都是由一个函数创建的。

javascript
CopyEdit
let arr = [1, 2, 3, 4];
// 这个数组是由 Array 函数创建的。
// new Array(); // 被调用来构造这个数组。

每一个函数都有一个原型对象(prototype object)与之关联。

原型对象中存储了该函数所创建的所有对象都会共享的属性和方法。

javascript
CopyEdit
arr.sort();  // sort 方法是在 Array.prototype 中定义的
// 所以文档中会写:Array.prototype.sort

原型链(prototype chain)连接了属于不同函数的原型对象。原型链的顶端是 null

你可以创建你自己的构造函数(constructor function)来创建自定义对象。这个构造函数会自动拥有一个原型对象。它也会连接到 Object.prototype(也就是 Object 函数的原型对象)。

因此,用你自定义的构造函数创建的对象不仅可以访问你构造函数原型中的方法,也可以访问 Object.prototype 中的方法。例如,toString() 方法实际上就是定义在 Object.prototype 中的。

当你看到错误信息说某个对象没有某个方法或属性时,这意味着 JavaScript 引擎沿着原型链一直查找,最终到达 null 还没找到这个属性或方法。

javascript
CopyEdit
function MyCustomObj() {
  this.f1 = function() {
    console.log('f1');
  }
}

MyCustomObj.prototype.f2 = function() {
  console.log('f2');
}

Object.prototype.f3 = function() {
  console.log('f3');
}

// 现在我们可以执行如下操作:
let myobj = new MyCustomObj();
myobj.f1();  // 输出 'f1'
myobj.f2();  // 输出 'f2'
myobj.f3();  // 输出 'f3'
// 这三个方法都可以调用成功。

1. [[Prototype]](内部属性)

[[Prototype]]每个对象都有的内部属性,它指向该对象的原型。

javascript
// 每个对象都有 [[Prototype]] 内部属性
const obj = {};
const arr = [];
const func = function() {};

// [[Prototype]] 是内部的,无法直接访问
// 只能通过特定方式间接访问

特点:

  • 是 ECMAScript 规范定义的内部属性
  • 用双方括号表示,如 [[Prototype]]
  • 不能直接访问,只能通过特定 API 访问

2. __proto__(访问器属性)

__proto__访问对象 [[Prototype]] 内部属性的方式

javascript
const obj = {};

// __proto__ 是访问 [[Prototype]] 的方式
console.log(obj.__proto__);                    // Object.prototype
console.log(obj.__proto__ === Object.prototype); // true

// 等价的标准方式
console.log(Object.getPrototypeOf(obj));       // Object.prototype
console.log(Object.getPrototypeOf(obj) === Object.prototype); // true

特点:

  • 是访问器属性(getter/setter)
  • 最初是非标准的,后来被标准化
  • 现代开发推荐使用 Object.getPrototypeOf()Object.setPrototypeOf()

3. prototype(函数属性)

prototype只有函数才有的属性,它是一个对象,用作构造函数创建实例时的原型。

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

// 只有函数有 prototype 属性
console.log(Person.prototype);              // { constructor: Person }
console.log(typeof Person.prototype);       // "object"

// 普通对象没有 prototype 属性
const obj = {};
console.log(obj.prototype);                 // undefined

特点:

  • 只有函数对象才有这个属性
  • 是一个普通对象
  • 用作构造函数时,新创建的实例的 [[Prototype]] 会指向这个 prototype 对象