JavaScript 三種方法,可以確定一個值到底是什麼型別。
typeof
instanceof
Object.prototype.toString
為什麼需要確定型別 ?
只有確定型別的情況,才知道當前操作物件擁有哪些功能; 比如使用 push,unshfit,shfit 等方法時,那麼其必須為陣列型別時才能正確使用;
當某些情況新增型別檢查時,這樣程式碼更加健壯,安全;
typeof 運算子
返回一個值的資料型別。
基本語法:
typeof operand
or
typeof (operand)
operand
是一個表示式,表示物件或原始值,其型別將被返回。括號是可選的.
示例:基本資料型別
{
typeof 1 // "number"
typeof Number(1) // "number"
typeof `` // "string"
typeof true // "boolean"
typeof null // "object"
typeof undefined // "undefined"
typeof Symbol // "function"
}
當運算元(operand)為基本資料型別,其返回字串與期望一樣,也能夠辨別當前運算元得型別;
這裡需要提及下基本資料型別 null ,為什麼 typeof null 返回的字串為 “object”;
在 JavaScript 最初的實現中,JavaScript 中的值是由一個表示型別的標籤和實際資料值表示的。物件的型別標籤是 0。由於 null
代表的是空指標(大多數平臺下值為 0x00),因此,null的型別標籤也成為了 0,typeof null
就錯誤的返回了”object"
。
示例:引用資料型別
typeof new Number(1) // `object`
typeof new String() // `object`
typeof new Array() // `object`
typeof new Date() // `object`
typeof new Function() // `function`
從上面看出,所有通過 new 關鍵例項化的建構函式返回都 object 型別. 當然函式是一個例外;
從上述兩個示例可以看出,當typeof 的運算元為基本資料型別、函式返回字串能夠區分其資料型別;
那麼如果需要區分引用型別時需要藉助 JavaScript中另外一個運算子;
instanceof 運算子
判斷例項物件是不是類(建構函式)的例項
instanceof
工作原理基於原型鏈,也就是說如果在物件的原型鏈上能夠找到建構函式的 prototype 物件,那麼該操作就返回 true;
基本語法:**
object instanceof constructor
引數
- object
要檢測的物件. - constructor
某個建構函式
示例一:
let o = new Object();
let bool = new Boolean();
let num = new Number(1);
let str = new String();
let arr = new Array();
// 自定義建構函式
function Person(){}
function Animal(){}
let person = new Person();
let animal = new Animal();
person instanceof Person // true
animal instanceof Animal // true
o instanceof Object; // true
bool instanceof Boolean; // true
num instanceof Number; // true
str instanceof String; // true
arr instanceof Array; // true
這樣彌補 typeof
在檢測引用型別的時的問題;
通過下面的示例,驗證下 instanceof
工作原理:
{
function Person(){}
function Animal(){}
// 例如自定義定義兩個類
let person = new Person();
let animal = new Animal();
console.log(animal instanceof Object); // => true
console.log(animal instanceof Animal); // true
console.log(person instanceof Object); // => true
console.log(person instanceof Person); // true
console.log(person instanceof Animal); // => false
console.log(animal instanceof Person); // => false
}
上面應該跟我們預期的一樣, 那麼有沒有通過一種辦法讓
person instanceof Animal // => true
那麼來針對上面作如下調整:
person.__proto__ = Animal.prototype;
console.log(person instanceof Animal) // => true
console.log(person instanceof Person) // => false
嚴格意義上來說, 上述這麼改是沒有意思;這裡只是為了驗證 instanceof
如何工作的;其次說明 person instanceof Person
返回 true, 則並不意味著給表示式永遠返回 true, Person.prototype
和 person.__proto__
都是可變的;
instanceof
和多個全域性物件(多個frame或多個window之間的互動)
可以定義兩個頁面測試: parent.html、 child.html
// parent.html
<iframe src="child.html" onload="test()">
</iframe>
<script>
function test(){
var value = window.frames[0].v;
console.log(value instanceof Array); // false
}
</script>
// child.html
<script>
window.name = `child`;
var v = [];
</script>
嚴格上來說value
就是陣列,但parent頁面中列印輸出: false ;也就是 parent 頁面中 Array.prototype != window.frames[0].Array.prototype ;
主要引起問題是,因為多個視窗意味著多個全域性環境,不同的全域性環境擁有不同的全域性物件,從而擁有不同的內建型別建構函式.
instanceof 應用 – 檢測作用域的安全
function Person(name, age){
this.name = name;
this.age = age
}
// 正常情況下
{
let person = new Person(`託尼`, 20);
console.log(person.name,person.age);
}
// 假如在例項化時候忘記新增 new 關鍵字尼? 會出現什麼情況尼?
{
let person = Person(`託尼`, 20);
console.log(person.name, person.age); // Uncaught TypeError: Cannot read property `name` of undefined
// Person 被當作普通的方法執行,其次 name ,age 被新增window上
console.log(name, age); //=> 託尼 20
}
// 如何避免這樣情況,在加 new 關鍵字或省略時候都能正常返回例項物件
{
function Person(name, age){
if(this instanceof Person) {
this.name = name;
this.age = age
return this;
}else {
return new Person(name, age);
}
}
let person = Person(`託尼`, 20);
console.log(person.name, person.age); // 託尼 20
}
Object.prototype.toString 方法
預設情況下(不覆蓋 toString
方法前提下),任何一個物件呼叫 Object 原生的 toString
方法都會返回 “[object type]”,其中 type 是物件的型別;
每個類的內部都有一個 [[Class]] 屬性,這個屬性中就指定了上述字串中的 type(建構函式名) ;
舉個例子吧:
console.log(Object.prototype.toString.call([])); // [object Array]
上述中: Array
對應也就當前物件的 type,同樣就是當前物件的建構函式名;
前面在說 instanceof
在多個作用域的情況下,嘗試用這種方式解決:
function isArray(arr){
return Object.prototype.toString.call(arr) === "[object Array]"
}
console.log(isArray(value)); // true
從輸出來看時完美解決了;
為什麼這種方式是可以咧?這裡辨別型別根據 建構函式名稱,由於原生陣列的建構函式名稱與全域性作用域無關,因此使用 toString() 就能保證返回一致的值。
同理其它原生物件檢測也能按照這種方式來,如 Date,RegExp,Function
function isFunction(value) {
return Object.prototype.toString.call(value) === "[object Function]"
}
function isDate(value) {
return Object.prototype.toString.call(value) === "[object Date]"
}
function isRegExp(value) {
return Object.prototype.toString.call(value) === "[object RegExp]"
}
isDate(new Date()); // true
isRegExp(/w/); // true
isFunction(function(){}); //true
上述程式碼,可以作進一步改進,由於 isFunction、isDate、isRegExp 方法中,其實只有 [object ConstructorName] ConstructorName不一樣,可以利用工廠函式再修飾一下:
function generator(type){
return function(value){
return Object.prototype.toString.call(value) === "[object "+ type +"]"
}
}
let isFunction = generator(`Function`)
let isArray = generator(`Array`);
let isDate = generator(`Date`);
let isRegExp = generator(`RegExp`);
isArray([])); // true
isDate(new Date()); // true
isRegExp(/w/); // true
isFunction(function(){}); //true
這樣即使要生成其它原生物件驗證函式前面時就簡單多了;
總結:
- typeof 適用於檢測值型別, 特別注意 null 的問題,這也是面試用經常遇到的.
- instanceof 檢測引用型別.
- toString 彌補 instanceof 在跨視窗下對型別檢測問題, toString 只適應於原生物件;
對於使用者自定義的物件無法區分的.