這次主要說說javascript的類型判斷函數typeof和判斷建構函式原型instanceof的用法和注意的地方。
typeof
先來說說typeof吧。首先需要注意的是,typeof方法返回一個字串,來表示資料的類型。
typeof 是一個一元運算,放在一個運算數之前,運算數可以是任意類型。
它傳回值是一個字串,該字串說明運算數的類型。typeof 一般只能返回如下幾個結果:
number,boolean,string,function,object,undefined。我們可以使用 typeof 來擷取一個變數是否存在,如 if(typeof a!="undefined"){alert("ok")},而不要去使用 if(a) 因為如果 a 不存在(未聲明)則會出錯,對於 Array,Null 等特殊對象使用 typeof 一律返回 object,這正是 typeof 的局限性。
文法講解
我們先看看各個資料類型對應typeof的值:
| 資料類型 |
Type |
| Undefined |
“undefined” |
| Null |
“object” |
| 布爾值 |
“boolean” |
| 數值 |
“number” |
| 字串 |
“string” |
| Symbol (ECMAScript 6 新增) |
“symbol” |
| 宿主對象(JS環境提供的,比如瀏覽器) |
Implementation-dependent |
| 函數對象 |
“function” |
| 任何其他對象 |
“object” |
再看看具體的執行個體:
// Numberstypeof 37 === 'number';typeof 3.14 === 'number';typeof Math.LN2 === 'number';typeof Infinity === 'number';typeof NaN === 'number'; // 儘管NaN是"Not-A-Number"的縮寫,意思是"不是一個數字"typeof Number(1) === 'number'; // 不要這樣使用!// Stringstypeof "" === 'string';typeof "bla" === 'string';typeof (typeof 1) === 'string'; // typeof返回的肯定是一個字串typeof String("abc") === 'string'; // 不要這樣使用!// Booleanstypeof true === 'boolean';typeof false === 'boolean';typeof Boolean(true) === 'boolean'; // 不要這樣使用!// Symbolstypeof Symbol() === 'symbol';typeof Symbol('foo') === 'symbol';typeof Symbol.iterator === 'symbol';// Undefinedtypeof undefined === 'undefined';typeof blabla === 'undefined'; // 一個未定義的變數,或者一個定義了卻未賦初值的變數// Objectstypeof {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 用於判斷一個變數是否某個對象的執行個體,如 var a=new Array();alert(a instanceof Array); 會返回 true,同時 alert(a instanceof Object) 也會返回 true;這是因為 Array 是 object 的子類。再如:function test(){};var a=new test();alert(a instanceof test) 會返回
談到 instanceof 我們要多插入一個問題,就是 function 的 arguments,我們大家也許都認為 arguments 是一個 Array,但如果使用 instaceof 去測試會發現 arguments 不是一個 Array 對象,儘管看起來很像。
如果對原型不太瞭解,可以看看深入理解原型。
下面我們看看instanceof的執行個體:
// 定義建構函式function C(){} function D(){} var o = new C();// true,因為 Object.getPrototypeOf(o) === C.prototypeo instanceof C; // false,因為 D.prototype不在o的原型鏈上o instanceof D; o instanceof Object; // true,因為Object.prototype.isPrototypeOf(o)返回trueC.prototype instanceof Object // true,同上C.prototype = {};var o2 = new C();o2 instanceof C; // trueo instanceof C; // false,C.prototype指向了一個Null 物件,這個Null 物件不在o的原型鏈上.D.prototype = new C(); // 繼承var o3 = new D();o3 instanceof D; // trueo3 instanceof C; // true
但是這裡我們需要注意一個問題:
function f(){ return f; }document.write(new f() instanceof f);//falsefunction g(){}document.write(new g() instanceof g);//true
第一個為什麼返回false呢?因為建構函式的原型被覆蓋了,我們可以看看new f和new g的區別: