在 ECMAScript 規范中,共定義了 7 種資料型別,分為 基本型別 和 參考型別 兩大類,如下所示:
基本型別:String、Number、Boolean、Symbol、Undefined、Null 參考型別:Object
基本型別也稱為簡單型別,由于其占據空間固定,是簡單的資料段,為了便于提升變數查詢速度,將其存盤在堆疊中,即按值訪問,
參考型別也稱為復雜型別,由于其值的大小會改變,所以不能將其存放在堆疊中,否則會降低變數查詢速度,因此,其值存盤在堆(heap)中,而存盤在變數處的值,是一個指標,指向存盤物件的記憶體處,即按址訪問,參考型別除 Object 外,還包括 Function 、Array、RegExp、Date 等等,
鑒于 ECMAScript 是松散型別的,因此需要有一種手段來檢測給定變數的資料型別,對于這個問題,JavaScript 也提供了多種方法,但遺憾的是,不同的方法得到的結果參差不齊,
下面介紹常用的4種方法,并對各個方法存在的問題進行簡單的分析,
1、typeof
typeof 是一個運算子,其右側跟一個一元運算式,并回傳這個運算式的資料型別,回傳的結果用該型別的字串(全小寫字母)形式表示,包括以下 7 種:number、boolean、symbol、string、object、undefined、function 等,
typeof'';// string 有效
typeof1;// number 有效
typeofSymbol();// symbol 有效
typeoftrue;//boolean 有效
typeofundefined;//undefined 有效
typeofnull;//object 無效
typeof[] ;//object 無效
typeofnewFunction();// function 有效
typeofnewDate();//object 無效
typeofnewRegExp();//object 無效
有些時候,typeof 運算子會回傳一些令人迷惑但技術上卻正確的值:
- 對于基本型別,除 null 以外,均可以回傳正確的結果,
- 對于參考型別,除 function 以外,一律回傳 object 型別,
- 對于 null ,回傳 object 型別,
- 對于 function 回傳 function 型別,
其中,null 有屬于自己的資料型別 Null , 參考型別中的 陣列、日期、正則 也都有屬于自己的具體型別,而 typeof 對于這些型別的處理,只回傳了處于其原型鏈最頂端的 Object 型別,沒有錯,但不是我們想要的結果,
2、instanceof
instanceof 是用來判斷 A 是否為 B 的實體,運算式為:A instanceof B,如果 A 是 B 的實體,則回傳 true,否則回傳 false, 在這里需要特別注意的是:instanceof 檢測的是原型,我們用一段偽代碼來模擬其內部執行程序:
instanceof (A,B) = {
varL = A.__proto__;
varR = B.prototype;
if(L === R) {
// A的內部屬性 __proto__ 指向 B 的原型物件
returntrue;
}
returnfalse;
}
從上述程序可以看出,當 A 的 proto 指向 B 的 prototype 時,就認為 A 就是 B 的實體,我們再來看幾個例子:
[] instanceof Array;// true
{} instanceof Object;// true
newDate() instanceof Date;// true
function Person(){};
newPerson() instanceof Person;
[] instanceof Object;// true
newDate() instanceof Object;// true
newPerson instanceof Object;// true
我們發現,雖然 instanceof 能夠判斷出 [ ] 是Array的實體,但它認為 [ ] 也是Object的實體,為什么呢?
我們來分析一下 [ ]、Array、Object 三者之間的關系:
從 instanceof 能夠判斷出 [ ].proto 指向 Array.prototype,而 Array.prototype.proto 又指向了Object.prototype,最終 Object.prototype.proto 指向了null,標志著原型鏈的結束,因此,[]、Array、Object 就在內部形成了一條原型鏈:

從原型鏈可以看出,[] 的 proto 直接指向Array.prototype,間接指向 Object.prototype,所以按照 instanceof 的判斷規則,[] 就是Object的實體,依次類推,類似的 new Date()、new Person() 也會形成一條對應的原型鏈 ,因此,instanceof 只能用來判斷兩個物件是否屬于實體關系, 而不能判斷一個物件實體具體屬于哪種型別,
instanceof 運算子的問題在于,它假定只有一個全域執行環境,如果網頁中包含多個框架,那實際上就存在兩個以上不同的全域執行環境,從而存在兩個以上不同版本的建構式,如果你從一個框架向另一個框架傳入一個陣列,那么傳入的陣列與在第二個框架中原生創建的陣列分別具有各自不同的建構式,
variframe = document.createElement('iframe');
document.body.appendChild(iframe);
xArray = window.frames[0].Array;
vararr =newxArray(1,2,3);// [1,2,3]
arr instanceof Array;// false
if(Array.isArray(value)){
//對陣列執行某些操作
}
Array.isArray() 本質上檢測的是物件的 [[Class]] 值,[[Class]] 是物件的一個內部屬性,里面包含了物件的型別資訊,其格式為 [object Xxx] ,Xxx 就是對應的具體型別 ,對于陣列而言,[[Class]] 的值就是 [object Array] ,
3、constructor
當一個函式 F被定義時,JS引擎會為F添加 prototype 原型,然后再在 prototype上添加一個 constructor 屬性,并讓其指向 F 的參考,如下所示:

當執行 var f = new F() 時,F 被當成了建構式,f 是F的實體物件,此時 F 原型上的 constructor 傳遞到了 f 上,因此 f.constructor == F

可以看出,F 利用原型物件上的 constructor 參考了自身,當 F 作為建構式來創建物件時,原型上的 constructor 就被遺傳到了新創建的物件上, 從原型鏈角度講,建構式 F 就是新物件的型別,這樣做的意義是,讓新物件在誕生以后,就具有可追溯的資料型別,
同樣,JavaScript 中的內置物件在內部構建時也是這樣做的:

細節問題:
- null 和 undefined 是無效的物件,因此是不會有 constructor 存在的,這兩種型別的資料需要通過其他方式來判斷,2. 函式的 constructor 是不穩定的,這個主要體現在自定義物件上,當開發者重寫 prototype 后,原有的 constructor 參考會丟失,constructor 會默認為 Object

為什么變成了 Object?
因為 prototype 被重新賦值的是一個 { }, { } 是 new Object() 的字面量,因此 new Object() 會將 Object 原型上的 constructor 傳遞給 { },也就是 Object 本身,
因此,為了規范開發,在重寫物件原型時一般都需要重新給 constructor 賦值,以保證物件實體的型別不被篡改,
4、toString
toString() 是 Object 的原型方法,呼叫該方法,默認回傳當前物件的 [[Class]] ,這是一個內部屬性,其格式為 [object Xxx] ,其中 Xxx 就是物件的型別,
對于 Object 物件,直接呼叫 toString() 就能回傳 [object Object] ,而對于其他物件,則需要通過 call / apply 來呼叫才能回傳正確的型別資訊,
Object.prototype.toString.call('') ; // [object String]
Object.prototype.toString.call(1) ; // [object Number]
Object.prototype.toString.call(true) ;// [object Boolean]
Object.prototype.toString.call(Symbol());//[object Symbol]
Object.prototype.toString.call(undefined) ;// [object Undefined]
Object.prototype.toString.call(null) ;// [object Null]
Object.prototype.toString.call(newFunction()) ;// [object Function]
Object.prototype.toString.call(newDate()) ;// [object Date]
Object.prototype.toString.call([]) ;// [object Array]
Object.prototype.toString.call(newRegExp()) ;// [object RegExp]
Object.prototype.toString.call(newError()) ;// [object Error]
Object.prototype.toString.call(document) ;// [object HTMLDocument]
Object.prototype.toString.call(window) ;//[object global] window 是全域物件 global 的參考
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/258196.html
標籤:其他
下一篇:前端開發當中常用的演算法
