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。
- 含义: 传统 JS 中用来获取“传入参数”和“调用当前函数的父级函数”的旧属性。在现代 ES6 严格模式下已被禁用,所以显示为
[[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 板块 | prototype | sayHello, constructor: ƒ Foo(), [[Prototype]]: Object | new 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