深入探究Prototype与原型链

Prototype_proto_的区别

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

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

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

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

// 1. 打印函数对象(查看它的 prototype 和 [[Prototype]])
function Foo() {}
Foo.prototype.sayHi = function() {};

console.dir(Foo);

函数对象的 4 大结构板块

第一块:函数自身的基础元数据(自身属性)

  • length: 0
    • 含义: 函数的形参个数。因为你的 Foo() 没有写入参,所以是 0。
  • name: "Foo"
    • 含义: 函数的名字字符串。

第二块:prototype(给 future 实例准备的共享仓库)

展开 ▼ prototype: 这一段,就是将来使用 new Foo() 生产出的实例对象能共享的所有内容:

  • sayHi: ƒ ()
    • 含义: 你手动挂载在 Foo.prototype 上的方法。
  • constructor: ƒ Foo()
    • 含义: 原型对象上的“反向指针”,表明这个原型仓库是属于 Foo 函数的。
  • ▼ [[Prototype]]: Object
    • 含义: Foo.prototype 本身也是一个普通的 JavaScript 对象,所以它的原型指向了最高顶层的 Object.prototype

第三块:已废弃的旧语法属性与引擎标记

  • arguments: null / caller: null
    • 含义: 传统 JS 中用来获取“传入参数”和“调用当前函数的父级函数”的旧属性。在现代 ES6 严格模式下已被禁用,所以显示为 null
  • [[FunctionLocation]]: prototype.html:10
    • 含义: 浏览器引擎的标记,提示你这个函数是在代码文件 prototype.html 的第 10 行被定义的。

第四块:▼ [[Prototype]]: ƒ ()(Foo 函数自己继承的能力)

这是最关键的隐式原型指针(即 Foo.__proto__。因为函数本身也是一个对象,所以它也需要继承“作为函数”的能力,这里指向的是 Function.prototype

  • apply: ƒ apply() / bind: ƒ bind() / call: ƒ call()
    • 含义: 函数独有的能力。你平时能写 Foo.call()Foo.apply(),就是因为在这个原型层级里找到了这些内置方法。
  • constructor: ƒ Function()
    • 含义: 说明函数 Foo 是由 JavaScript 内置的 Function 构造函数创建出来的。
  • toString: ƒ toString()
    • 含义: 将函数源码转为字符串的方法(如 Foo.toString())。
  • ▼ [[Prototype]]: Object
    • 含义: Function.prototype 本身也是个对象,它的原型同样指向顶级 Object.prototype
  • ▼ [[Scopes]]: Scopes[0]
    • 含义: Function.prototype 的作用域链。

第五块:▼ [[Scopes]]: Scopes[1](Foo 函数的作用域链)

  • 0: Global {window: ...}
    • 含义: 引擎记录的当前函数生效的上下文环境。这里显示它能访问全局的 window 对象。如果是闭包函数,这里还会看到 Closure 作用域。

控制台展开结构对照表

在 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)

// 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