当前位置:网站首页>The instanceof operator in ecmascript7 specification

The instanceof operator in ecmascript7 specification

2020-11-07 20:55:49 luckness

This article mainly explains ECMAScript7 Normative instanceof The operator .

Preliminary knowledge

Famous Symbols

“ famous ” Of Symbols Refers to built-in symbols , They are defined in Symbol On the object .ECMAScript7 Used in @@name These built-in symbols are referenced in the form of , For example, the following will be mentioned @@hasInstance, In fact, that is Symbol.hasInstance.

InstanceofOperator(O, C)

O instanceof C Called internally InstanceofOperator(O, C) Abstract operations , The steps of the abstract operation are as follows :

  1. If C Is not an object , Throw an exception of the wrong type ;
  2. Give Way instOfHandler be equal to GetMethod(C, @@hasInstance), Maybe semantics is to get objects C Of @@hasInstance The value of the property ;
  3. If instOfHandler The value is not undefined, that :

    1. return ToBoolean(? Call(instOfHandler, C, « O »)) Result , Maybe semantics is execution instOfHandler(O), Then the call result is forced to be converted to boolean type and returned .
  4. If C Cannot be called , Throw an exception of the wrong type ;
  5. return OrdinaryHasInstance(C, O) Result .

OrdinaryHasInstance(C, O)

OrdinaryHasInstance(C, O) The steps of abstract operation are as follows :

  1. If C Cannot be called , return false;
  2. If C There are internal slots [[BoundTargetFunction]], that :

    1. Give Way BC be equal to C The internal slot of [[BoundTargetFunction]] Value ;
    2. return InstanceofOperator(O, BC) Result ;
  3. If O The type of is not an object , return false;
  4. Give Way P be equal to Get(C, "prototype"), Probably semantics is to get C.prototype Value ;
  5. If P Is not an object , Throw an exception of the wrong type ;
  6. Repeat the following steps :

    1. Give Way O be equal to O.[[GetPrototypeOf]]() Result , Maybe semantics is to get O Prototype object ;
    2. If O be equal to null, return false;
    3. If SameValue(P, O) The result is true, return true.

SameValue For abstract operations, see JavaScript Medium ==,=== and Object.js() Medium Object.is(),Object.is() This is the result of this abstract operation .

From the above steps 2 You know , If C It's a bind function , Then it will be back in C Bound to the target function InstanceofOperator(O, BC) operation .

From the above steps 6 You know , Will repeatedly get the object O Prototype object , Then compare the prototype object with C Of prototype Whether the attributes are equal , Until equal returns true, perhaps O Turn into null, That is, traversing the entire prototype chain , return false.

Function.prototype[@@hasInstance] (V)

By the above InstanceofOperator(O, C) Steps for abstract operations 2 and 3 You can know , If C It defines or inherits from @@ hasInstance Attribute words , The value of the property is called , And not to step up to the steps 4 and 5. step 4 and 5 The aim is to be compatible without implementation @@hasInstance Method browser . If a function has no definition or inheritance @@hasInstance attribute , Then the default will be used instanceof The semantics of the , That is to say OrdinaryHasInstance(C, O) The steps described in the abstract operation .

ECMAScript7 Specification , stay Function Of prototype Property defined on @@hasInstance attribute .Function.prototype[@@hasInstance](V) The steps are as follows :

  1. Give Way F be equal to this value ;
  2. return OrdinaryHasInstance(F, V) Result .

therefore , You can see that by default ,instanceof The semantics of are the same , All return to OrdinaryHasInstance(F, V) Result . Why say by default ? Because you can cover Function.prototype[@@hasInstance] Method , To customize instanceof act .

Example

function A () {}
function B () {}

var a = new A
a.__proto__ === A.prototype // true
a.__proto__.__proto__ === Object.prototype // true
a.__proto__.__proto__.__proto__ === null // true

a instanceof A // true
a instanceof B // false

from OrdinaryHasInstance(C, O) Of the 6 Step by step :

  • about a instanceof A,P yes A.prototype, In the first cycle ,a Prototype object a._proto__ yes A.prototype, That is, in the steps O yes A.prototype, So it goes back true;
  • about a instanceof B,P yes B.prototype, In the first cycle ,a Prototype object a._proto__ yes A.prototype, It's not equal to P; Perform the second cycle , here O yes a.__proto__.__proto__, That is to say Object.prototype, It's not equal to P; Perform the third cycle , here O yes a.__proto__.__proto__.__proto__, That is to say null, That is, the prototype chain has been traversed , So it goes back false.

Next, let's take the example above :

A.prototype.__proto__ = B.prototype

a.__proto__ === A.prototype // true
a.__proto__.__proto__ === B.prototype // true
a.__proto__.__proto__.__proto__ === Object.prototype // true
a.__proto__.__proto__.__proto__.__proto__ === null // true

a instanceof B // true

In the example above , We put B.prototype Set up a a A link in the prototype chain of , such a instanceof B stay OrdinaryHasInstance(C, O) Of the 6 Step one 2 In the second cycle , Back to true.

from OrdinaryHasInstance(C, O) Of the 2 Step , We know bind The behavior of a function is different from that of a normal function :

function A () {}
var B = A.bind()

B.prototype === undefined // true

var b = new B
b instanceof B // true
b instanceof A // true

From the above example ,B.prototype yes undefined. therefore ,instanceof Act on bind The return result of a function is actually the return value of the binding target function , and bind Functions basically have nothing to do with .

from InstanceofOperator(O, C) step 2 And steps 3 You know , We can go through @@hasInstance Attributes come from definitions instanceof act :

function A () {}
var a = new A
a instanceof A // true

A[Symbol.hasInstance] = function () { return false }
a instanceof A // ?

stay chrome The browser tested , Discover or output true. Then I took a look at ECMAScript6 Documents ,ECMAScript6 There is no provision in the document that can be passed @@hasInstance change instanceof act , So it should be the present chrome The browser hasn't been implemented yet ECMAScript7 Medium instanceof The behavior of the operator .

Until one day I saw MDN On Symbol.hasInstance Compatibility section of , Find out chrome from 51 The version starts to support Symbol.hasInstance 了 :

class MyArray {  
  static [Symbol.hasInstance](instance) {
    return Array.isArray(instance)
  }
}
console.log([] instanceof MyArray) // true

So why can't I write like that ? Until I found out :

function A () {}
var fun = function () {return false}
A[Symbol.hasInstance] = fun
A[Symbol.hasInstance] === fun // false
A[Symbol.hasInstance] === Function.prototype[Symbol.hasInstance] // true
A[Symbol.hasInstance] === A.__proto__[Symbol.hasInstance] // true

You can tell by the code above ,A[Symbol.hasInstance] No assignment succeeded , And always equal to Function.prototype[Symbol.hasInstance], That is always equal to A On the prototype of Symbol.hasInstance Method . Is that because of the same name method on the prototype ?

Object.getOwnPropertyDescriptor(Function.prototype, Symbol.hasInstance)
// Object {writable: false, enumerable: false, configurable: false, value: function}

You can tell by the code above ,Function.prototype Upper Symbol.hasInstance Of the property descriptor of writable yes false, That is, this property is read-only , So in A Add above Symbol.hasInstance Property failed . But why is there no hint of failure ?

'use strict'
function A () {}
var fun = function () {return false}
A[Symbol.hasInstance] = fun
// Uncaught TypeError: Cannot assign to read only property 'Symbol(Symbol.hasInstance)' of function 'function A() {}'

The error is coming out , So try to use strict mode in the future . Some operations in non strict mode will Silence fails , That is, even if the operation fails, there will be no prompt , Causes the developer to think that the operation was successful .

var a = {}
a[Symbol.hasInstance] = function () {return true}
new Number(3) instanceof a // true

Because you can customize Symbol.hasInstance Method to override the default behavior , So use instanceof Operators are not necessarily reliable in judging data types .

There's another problem : Why is it up there MDN Examples of documents can be successful , My first example didn't work , The purpose is not to write a constructor , Then add an attribute to the constructor ?
The result of personal analysis is : Although everyone said Class Is a syntax sugar for writing constructors , But it's still used function There are differences in the way , Like the example above . Use Class When , Will directly add a static property to the constructor , It does not first check whether there is an attribute with the same name on the prototype chain . While using function The way when , Add a static method to the constructor , It is equivalent to assigning a value to an object , The assignment operation will first check whether there is an attribute with the same name on the prototype chain , So there is a risk of assignment failure . therefore , Just add... To the constructor Symbol.hasInstance In terms of attributes ,Class Can do , Use Function The way it can't be done .

Updated on 2018/11/20
It concludes that

therefore , Just add... To the constructor Symbol.hasInstance In terms of attributes , Class Can do , Use Function The way it can't be done .

however , It turns out that adding properties to an object is not just a way of assigning values , One more Object.defineProperty Method :

function A () {}
var a = new A
a instanceof A // true

Object.defineProperty(A, Symbol.hasInstance, {
    value: function () { return false }
})
a instanceof A // false

summary

This article mainly explains ECMAScript7 Normative instanceof The operator , I hope you can get something . If there is any mistake or laxity in this article , Feel free to leave a comment in the comments section .

版权声明
本文为[luckness]所创,转载请带上原文链接,感谢