深入探究Prototype与原型链

Prototype_proto_的区别

  • Prototype属性(原型对象)。是函数(Function)独有的属性,非函数类对象没有这个属性。作用是,作为一个“公共仓库”,用来放置希望所有实例共享的属性和方法。
  • _proto_属性(隐式原型指针)。JavaScript 中的每一个对象(包含函数、数组、实例等)都有。作用是,指向创建该对象的构造函数的 prototype。它就是对象用来“向上查找”的连接线。

一个函数对象拆解后的实际样子

要彻底搞懂一个函数对象在内存和浏览器控制台里的完整拆解结构,我们可以将它划分为 4 大核心板块

在 Chrome 控制台中运行 console.dir(Foo) 并展开后,你能看到它的全部内部组成:

function Foo(name) {
  this.name = name;
}
Foo.prototype.sayHello = function() {};
Foo.myStaticMethod = function() {}; // 静态方法

console.dir(Foo);

函数对象的 4 大结构板块

板块 1:函数自身的常规属性(静态属性与方法)

这是挂载在函数本身上的属性。当你把函数当成普通对象使用时(例如 Foo.myStaticMethod = ...),这些属性直接存在于函数自身。

  • name:函数名(如 "Foo")。
  • length:形参个数(如 1)。
  • 自定义静态属性/方法:例如 myStaticMethodArray.isArrayObject.keys 等。

板块 2:prototype(显式原型板块 —— “供实例调用的共享仓库”)

这个板块专门负责给由 new Foo() 创建出来的实例对象提供共享的方法和属性。

  • 主要内容:
    1. 自定义共享方法/属性:例如 sayHello,所有实例共享此方法。
    2. constructor(构造器指针):默认存在,反向指回函数 Foo 本身(Foo.prototype.constructor === Foo)。
    3. [[Prototype]](原型的原型)Foo.prototype 本身也是一个普通对象,所以它也有自己的 [[Prototype]],指向 Object.prototype

板块 3:[[Prototype]](隐式原型板块 —— “函数自身的继承通道”)

这是函数自身作为对象所拥有的原型,指向 Function.prototype。因为函数本身也是由 JavaScript 的内置构造函数 Function 创建的。

  • 主要内容:
    1. 内置函数方法call()apply()bind()toString() 等。你能在任何函数上直接调用 Foo.call(),就是从这个板块里继承来的。
    2. constructor:指向 Function 构造函数。
    3. [[Prototype]]:指向 Object.prototype(原型链继续向上查找)。

板块 4:内部槽 / 隐式作用域(引擎私有板块)

控制台中以 [[...]] 双方括号包裹的内容,属于 JavaScript 引擎内部维护的私有属性。

  • [[Scopes]]:函数的作用域链(包含了函数定义时的闭包 Closure、全局作用域 Global 等)。
  • [[FunctionLocation]]:源代码中该函数定义的文件和行号位置。

控制台展开结构对照表

在 Chrome / Edge 控制台里展开 console.dir(Foo),对应结构如下:

板块名称在控制台中的显示内容是什么谁会用到它
自身属性name, length, myStaticMethod函数名、形参个数、静态属性/方法函数自身(如 Foo.myStaticMethod()
prototype 板块prototypesayHello, constructor: ƒ Foo(), [[Prototype]]: Objectnew Foo() 生成的实例
[[Prototype]] 板块[[Prototype]]: ƒ ()call(), apply(), bind(), constructor: ƒ Function()函数 Foo 自身
作用域与元数据[[Scopes]], [[FunctionLocation]]闭包变量(Closure)、环境上下文、代码源码位置JS 执行引擎

代码验证(一秒分清两个 Prototype)

JavaScript

// 1. 查看 Foo 给【实例】准备的共享仓库 (板块 2)
console.log(Foo.prototype.sayHello); // ƒ () {}

// 2. 查看 Foo 【自己】调用的函数能力 (板块 3)
console.log(Foo.call); // ƒ call() { [native code] }

// 验证:Foo 的 [[Prototype]] 就是 Function.prototype
console.log(Object.getPrototypeOf(Foo) === Function.prototype); // true