JavaScript instanceof運(yùn)算符深入分析

前言

如果你要開(kāi)發(fā)一個(gè)復(fù)雜的產(chǎn)品,那么肯定少不了使用面向?qū)ο髾C(jī)制,當(dāng)然也避不開(kāi) Javascript 里面的繼承,instanceof 運(yùn)算符是原生 Javascript 語(yǔ)言中用來(lái)判斷實(shí)例繼承的操作符。所以我們有必要深入理解該運(yùn)算符!

instanceof 運(yùn)算符簡(jiǎn)介

在 JavaScript 中,要判斷某個(gè)變量的類(lèi)型,我們第一個(gè)想起的就是 typeof 運(yùn)算符,但是在使用 typeof 運(yùn)算符的時(shí)候 ,只要是引用類(lèi)型存儲(chǔ)的變量,無(wú)論你引用的是什么類(lèi)型的對(duì)象,統(tǒng)一返回 “object” ,這就是一個(gè)大問(wèn)題,我們?nèi)绾闻袛噙@個(gè)對(duì)象是屬于哪個(gè)類(lèi)型的呢? ECMAScript 引入了在Java 中的instanceof運(yùn)算符來(lái)解決這個(gè)問(wèn)題,instanceof與typeof都可以檢測(cè)數(shù)據(jù)類(lèi)型,instanceof 運(yùn)算符與 typeof 運(yùn)算符的不同點(diǎn)是:要求開(kāi)發(fā)者明確的確認(rèn)檢測(cè)對(duì)象為某特定類(lèi)型的數(shù)據(jù)

舉例說(shuō)明分析

Javascript 變量類(lèi)型

這個(gè)網(wǎng)上有很多種說(shuō)法,說(shuō)法很多的原因也很簡(jiǎn)單,就是因?yàn)樵贘S一切皆對(duì)象,一切引用類(lèi)型都是繼承于 Object 類(lèi)型的。我在這里分為幾大類(lèi)

 undefiend 沒(méi)有定義數(shù)據(jù)類(lèi)型
 number 數(shù)值數(shù)據(jù)類(lèi)型 例如 10或者1或者5.5
 string 字符串?dāng)?shù)據(jù)類(lèi)型用來(lái)描述文本 例如 "你的姓名"
 boolean 布爾類(lèi)型 true|false  不是正就是反,是或者否,有或者沒(méi)有
 object 對(duì)象類(lèi)型 復(fù)雜的一組描述信息的集合
 function 函數(shù)類(lèi)型 

這么分的根據(jù)是什么呢?很簡(jiǎn)單,就是通過(guò) typeof 來(lái)區(qū)分的

function Fun(){}
var unde = typeof abc;    // 基本類(lèi)型
var numb = typeof 300;    // 基本類(lèi)型
var str  = typeof "hello";    // 基本類(lèi)型
var bool = typeof true;       // 基本類(lèi)型
var func = typeof function(){}   // 引用/對(duì)象 類(lèi)型
var obje = typeof new String("hello");   // 引用/對(duì)象 類(lèi)型
var nul = typeof null;    // 引用/對(duì)象 類(lèi)型
var obj = typeof new Fun()   // 引用/對(duì)象 類(lèi)型
console.log('\n',unde,'\n',numb,'\n',str,'\n',bool,'\n',func,'\n',obje);
console.log('\n',obj,'\n',nul)

結(jié)果為:

answer

從中可以看出,typeof 就只能返回這六種數(shù)據(jù)類(lèi)型,所以基本類(lèi)型就確定為這六種。其實(shí)函數(shù) function 也是對(duì)象類(lèi)型,這個(gè)問(wèn)題后面會(huì)講到的。
注意基本變量類(lèi)型不是對(duì)象類(lèi)型,只有基本包裝類(lèi)型才是對(duì)象類(lèi)型。所以 str instanceof Object/String 都是 false

instanceof 舉例

  1. 常規(guī)用法
var StringObje = new String("instanceof");
console.log(StringObje instaceof String); // 輸出為true

這段代碼問(wèn)的是,變量 StringObje 是不是 String 對(duì)象的實(shí)例,答案為 true,很顯然是的 , typeof 只會(huì)返回 "Object",所以 instanceof 還是有用的,當(dāng)然你會(huì)發(fā)現(xiàn) StringObje instanceof Object 也是true
通常來(lái)講使用 instanceof 就是判斷一個(gè)實(shí)例是否屬于某種類(lèi)型

// 判斷 foo 是否是 Foo 的實(shí)例
function Foo(){}
var foo = new Foo();
console.log(foo instaceof Foo)  // true
  1. **instanceof 在繼承中關(guān)系應(yīng)用 **
// 判斷 a 是否是A類(lèi)的實(shí)例,和是否是其父類(lèi) AFather 的實(shí)例
function AFather(){}
function A(){}
AFather.prototype = new A(){};  // js原型繼承
var a = new A();
console.log(a instanceof A)  //true
console.log(a instanceof AFather)   //true

在多層繼承中,仍然適用。

  1. instanceof 復(fù)雜用法
function Foo(){}
console.log(Object instanceof Object);//1  true 
console.log(Function instanceof Function);//2  true 
console.log(Number instanceof Number);//3  false 
console.log(String instanceof String);//4  false 
console.log(Function instanceof Object);//5   true 
console.log(Foo instanceof Function);//6  true 
console.log(Foo instanceof Foo);//7  false

對(duì)上面的結(jié)果有沒(méi)有感覺(jué)到奇怪,奇怪也正常,因?yàn)榇_實(shí)挺奇怪的!
對(duì)上面的分析,首先要看看,你檢測(cè)的到底是什么?

console.log(Object,Function,String,Number,Foo);
  /***
  *  結(jié)果如下
  *   function Object() { [native code] } 
  *   function Function() { [native code] } 
  *   function String() { [native code] } 
  *   function Number() { [native code] } 
  *   function Foo(){}
  */

這已經(jīng)很明顯了,所有的檢測(cè)對(duì)象都是一個(gè)函數(shù),那么必定屬于函數(shù)類(lèi)型和對(duì)象類(lèi)型,只剩下3,4,7有問(wèn)題了,那么為什么是 false 呢?你想想,Foo函數(shù)是Foo的實(shí)例嗎?顯然不是啊,同理,String和Number函數(shù)也不是其本身的實(shí)例,new Func() , 這個(gè)才是實(shí)例對(duì)象。
想徹底明白其中奧妙,必須要了解語(yǔ)言規(guī)范原型繼承機(jī)制

  1. 規(guī)范中 instanceof 運(yùn)算符定義
    可以參考這個(gè)網(wǎng)址 :instanceof 語(yǔ)法
      規(guī)范定義很晦澀,而且看起來(lái)比較復(fù)雜,涉及到很多概念,但把規(guī)范翻譯成 JavaScript 代碼卻很簡(jiǎn)單,如下:
 function instance_of(L, R) {    //L 表示左表達(dá)式,R 表示右表達(dá)式
  var O = R.prototype;           // 取 R 的顯示原型
  L = L.__proto__;               // 取 L 的隱式原型
  while (true) { 
    if (L === null)  return false;
    if (O === L)  return true;   // 這里重點(diǎn):當(dāng) O 嚴(yán)格等于 L 時(shí),返回 true 
    L = L.__proto__; 
  } 
 }
  1. Javascript 原型繼承機(jī)制
      本文主要在分析 JavaScript instanceof 運(yùn)算符,對(duì)于 JavaScript 的原型繼承機(jī)制不再做詳細(xì)的講解,下面參考來(lái)自http://www.mollypages.org/misc/js.mp 的一張圖片,此圖片詳細(xì)的描述了 JavaScript 各種對(duì)象的顯示和隱式原型鏈結(jié)構(gòu)。
      由其涉及顯示原型和隱式原型,所以下面對(duì)這兩個(gè)概念作一下簡(jiǎn)單說(shuō)明。在 JavaScript 原型繼承結(jié)構(gòu)里面,規(guī)范中用 [[Prototype]] 表示對(duì)象隱式的原型,在 JavaScript 中用 __proto__ 表示,并且在 Firefox 和 Chrome 瀏覽器中是可以訪問(wèn)得到這個(gè)屬性的,但是 IE 下不行。所有 JavaScript 對(duì)象都有 __proto__ 屬性,但只有 Object.prototype.__proto__ 為 null,前提是沒(méi)有在 Firefox 或者 Chrome 下修改過(guò)這個(gè)屬性。這個(gè)屬性指向它的原型對(duì)象。 至于顯示的原型,在 JavaScript 里用 prototype 屬性表示,這個(gè)是 JavaScript 原型繼承的基礎(chǔ)知識(shí),在這里就不在敘述了。

    原型結(jié)構(gòu)圖

    這個(gè)圖片很好地表達(dá)了js原型鏈的關(guān)系,要仔細(xì)去看,跟著箭頭一步一步分析。

  2. 上述復(fù)雜問(wèn)題的推演過(guò)程
    如果你理解了 javascript 原型鏈,那么這個(gè)問(wèn)題就簡(jiǎn)單了!
    下面將詳細(xì)講解 Object instanceof Object,F(xiàn)unction instanceof Function 和 Foo instanceof Foo 三個(gè)示例,其它示例讀者可自行推演。

 1. Object instanceof Object
 // 為了方便表述,首先區(qū)分左側(cè)表達(dá)式和右側(cè)表達(dá)式
 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
 2. Function instanceof Function
 // 為了方便表述,首先區(qū)分左側(cè)表達(dá)式和右側(cè)表達(dá)式
 FunctionL = Function, FunctionR = Function; 
 // 下面根據(jù)規(guī)范逐步推演
 O = FunctionR.prototype = Function.prototype 
 L = FunctionL.__proto__ = Function.prototype 
 // 第一次判斷
 O == L 
 // 返回 true
 3. Foo instanceof Foo
 // 為了方便表述,首先區(qū)分左側(cè)表達(dá)式和右側(cè)表達(dá)式
 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
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容