成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

ECMAScript7規(guī)范中的instanceof操作符

zhangwang / 2293人閱讀

摘要:本文主要講解規(guī)范中的操作符。由上述步驟可知,如果是一個函數(shù),那么會重新在綁定的目標函數(shù)上執(zhí)行操作。而使用的方式的時候,給構(gòu)造函數(shù)添加一個靜態(tài)方法,相當(dāng)于給對象賦值,賦值操作會先檢查原型鏈上是否存在同名屬性,所以就會有賦值失敗的風(fēng)險。

本文主要講解ECMAScript7規(guī)范中的instanceof操作符。

預(yù)備知識 有名的Symbols

“有名”的Symbols指的是內(nèi)置的符號,它們定義在Symbol對象上。ECMAScript7中使用了@@name的形式引用這些內(nèi)置的符號,比如下面會提到的@@hasInstance,其實就是Symbol.hasInstance。

InstanceofOperator(O, C)

O instanceof C在內(nèi)部會調(diào)用InstanceofOperator(O, C)抽象操作,該抽象操作的步驟如下:

如果C的數(shù)據(jù)類型不是對象,拋出一個類型錯誤的異常;

instOfHandler等于GetMethod(C, @@hasInstance),大概語義就是獲取對象C@@hasInstance屬性的值;

如果instOfHandler的值不是undefined,那么:

返回ToBoolean(? Call(instOfHandler, C, ? O ?))的結(jié)果,大概語義就是執(zhí)行instOfHandler(O),然后把調(diào)用結(jié)果強制轉(zhuǎn)化為布爾類型返回。

如果C不能被調(diào)用,拋出一個類型錯誤的異常;

返回OrdinaryHasInstance(C, O)的結(jié)果。

OrdinaryHasInstance(C, O)

OrdinaryHasInstance(C, O)抽象操作的步驟如下:

如果C不能被調(diào)用,返回false;

如果C有內(nèi)部插槽[[BoundTargetFunction]],那么:

BC等于C的內(nèi)部插槽[[BoundTargetFunction]]的值;

返回InstanceofOperator(O, BC)的結(jié)果;

如果O的類型不是對象,返回false

P等于Get(C, "prototype"),大概語義是獲取C.prototype的值;

如果P的數(shù)據(jù)類型不是對象,拋出一個類型錯誤的異常;

重復(fù)執(zhí)行下述步驟:

O等于O.[[GetPrototypeOf]]()的結(jié)果,大概語義就是獲取O的原型對象;

如果O等于null,返回false;

如果SameValue(P, O)的結(jié)果是true,返回true。

SameValue抽象操作參見JavaScript中的==,===和Object.js()中的Object.is(),Object.is()使用的就是這個抽象操作的結(jié)果。

由上述步驟2可知,如果C是一個bind函數(shù),那么會重新在C綁定的目標函數(shù)上執(zhí)行InstanceofOperator(O, BC)操作。

由上述步驟6可知,會重復(fù)地獲取對象O的原型對象,然后比較該原型對象和Cprototype屬性是否相等,直到相等返回true,或者O變?yōu)?b>null,也就是遍歷完整個原型鏈,返回false。

Function.prototype[@@hasInstance](V)

由上面的InstanceofOperator(O, C)抽象操作的步驟23可以知道,如果C上面定義或繼承了@@ hasInstance屬性的話,會調(diào)用該屬性的值,而不會走到步驟45。步驟45的目的是為了兼容沒有實現(xiàn)@@hasInstance方法的瀏覽器。如果一個函數(shù)沒有定義或繼承@@hasInstance屬性,那么就會使用默認的instanceof的語義,也就是OrdinaryHasInstance(C, O)抽象操作描述的步驟。

ECMAScript7規(guī)范中,在Functionprototype屬性上定義了@@hasInstance屬性。Function.prototype[@@hasInstance](V)的步驟如下:

F等于this值;

返回OrdinaryHasInstance(F, V)的結(jié)果。

所以,你可以看到在默認情況下,instanceof的語義是一樣的,都是返回OrdinaryHasInstance(F, V)的結(jié)果。為什么說默認情況下?因為你可以覆蓋Function.prototype[@@hasInstance]方法,去自定義instanceof的行為。

例子
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

OrdinaryHasInstance(C, O)的第6步可知:

對于a instanceof APA.prototype,在第一次循環(huán)的時候,a的原型對象a._proto__A.prototype,也就是步驟中的OA.prototype,所以返回了true;

對于a instanceof BPB.prototype,在第一次循環(huán)的時候,a的原型對象a._proto__A.prototype,不等于P;執(zhí)行第二次循環(huán),此時Oa.__proto__.__proto__,也就是Object.prototype,不等于P;執(zhí)行第三次循環(huán),此時Oa.__proto__.__proto__.__proto__,也就是null,也就是原型鏈都遍歷完了,所以返回了false。

接著上面的例子:

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

在上面的例子中,我們把B.prototype設(shè)置成了a的原型鏈中的一環(huán),這樣a instanceof BOrdinaryHasInstance(C, O)的第6步的第2次循環(huán)的時候,返回了true

OrdinaryHasInstance(C, O)的第2步,我們知道bind函數(shù)的行為和普通函數(shù)的行為是不一樣的:

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

B.prototype === undefined // true

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

由上面的例子可知,B.prototypeundefined。所以,instanceof作用于bind函數(shù)的返回結(jié)果其實是作用于綁定的目標函數(shù)的返回值,和bind函數(shù)基本上沒有什么關(guān)系。

InstanceofOperator(O, C)步驟2和步驟3可知,我們可以通過@@hasInstance屬性來自定義instanceof的行為:

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

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

chrome瀏覽器測試了一下,發(fā)現(xiàn)還是輸出true。然后看了一下ECMAScript6的文檔,ECMAScript6文檔里面還沒有規(guī)定可以通過@@hasInstance改變instanceof的行為,所以應(yīng)該是目前chrome瀏覽器還沒有實現(xiàn)ECMAScript7中的instanceof操作符的行為。

直到有一天看了MDN上Symbol.hasInstance的兼容性部分,發(fā)現(xiàn)chrome51版本就開始支持Symbol.hasInstance了:

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

那么為什么我那樣寫不行呢?直到我發(fā)現(xiàn):

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

由上面的代碼可知,A[Symbol.hasInstance]并沒有賦值成功,而且始終等于Function.prototype[Symbol.hasInstance],也就是始終等于A的原型上的Symbol.hasInstance方法。那是不是因為原型上的同名方法?

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

由上面的代碼可知,Function.prototype上的Symbol.hasInstance的屬性描述符的writablefalse,也就是這個屬性是只讀的,所以在A上面添加Symbol.hasInstance屬性失敗了。但是為啥沒有失敗的提示呢?

"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() {}"

錯誤提示出來了,所以以后還是盡量使用嚴格模式。非嚴格模式下有些操作會靜默失敗,也就是即使操作失敗了也不會有任何提示,導(dǎo)致開發(fā)人員認為操作成功了。

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

因為可以通過自定義Symbol.hasInstance方法來覆蓋默認行為,所以用instanceof操作符判斷數(shù)據(jù)類型并不一定是可靠的。

還有一個問題:為什么上面MDN文檔的例子可以成功,我最初的例子就不行呢,目的不都是寫一個構(gòu)造函數(shù),然后在構(gòu)造函數(shù)上添加一個屬性嗎?
個人分析的結(jié)果是:雖然大家都說Class是寫構(gòu)造函數(shù)的一個語法糖,但是其實還是和使用function的方式有差別的,就比如上面的例子。使用Class的時候,會直接在構(gòu)造函數(shù)上添加一個靜態(tài)屬性,不會先檢查原型鏈上是否存在同名屬性。而使用function的方式的時候,給構(gòu)造函數(shù)添加一個靜態(tài)方法,相當(dāng)于給對象賦值,賦值操作會先檢查原型鏈上是否存在同名屬性,所以就會有賦值失敗的風(fēng)險。所以,就給構(gòu)造函數(shù)添加Symbol.hasInstance屬性來說,Class能做到,使用Function的方式就做不到。

更新于2018/11/20
上面總結(jié)到:

所以,就給構(gòu)造函數(shù)添加Symbol.hasInstance屬性來說,Class能做到,使用Function的方式就做不到。

但是,給對象添加屬性除了直接賦值之外,還可以使用Object.defineProperty方法:

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

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

使用Object.defineProperty方法添加或者修改對象屬性的時候不會檢查原型鏈,所以就可以成功了。所以上面的總結(jié)也就不成立了,也就是:

所以,就給構(gòu)造函數(shù)添加Symbol.hasInstance屬性來說,Class能做到,使用Function的方式也可以做到。
總結(jié)

本文主要講解ECMAScript7規(guī)范中的instanceof操作符,希望大家能有所收獲。如果本文有什么錯誤或者不嚴謹?shù)牡胤?,歡迎在評論區(qū)留言。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/97833.html

相關(guān)文章

  • 如何判斷JavaScript的數(shù)據(jù)類型?

    摘要:本文將講解我目前所知道的判斷數(shù)據(jù)類型的方法。數(shù)據(jù)類型一共有種除了之外的種屬于原始數(shù)據(jù)類型。等價于問題四的返回值是什么答案。 本文將講解我目前所知道的判斷JavaScript數(shù)據(jù)類型的方法。JavaScript數(shù)據(jù)類型一共有7種: Undefined Null Boolean String Symbol Number Object 除了Object之外的6種屬于原始數(shù)據(jù)類型。有時,我...

    jackzou 評論0 收藏0
  • ECMAScript7規(guī)范中的ToPrimitive抽象操作

    摘要:本文將介紹規(guī)范中的抽象操作。它們主要用于規(guī)范的說明,不需要被真正地實現(xiàn)。該抽象操作接受一個參數(shù)和一個可選的參數(shù)。根據(jù)規(guī)范中的加法操作,對于操作,會調(diào)用和把和轉(zhuǎn)化為原始數(shù)據(jù)類型。 本文將介紹ECMAScript7規(guī)范中的ToPrimitive抽象操作。 預(yù)備知識 ECMAScript數(shù)據(jù)類型 ECMAScript數(shù)據(jù)類型細分為兩大類數(shù)據(jù)類型,一種是語言類型,一種是規(guī)范類型: 語言類型...

    張漢慶 評論0 收藏0
  • JavaScript中的==,===和Object.is()

    概述 本文主要講解JavaScript中的三種相等運算:==,===和Object.is()。通過對比和例子,加深大家的印象,并就個別例子進行詳細說明。 預(yù)備知識 ECMAScript7規(guī)范中的ToPrimitive抽象操作 ===運算符 對于x === y,該運算符的比較步驟如下: 如果x的類型和y的類型不一樣,返回false; 如果x的類型是數(shù)字,那么: 如果x是NaN,返回false;...

    hiyang 評論0 收藏0
  • 使用JavaScript隱式類型轉(zhuǎn)換輸出"nb"

    摘要:本文將介紹一段使用隱式類型轉(zhuǎn)換輸出的代碼,并講解具體的轉(zhuǎn)換過程。代碼轉(zhuǎn)換過程我們分四部分講解具體的轉(zhuǎn)換過程,一個空數(shù)組,緊跟在數(shù)組后面的的語義應(yīng)該是表示屬性操作,類似于中的作用,而不是表示數(shù)組。 本文將介紹一段使用JavaScript隱式類型轉(zhuǎn)換輸出nb的代碼,并講解具體的轉(zhuǎn)換過程。 預(yù)備知識 請先閱讀文章ECMAScript7規(guī)范中的ToPrimitive抽象操作。 代碼 ([][[...

    tomlingtm 評論0 收藏0
  • JavaScript instanceof運算符深入分析

    摘要:注意基本變量類型不是對象類型,只有基本包裝類型才是對象類型。至于顯示的原型,在里用屬性表示,這個是原型繼承的基礎(chǔ)知識,在這里就不在敘述了。 前言 如果你要開發(fā)一個復(fù)雜的產(chǎn)品,那么肯定少不了使用面向?qū)ο髾C制,當(dāng)然也避不開 Javascript 里面的繼承,instanceof 運算符是原生 Javascript 語言中用來判斷實例繼承的操作符。所以我們有必要深入理解該運算符! inst...

    zhangyucha0 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<