深入剖析JavaScript instanceof 運算符
instanceof 運算符簡介
在 JavaScript 中,判斷一個變量的類型嘗嘗會用 typeof 運算符,在使用 typeof 運算符時采用引用類型存儲值會出現(xiàn)一個問題,無論引用的是什么類型的對象,它都返回 "object"。ECMAScript 引入了另一個 Java 運算符 instanceof 來解決這個問題。instanceof 運算符與 typeof 運算符相似,用于識別正在處理的對象的類型。與 typeof 方法不同的是,instanceof 方法要求開發(fā)者明確地確認對象為某特定類型。例如:
清單 1. instanceof 示例
var oStringObject = new String("hello world"); console.log(oStringObject instanceof String); // 輸出 "true"
這段代碼問的是“變量 oStringObject 是否為 String 對象的實例?”oStringObject 的確是 String 對象的實例,因此結(jié)果是"true"。盡管不像 typeof 方法那樣靈活,但是在 typeof 方法返回 "object" 的情況下,instanceof 方法還是很有用的。
instanceof 運算符的常規(guī)用法
通常來講,使用 instanceof 就是判斷一個實例是否屬于某種類型。例如:
清單 2. instanceof 常規(guī)用法
// 判斷 foo 是否是 Foo 類的實例 function Foo(){} var foo = new Foo(); console.log(foo instanceof Foo)//true
另外,更重的一點是 instanceof 可以在繼承關(guān)系中用來判斷一個實例是否屬于它的父類型。例如:
清單 3. instanceof 在繼承中關(guān)系中的用法
// 判斷 foo 是否是 Foo 類的實例 , 并且是否是其父類型的實例 function Aoo(){} function Foo(){} Foo.prototype = new Aoo();//JavaScript 原型繼承 var foo = new Foo(); console.log(foo instanceof Foo)//true console.log(foo instanceof Aoo)//true
上面的代碼中是判斷了一層繼承關(guān)系中的父類,在多層繼承關(guān)系中,instanceof 運算符同樣適用。
你真的了解 instanceof 操作符嗎?
看了上面的代碼示例,是不是覺得 instanceof 操作符很簡單,下面來看點復(fù)雜的用法。
清單 4. instanceof 復(fù)雜用法
console.log(Object instanceof Object);//true console.log(Function instanceof Function);//true console.log(Number instanceof Number);//false console.log(String instanceof String);//false console.log(Function instanceof Object);//true console.log(Foo instanceof Function);//true console.log(Foo instanceof Foo);//false
看了上面的代碼是不是又暈頭轉(zhuǎn)向了?為什么 Object 和 Function instanceof 自己等于 true,而其他類 instanceof 自己卻又不等于 true 呢?如何解釋?要想從根本上了解 instanceof 的奧秘,需要從兩個方面著手:1,語言規(guī)范中是如何定義這個運算符的。2,JavaScript 原型繼承機制。
詳細剖析 ECMAScript-262 edition 3 中 instanceof 運算符的定義
語言規(guī)范對中 instanceof 運算符的定義如下:
清單 5. 規(guī)范中 instanceof 運算符定義
11.8.6 The instanceof operator The production RelationalExpression: RelationalExpression instanceof ShiftExpression is evaluated as follows: 1. Evaluate RelationalExpression. 2. Call GetValue(Result(1)).// 調(diào)用 GetValue 方法得到 Result(1) 的值,設(shè)為 Result(2) 3. Evaluate ShiftExpression. 4. Call GetValue(Result(3)).// 同理,這里設(shè)為 Result(4) 5. If Result(4) is not an object, throw a TypeError exception.// 如果 Result(4) 不是 object, //拋出異常 /* 如果 Result(4) 沒有 [[HasInstance]] 方法,拋出異常。規(guī)范中的所有 [[...]] 方法或者屬性都是內(nèi)部的, 在 JavaScript 中不能直接使用。并且規(guī)范中說明,只有 Function 對象實現(xiàn)了 [[HasInstance]] 方法。 所以這里可以簡單的理解為:如果 Result(4) 不是 Function 對象,拋出異常 */ 6. If Result(4) does not have a [[HasInstance]] method, throw a TypeError exception. // 相當于這樣調(diào)用:Result(4).[[HasInstance]](Result(2)) 7. Call the [[HasInstance]] method of Result(4) with parameter Result(2). 8. Return Result(7). // 相關(guān)的 HasInstance 方法定義 15.3.5.3 [[HasInstance]] (V) Assume F is a Function object.// 這里 F 就是上面的 Result(4),V 是 Result(2) When the [[HasInstance]] method of F is called with value V, the following steps are taken: 1. If V is not an object, return false.// 如果 V 不是 object,直接返回 false 2. Call the [[Get]] method of F with property name "prototype".// 用 [[Get]] 方法取 // F 的 prototype 屬性 3. Let O be Result(2).//O = F.[[Get]]("prototype") 4. If O is not an object, throw a TypeError exception. 5. Let V be the value of the [[Prototype]] property of V.//V = V.[[Prototype]] 6. If V is null, return false. // 這里是關(guān)鍵,如果 O 和 V 引用的是同一個對象,則返回 true;否則,到 Step 8 返回 Step 5 繼續(xù)循環(huán) 7. If O and V refer to the same object or if they refer to objects joined to each other (section 13.1.2), return true. 8. Go to step 5.
上面的規(guī)范定義很晦澀,而且看起來比較復(fù)雜,涉及到很多概念,但把這段規(guī)范翻譯成 JavaScript 代碼卻很簡單,如下:
清單 6. JavaScript instanceof 運算符代碼
function instance_of(L, R) {//L 表示左表達式,R 表示右表達式 var O = R.prototype;// 取 R 的顯示原型 L = L.__proto__;// 取 L 的隱式原型 while (true) { if (L === null) return false; if (O === L)// 這里重點:當 O 嚴格等于 L 時,返回 true return true; L = L.__proto__; } }
JavaScript 原型繼承機制
由于本文主要集中在剖析 JavaScript instanceof 運算符,所以對于 JavaScript 的原型繼承機制不再做詳細的講解,下面參考來自 http://www.mollypages.org/misc/js.mp 的一張圖片,此圖片詳細的描述了 JavaScript 各種對象的顯示和隱式原型鏈結(jié)構(gòu)。
由其本文涉及顯示原型和隱式原型,所以下面對這兩個概念作一下簡單說明。在 JavaScript 原型繼承結(jié)構(gòu)里面,規(guī)范中用 [[Prototype]] 表示對象隱式的原型,在 JavaScript 中用 __proto__ 表示,并且在 Firefox 和 Chrome 瀏覽器中是可以訪問得到這個屬性的,但是 IE 下不行。
所有 JavaScript 對象都有 __proto__ 屬性,但只有 Object.prototype.__proto__ 為 null,前提是沒有在 Firefox 或者 Chrome 下修改過這個屬性。這個屬性指向它的原型對象。 至于顯示的原型,在 JavaScript 里用 prototype 屬性表示,這個是 JavaScript 原型繼承的基礎(chǔ)知識,在這里就不在敘述了。
JavaScript 原型鏈
講解 instanceof 復(fù)雜用法
有了上面 instanceof 運算符的 JavaScript 代碼和原型繼承圖,再來理解 instanceof 運算符將易如反掌。下面將詳細講解 Object instanceof Object,F(xiàn)unction instanceof Function 和 Foo instanceof Foo 三個示例,其它示例讀者可自行推演。
清單 7. Object instanceof Object
// 為了方便表述,首先區(qū)分左側(cè)表達式和右側(cè)表達式 ObjectL = Object, ObjectR = Object; // 下面根據(jù)規(guī)范逐步推演 O = ObjectR.prototype = Object.prototype L = ObjectL.__proto__ = Function.prototype // 第一次判斷 O != L // 循環(huán)查找 L 是否還有 __proto__ L = Function.prototype.__proto__ = Object.prototype // 第二次判斷 O == L // 返回 true
清單 8. Function instanceof Function
// 為了方便表述,首先區(qū)分左側(cè)表達式和右側(cè)表達式 FunctionL = Function, FunctionR = Function; // 下面根據(jù)規(guī)范逐步推演 O = FunctionR.prototype = Function.prototype L = FunctionL.__proto__ = Function.prototype // 第一次判斷 O == L // 返回 true
清單 9. Foo instanceof Foo
// 為了方便表述,首先區(qū)分左側(cè)表達式和右側(cè)表達式 FooL = Foo, FooR = Foo; // 下面根據(jù)規(guī)范逐步推演 O = FooR.prototype = Foo.prototype L = FooL.__proto__ = Function.prototype // 第一次判斷 O != L // 循環(huán)再次查找 L 是否還有 __proto__ L = Function.prototype.__proto__ = Object.prototype // 第二次判斷 O != L // 再次循環(huán)查找 L 是否還有 __proto__ L = Object.prototype.__proto__ = null // 第三次判斷 L == null // 返回 false
簡析 instanceof 在 Dojo 繼承機制中的應(yīng)用
在 JavaScript 中,是沒有多重繼承這個概念的,就像 Java 一樣。但在 Dojo 中使用 declare 聲明類時,是允許繼承自多個類的。下面以 Dojo 1.6.1 為例。
清單 10. Dojo 中多重繼承
dojo.declare("Aoo",null,{}); dojo.declare("Boo",null,{}); dojo.declare("Foo",[Aoo,Boo],{}); var foo = new Foo(); console.log(foo instanceof Aoo);//true console.log(foo instanceof Boo);//false console.log(foo.isInstanceOf(Aoo));//true console.log(foo.isInstanceOf(Boo));//true
上面的示例中,F(xiàn)oo 同時繼承自 Aoo 和 Boo,但當使用 instanceof 運算符來檢查 foo 是否是 Boo 的實例時,返回的是 false。實際上,在 Dojo 的內(nèi)部,F(xiàn)oo 仍然只繼承自 Aoo,而通過 mixin 機制把 Boo 類中的方法和屬性拷貝到 Foo 中,所以當用 instanceof 運算符來檢查是否是 Boo 的實例時,會返回 false。所以 Dojo 為每個類的實例添加了一個新的方法叫 isInstanceOf,用這個方法來檢查多重繼承。
結(jié)束語
本文詳細介紹了 JavaScript 語言中 instanceof 運算符,并且結(jié)合語言規(guī)范深入剖析了此操作符的算法。對讀者使用 JavaScript 編寫復(fù)雜的面向?qū)ο蟪绦驎泻艽蟮膸椭?。本文所有代碼在 Firefox 15 下通過測試。
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
js中string轉(zhuǎn)int把String類型轉(zhuǎn)化成int類型
今天碰到一個問題,需要把String類型的變量轉(zhuǎn)化成int類型的,js中String轉(zhuǎn)int和Java中不一樣,不能直接把Java中的用到j(luò)s中2014-08-08javascript tips提示框組件實現(xiàn)代碼
一個簡單的類似title的提示效果,但現(xiàn)實內(nèi)容可以很豐富,以上js另存為tip.js,下面是使用的demo。2010-11-11JavaScript針對網(wǎng)頁節(jié)點的增刪改查用法實例
這篇文章主要介紹了JavaScript針對網(wǎng)頁節(jié)點的增刪改查用法,實例分析了JavaScript操作網(wǎng)頁節(jié)點的技巧,非常具有實用價值,需要的朋友可以參考下2015-02-02javascript prototype的深度探索不是原型繼承那么簡單
JavaScript中對象的prototype屬性,可以返回對象類型原型的引用。這是一個相當拗口的解釋,要理解它,先要正確理解對象類型(Type)以及原型(prototype)的概念。2008-06-06BootStrap創(chuàng)建響應(yīng)式導航條實例代碼
這篇文章主要介紹了BootStrap創(chuàng)建響應(yīng)式導航條實例代碼的相關(guān)資料,需要的朋友可以參考下2016-05-05如何利用Web Speech API之speechSynthesis實現(xiàn)文字轉(zhuǎn)語音功能
Web Speech API使你能夠?qū)⒄Z音數(shù)據(jù)合并到Web應(yīng)用程序中,SpeechSynthesisUtterance是HTML5中新增的API,用于將指定文字合成為對應(yīng)的語音,這篇文章主要介紹了利用Web Speech API之speechSynthesis實現(xiàn)文字轉(zhuǎn)語音功能,需要的朋友可以參考下2024-06-06