为什么在 JavaScript 中同时执行Object instanceof Function
和Function instanceof Object
返回true
?
我在 Safari WebInspector 中尝试过。
为什么在 JavaScript 中同时执行Object instanceof Function
和Function instanceof Object
返回true
?
我在 Safari WebInspector 中尝试过。
我花了一段时间才弄明白,但这真的值得花时间。首先,让我们看看它是如何instanceof
工作的。
引自MDN,
所述
instanceof
操作者测试对象是否在其原型链具有prototype
一个构造的属性。
[instanceof]
现在,让我们看看instanceof
ECMA 5.1 规范是如何定义的,
生产
RelationalExpression: RelationalExpression instanceof ShiftExpression
评估如下:
- 让
lref
成为评估的结果RelationalExpression
。- 我们
lval
是GetValue(lref)
。- 让
rref
成为评估的结果ShiftExpression
。- 我们
rval
是GetValue(rref)
。- 如果
Type(rval)
不是对象,则抛出TypeError
异常。- 如果
rval
没有[[HasInstance]]
内部方法,则抛出TypeError
异常。- 返回调用with 参数的
[[HasInstance]]
内部方法的结果。rval
lval
首先评估左侧和右侧的表达式 ( GetValue
),然后右侧的结果应该是一个带有[[HasInstance]]
内部方法的对象。不是所有的对象都有[[HasInstance]]
内部方法,但有函数。例如,以下将失败
console.log(Object instanceof {});
# TypeError: Expecting a function in instanceof check, but got #<Object>
[[HasInstance]]
现在,让我们看看[[HasInstance]]
ECMA 5.1 规范中是如何定义的,
假设
F
是一个函数对象。当使用 value 调用的
[[HasInstance]]
内部方法时,执行以下步骤:F
V
- 如果
V
不是对象,则返回false
。- 让
O
是调用具有属性名称的[[Get]]
内部方法的结果。F
"prototype"
- 如果
Type(O)
不是对象,则抛出TypeError
异常。- 重复
- 让
V
成为 的[[Prototype]]
内部属性的值V
。- 如果
V
是null
,则返回false
。- 如果
O
和V
引用同一个对象,则返回true
。
就这么简单。取prototype
的财产F
,并将其与比较[[Prototype]]
的内部属性O
,直到它变成null
或prototype
的F
是一样的O
。
[[prototype]]
内部财产首先让我们看看什么是[[prototype]]
内部属性,
所有对象都有一个名为 的内部属性
[[Prototype]]
。该属性的值是或者是null
一个对象,用于实现继承。本机对象是否可以拥有宿主对象[[Prototype]]
取决于实现。每条[[Prototype]]
链都必须有有限的长度(即从任何对象开始,递归访问[[Prototype]]
内部属性最终必须得到一个null
值)。
注意:我们可以通过Object.getPrototypeOf
函数获取这个内部属性。
prototype
财产[[HasInstance]]
还讨论了另一个称为 的属性prototype
,它是特定于Function
对象的。
在将 Function 对象作为新创建对象的构造函数调用之前,该
prototype
属性的值用于初始化[[Prototype]]
新创建对象的内部属性。
这意味着,当函数对象用作构造函数时,将创建一个新对象,并且新对象的内部[[Prototype]]
将使用此prototype
属性进行初始化。例如,
function Test() {}
Test.prototype.print = console.log;
console.log(Object.getPrototypeOf(new Test()) === Test.prototype);
# true
现在让我们回到实际的问题。让我们看第一种情况
console.log(Object instanceof Function);
# true
它将Function.prototype
首先获取并尝试查找该对象是否在 的原型层次结构中Object
。让我们看看结果如何
console.log(Function.prototype);
# [Function: Empty]
console.log(Object.getPrototypeOf(Object));
# [Function: Empty]
console.log(Object.getPrototypeOf(Object) === Function.prototype);
# true
由于Function.prototype
匹配Object
的内部属性[[Prototype]]
,它返回true
。
现在让我们来看第二种情况
console.log(Function instanceof Object);
# true
console.log(Object.prototype);
# {}
console.log(Object.getPrototypeOf(Function));
# [Function: Empty]
console.log(Object.getPrototypeOf(Function) === Object.prototype);
# false
console.log(Object.getPrototypeOf(Object.getPrototypeOf(Function)));
# {}
Object.getPrototypeOf(Object.getPrototypeOf(Function)) === Object.prototype
# true
在这里,首先我们得到Object.prototype
,即{}
。现在它正在尝试查找的原型链中{}
是否存在相同的对象Function
。Function
is 和 Empty 函数的直接父级。
console.log(Object.getPrototypeOf(Function));
# [Function: Empty]
它与 Object.prototype
console.log(Object.getPrototypeOf(Function) === Object.prototype);
# false
但[[HasInstance]]
算法并不止于此。它重复并上升一级
console.log(Object.getPrototypeOf(Object.getPrototypeOf(Function)));
# {}
这与Object.prototype
. 这就是为什么返回true
.
来自MDN:
instanceof 运算符测试对象的原型链中是否具有构造函数的原型属性。
本质上,它正在检查Object
(不是 的实例Object
,而是构造函数本身)是否具有作为Function.constructor
其原型链上某处的实例。
而且,确实:
> Function.__proto__.__proto__ === Object.prototype
true
> Object.__proto__ === Function.prototype
true
这解释了为什么Object instanceof Function
以及相反的原因。
所有对象都有一个称为 [[Prototype]] 的内部属性。该属性的值为空或对象,用于实现继承。如果您尝试查找对象上的键但未找到,JavaScript 将在原型链中查找它。
Function 构造函数创建新的 Function 对象(Function 构造函数的实例)。原型属性特定于 Function 对象。Function 构造函数本身就是一个 Function 对象(Function 构造函数的实例)。
当 Function 对象用作构造函数时,将创建一个新对象,并且新对象的 [[Prototype]] 将使用构造函数的prototype 属性进行初始化。
function Dog () {}
var myCrazyDog = new Dog();
myCrazyDog.__proto__ === Dog.prototype // true
语言规范是所有对象都是 Object 构造函数的实例,所有函数都是 Function 构造函数的实例。
Object instanceof Function 为真,因为 Object 是一个函数,因此是 Function 的一个实例(Object 是一个 Function 对象 - Function 构造函数的一个实例)。对象继承自 Function.prototype。
console.log(Object instanceof Function) // true
console.log(Object.__proto__ === Function.prototype) // true
Object instanceof Object 为真,因为 Object 继承自 Function.prototype。由于 Function.prototype 是一个对象,它继承自 Object.prototype。 Object 的函数实例为真,因为Function 继承自Function.prototype。由于 Function.prototype 是一个对象,它继承自 Object.prototype。原型链如下所示:
Object ---> Function.prototype ---> Object.prototype ---> null
Function ---> Function.prototype ---> Object.prototype ---> null
console.log(Object instanceof Object) // true
console.log(Object.__proto__ === Function.prototype) // true
console.log(Object.__proto__.__proto__ === Object.prototype) // true
console.log(Function instanceof Object) // true
console.log(Function.__proto__ === Function.prototype) // true
console.log(Function.__proto__.__proto__ === Object.prototype) // true
函数 instanceof 函数为 true。Function 是它自身的一个实例(自然,因为它是一个函数,因此也是 Function 的一个实例)。原型链如下所示:
Function ---> Function.prototype ---> Object.prototype ---> null
console.log(Function instanceof Function) // true
console.log(Function.__proto__ === Function.prototype) // true
console.log(Function.__proto__.__proto__ === Object.prototype) // true
因此请记住,Function() 和 Object() 构造函数是函数。由于它们是函数,因此它们是 Function() 构造函数的实例并继承自 Function.prototype。由于Function.prototype是一个对象,Function.prototype是Object的一个实例,因此继承自Object.prototype。
console.log(Object instance of Function) // true
console.log(Function instance of Function) // true
console.log(Function.prototype instanceof Object); // true
您问题中混淆的根源在于 JavaScript (ECMAScript) 中函数* 的固有双重性质。
js 中的函数既是正则函数又是对象。将它们视为算法博士 Jekyll 和 Mr. Hyde。它们在外面看起来像对象,但在内部,它们只是具有所有怪癖的旧 js 函数,或者可能正好相反!
JavaScript 真的是一件棘手的事情 :)
所以回到你的问题,借用出现在 MDN 上的语法:
object instanceof constructor
将其应用于代码中的第一条语句:
Object instanceof Function
这里有Object
一个构造函数,它用作对象初始值设定项,但由于函数在 js 中具有双重生命,因此它附加了特定于对象的 props 和方法,从而也有效地将其渲染为对象。
因此,语句中的第一个条件已满足。我们将继续调查其他条件或操作数。
Function
正如您可能已经注意到的,也是函数构造函数,但在执行此特定语句期间,我们现在对它的另一个对象方不感兴趣。
因此,句法条件都满足,即“对象”和“构造函数”。我们现在可以继续调查他们的遗传关系,以及他们之间是否存在联系。
由于Object
它本身是一个工作函数,因此假设它的内部原型 prop 指向Function.prototype
对象引用是很有意义的,因为在 js 中,所有函数都通过同一位置继承它们的 props 和方法Function.prototype
。
true
绝对是操作员执行此比较的唯一预期结果instanceof
。
对于另一种情况:
Function instanceof Object
因为我们已经确定 js 中的函数也有对象的一面。他们从 中获得他们奇特的特定于对象的玩具是有道理的Object.prototype
,因此它们构成 了 Object 构造函数的实例。
希望我的解释和寓言不会增加混乱。:)
*:不仅仅是在js中导致双重生活的函数。js 中的几乎所有数据类型都有一个对象的黑暗面,它们有助于轻松完成操作和操作。
最糟糕的属性实际上是 Function 是它自己的一个实例。Function instanceof Function
返回真。
在 Kannan 的The Surprisingly Elegant Javascript Type Model 中有很好的解释,在http://web.archive.org/web/20140205182624/http://vijayan.ca/blog/2012/02/21/javascript-type-model
在解释的末尾引用:
是的,这意味着 Function 是它自己的一个实例(自然,因为它是一个函数,因此是 Function 的一个实例)。这是我们一直在处理的事情,无论有意与否,很长一段时间以来 - 所有构造函数都是常规函数,因此是 Function 的实例,而 Function 本身只是用于构造其他函数的构造函数,所以它也是函数的一个实例。