JavaScript中typeof和instanceof深入詳解

jopen 8年前發布 | 25K 次閱讀 JavaScript開發 JavaScript

這次主要說說javascript的類型判斷函數typeof和判斷構造函數原型instanceof的用法和注意的地方。

typeof

先來說說typeof吧。首先需要注意的是,typeof方法返回一個字符串,來表示數據的類型

語法講解

我們先看看各個數據類型對應typeof的值:

數據類型 Type
Undefined “undefined”
Null “object”
布爾值 “boolean”
數值 “number”
字符串 “string”
Symbol (ECMAScript 6 新增) “symbol”
宿主對象(JS環境提供的,比如瀏覽器) Implementation-dependent
函數對象 “function”
任何其他對象 “object”

再看看具體的實例:

// Numbers
typeof 37 === 'number';
typeof 3.14 === 'number';
typeof Math.LN2 === 'number';
typeof Infinity === 'number';
typeof NaN === 'number'; // 盡管NaN是"Not-A-Number"的縮寫,意思是"不是一個數字"
typeof Number(1) === 'number'; // 不要這樣使用!

// Strings
typeof "" === 'string';
typeof "bla" === 'string';
typeof (typeof 1) === 'string'; // typeof返回的肯定是一個字符串
typeof String("abc") === 'string'; // 不要這樣使用!

// Booleans
typeof true === 'boolean';
typeof false === 'boolean';
typeof Boolean(true) === 'boolean'; // 不要這樣使用!

// Symbols
typeof Symbol() === 'symbol';
typeof Symbol('foo') === 'symbol';
typeof Symbol.iterator === 'symbol';

// Undefined
typeof undefined === 'undefined';
typeof blabla === 'undefined'; // 一個未定義的變量,或者一個定義了卻未賦初值的變量

// Objects
typeof {a:1} === 'object';

// 使用Array.isArray或者Object.prototype.toString.call方法可以從基本的對象中區分出數組類型
typeof [1, 2, 4] === 'object';

typeof new Date() === 'object';

// 下面的容易令人迷惑,不要這樣使用!
typeof new Boolean(true) === 'object';
typeof new Number(1) ==== 'object';
typeof new String("abc") === 'object';

// 函數
typeof function(){} === 'function';
typeof Math.sin === 'function';

我們會發現一個問題,就是typeof來判斷數據類型其實并不準確。比如數組、正則、日期、對象的typeof返回值都是object,這就會造成一些誤差。

所以在typeof判斷類型的基礎上,我們還需要利用Object.prototype.toString方法來進一步判斷數據類型。

我們來看看在相同數據類型的情況下,toString方法和typeof方法返回值的區別:

數據 toString typeof
“foo” String string
new String(“foo”) String object
new Number(1.2) Number object
true Boolean boolean
new Boolean(true) Boolean object
new Date() Date object
new Error() Error object
new Array(1, 2, 3) Array object
/abc/g RegExp object
new RegExp(“meow”) RegExp object

可以看到利用toString方法可以正確區分出Array、Error、RegExp、Date等類型。

所以我們一般通過該方法來進行數據類型的驗證

真題檢測

但是既然今天說到了typeof,那這里就列出幾道題目,來看看自己是否真正掌握了typeof的用法。

第一題:

var y = 1, x = y = typeof x;
 x;

第二題:

 (function f(f){
    return typeof f();
  })(function(){ return 1; });

第三題:

  var foo = {
    bar: function() { return this.baz; },
    baz: 1
  };
  (function(){
    return typeof arguments[0]();
  })(foo.bar);

第四題:

  var foo = {
    bar: function(){ return this.baz; },
    baz: 1
  }
  typeof (f = foo.bar)();

第五題:

var f = (function f(){ return "1"; }, function g(){ return 2; })();
typeof f;

第六題:

  var x = 1;
  if (function f(){}) {
    x += typeof f;
  }
  x;

第七題:

  (function(foo){
    return typeof foo.bar;
  })({ foo: { bar: 1 } });

下面公布答案了,這七題的答案分別是:

"undefined","number","undefined","undefined","number","1undefined","undefined"

做對了幾道呢?是不是很大的困惑呢?這幾題雖然都有typeof,但是考察了很多javascript的基礎噢。下面我們來一一詳解。

第一題:

var y = 1, x = y = typeof x;
 x;//"undefined"

表達式是從右往左的,x由于變量提升,類型不是null,而是undefined,所以x=y=”undefined”。

變量提升我在這篇文章中提到過,可以看看。

第二題:

  (function f(f){
    return typeof f();//"number"
  })(function(){ return 1; });

傳入的參數為f也就是function(){ return 1; }這個函數。通過f()執行后,得到結果1,所以typeof 1返回”number”。這道題很簡單,主要是區分f和f()。

第三題:

  var foo = {
    bar: function() { return this.baz; },
    baz: 1
  };
  (function(){
    return typeof arguments[0]();//"undefined"
  })(foo.bar);

這一題考察的是this的指向。this永遠指向函數執行時的上下文,而不是定義時的(ES6的箭頭函數不算)。當arguments執行時,this已經指向了window對象。所以是”undefined”。對this執行不熟悉的同學可以看看這篇文章:深入理解this,對剛剛提到的箭頭函數感興趣的同學可以看看初步探究ES6之箭頭函數

第四題:

  var foo = {
    bar: function(){ return this.baz; },
    baz: 1
  }
  typeof (f = foo.bar)();//undefined

如果上面那一題做對了,那么這一題也應該不會錯,同樣是this的指向問題。

第五題:

var f = (function f(){ return "1"; }, function g(){ return 2; })();
typeof f;//"number"

這一題比較容易錯,因為我在遇到這道題之前也從來沒有遇到過javascript的分組選擇符。什么叫做分組選擇符呢?舉一個例子就會明白了:

var a = (1,2,3);
document.write(a);//3,會以最后一個為準

所以上面的題目會返回2,typeof 2當然是”number”啦。

第六題:

  var x = 1;
  if (function f(){}) {
    x += typeof f;
  }
  x;//"1undefined"

這是一個javascript語言規范上的問題,在條件判斷中加入函數聲明。這個聲明語句本身沒有錯,也會返回true,但是javascript引擎在搜索的時候卻找不到該函數。所以結果為”1undefined”。

第七題:

  (function(foo){
    return typeof foo.bar;
  })({ foo: { bar: 1 } });

這題其實是一個考察心細程度的題目。形參的foo指向的是{ foo: { bar: 1 } }這個整體。相信這么說就明白了。

好啦。上面的題目都是很好的資源噢。

instanceof

接下來該說說instanceof方法了。instanceof運算符可以用來判斷某個構造函數的prototype屬性是否存在于另外一個要檢測對象的原型鏈上

如果對原型不太了解,可以看看深入理解原型

下面我們看看instanceof的實例:

// 定義構造函數
function C(){} 
function D(){} 

var o = new C();

// true,因為 Object.getPrototypeOf(o) === C.prototype
o instanceof C; 

// false,因為 D.prototype不在o的原型鏈上
o instanceof D; 

o instanceof Object; // true,因為Object.prototype.isPrototypeOf(o)返回true
C.prototype instanceof Object // true,同上

C.prototype = {};
var o2 = new C();

o2 instanceof C; // true

o instanceof C; // false,C.prototype指向了一個空對象,這個空對象不在o的原型鏈上.

D.prototype = new C(); // 繼承
var o3 = new D();
o3 instanceof D; // true
o3 instanceof C; // true

但是這里我們需要注意一個問題:

function f(){ return f; }
document.write(new f() instanceof f);//false
function g(){}
document.write(new g() instanceof g);//true

第一個為什么返回false呢?因為構造函數的原型被覆蓋了,我們可以看看new f和new g的區別:

JavaScript中typeof和instanceof深入詳解

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!