🗒️函数对象和构造器对象
这篇不是原创,是整理的笔记
在 JavaScript 中,可以用对象来模拟函数和构造器。JavaScript 为这类对象预留了私有字段机制,并规定了抽象的函数对象和构造器对象的概念。
函数对象:具有私有字段
[[call]]
的对象构造器对象:具有私有字段
[[construct]]
的对象
JavaScript 里的函数是用对象来模拟的,它们和一般编程语言中的函数一样,可以被调用和传参。任何宿主只要提供了“具有私有字段 [[call]]
的对象”,就可以被 JavaScript 的函数调用语法支持。私有字段 [[call]]
必须是一个引擎中定义的函数,需要接受 this 值和参数,并且会产生域(realm)的切换。
我们可以说,任何对象只要实现了私有字段 [[call]]
它就是一个函数对象了——可以作为函数被调用,只要实现了私有字段 [[construct]]
它就是一个构造器对象了——可以作为构造器被调用。只要字段符合,我们就可以用 JavaScript 里的固有对象来模拟函数和构造器了。
1. 不总是一致
然而,JavaScript 固有对象实现 [[call]]
和 [[construct]]
的方式并不总是一致的。比如:
浏览器宿主环境提供的 Image 对象,只支持构造器调用,不支持函数调用
基本类型(String, Number, Boolean)的构造器在被当作函数调用时会执行类型转换,而不返回对象
用 ES6
=>
语法创建的函数仅仅是函数,而不能作为构造器来使用
2. 函数
对于使用 function
语法或 Function
构造器创建的对象来说,[[call]]
和 [[construct]]
的行为总是相似的,因为它们会执行同一段代码。
但是 [[construct]]
会比 [[call]]
多干几件事情:
首先,以 Object.prototype 为原型创建一个新对象 A
然后,以新对象 A 为 this 来执行函数的私有字段
[[call]]
如果
[[call]]
的返回值是对象 B,那么就返回该对象 B,否则就返回第 1 步中创建的对象 A
如果 [[construct]]
最终返回的是对象 B,那么对象 A 就成了在构造函数之外完全没法被访问的对象,这在一定程度上可以实现私有。示例代码如下:
3. 特殊行为
和普通对象的行为相比,有一些对象的行为比较特殊。包括但不限于:
Object.prototype:作为所有普通对象的默认原型,不能再给它设置原型了
Array:length 属性既不是数据属性也不是访问器属性,它还可以根据数组的最大下标自动发生变化
String:为了支持下标运算,String 的正整数属性访问会去查字符串,也不算一般意义上的属性
Arguments:非负整数型的下标属性,会和对应的形参变量联动(同时修改)
类型数组和数组 buffer:和内存块相关联,下标运算比较特殊
bind 后的 function:和原函数相关联
模块的 namespace 对象:特殊的地方很多,和普通对象完全不一样。尽量只用它来 import
4. 写在最后
4.1 附代码
找固有对象和宿主对象的代码:
执行结果如下:
4.2 主要参考
Last updated