JavaScript中typeof和instanceof深入詳解
這次主要說說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的區別: