天天看點

4種JavaScript資料類型判斷的方法

4種JavaScript資料類型判斷的方法

今天分享4種JavaScript類型判斷的方法:typeof、instanceof、Object.prototype.toString.call()、constructor

JavaScript資料類型

JavaScript有八種内置類型,除對象外,其他統稱為基本類型

  • 空值(null)
  • 未定義(undefined)
  • 布爾值(boolean)
  • 數字(number)
  • 字元串(string)
  • 對象 (object)
  • 符号(symbol, ES6中新增)
  • 大整數(BigInt, ES2020 引入)

Symbol: 是ES6中引入的一種原始資料類型,表示獨一無二的值。

BigInt:是 ES2020 引入的一種新的資料類型,用來解決 JavaScript中數字隻能到 53 個二進制位(JavaScript 所有數字都儲存成 64 位浮點數,大于這個範圍的整數,無法精确表示的問題。具體可檢視:新資料類型 — BigInt

1、typeof

typeof是一個操作符而不是函數,其右側跟一個一進制表達式,并傳回這個表達式的資料類型。傳回的結果用該類型的字元串(全小寫字母)形式表示

包括以下 8 種:string、number、boolean、undefined、function 、symbol、bigInt、object。

對于數組,對象,null以及時間等資料,typeof隻能傳回object,而不能直接傳回對應的類型,還需要通過其他法判斷。

console.log(typeof "");            // string
console.log(typeof 1 );            // number
console.log(typeof NaN );          // number
console.log(typeof true);          // boolean
console.log(typeof undefined);     // undefined
console.log(typeof function(){});  // function
console.log(typeof isNaN);         // function
console.log(typeof Symbol());      // symbol
console.log(typeof 123n);          // bigint
console.log(typeof []);            // object
console.log(typeof {});            // object
console.log(typeof null);          // object
console.log(typeof new Date());    // object
console.log(typeof new RegExp());  // object      

2、instanceof

instanceof 是用來判斷 A 是否為 B 的執行個體,表達式為:A instanceof B,如果 A 是 B 的執行個體,則傳回 true,否則傳回 false。 需特别注意:instanceof 檢測的是原型

即instanceof 用來比較一個對象是否為某一個構造函數的執行個體。instanceof可以準确的判斷複雜資料類型,但是不能正确判斷基本資料類型。

console.log(12 instanceof Number);                 // false
console.log('22' instanceof String);               // false
console.log(true instanceof Boolean);              // false
console.log(null instanceof Object);               // false
console.log(undefined instanceof Object);          // false
console.log(function a() {} instanceof Function);  // true
console.log([] instanceof Array);                  // true
console.log({a: 1} instanceof Object);             // true
console.log(new Date() instanceof Date);           // true      

補充:

instanceof 的原理:主要的實作原理就是隻要右邊變量的 prototype在左邊變量的原型鍊上即可。

是以,instanceof 在查找的過程中會周遊左邊變量的原型鍊,直到找到右邊變量的 prototype,如果存在傳回true 否則傳回false

function new_instance_of(leftVaule, rightVaule) { 
    let rightProto = rightVaule.prototype; // 取右表達式的 prototype 值
    leftVaule = leftVaule.__proto__; // 取左表達式的__proto__值
    while (true) {
      if (leftVaule === null) {
            return false;  
        }
        if (leftVaule === rightProto) {
            return true;  
        } 
        leftVaule = leftVaule.__proto__ 
    }
}      

注意:instanceof 運算時會遞歸查找leftVaule的原型鍊,即leftVaule.__proto__.__proto__.__proto__.__proto__... 直到找到了或者找到頂層為止。

一句話了解其運算規則:instanceof 檢測左側的 __proto__ 原型鍊上,是否存在右側的 prototype 原型。

3、constructor

  1. JavaScript中,每個對象都有一個constructor屬性,可以得知某個執行個體對象,到底是哪一個構造函數産生的, constructor屬性表示原型對象與構造函數之間的關聯關系。
  • 當一個函數F被定義時,JS引擎會為F添加prototype原型,然後在prototype上添加一個constructor屬性,并讓其指向F的引用,F利用原型對象的constructor屬性引用了自身,當F作為構造函數建立對象時,原型上的constructor屬性被遺傳到了新建立的對象上,從原型鍊角度講,構造函數F就是新對象的類型。這樣做的意義是,讓對象誕生以後,就具有可追溯的資料類型。
  • 通過typeof運算符來判斷它是原始的值還是對象。如果是對象,就可以使用constructor屬性來判斷其類型。
  • 如判斷數組的函數:
function isArray(data){ 
  return typeof data == "object" && data.constructor == Array; 
}
isArray([])  // true      

注意:null 和 undefined 是沒有 constructor 存在的,這兩種類型的資料需要通過其他方式來判斷。

console.log('22'.constructor === String)             // true
console.log(true.constructor === Boolean)            // true
console.log([].constructor === Array)                // true
console.log(document.constructor === HTMLDocument)   // true
console.log(window.constructor === Window)           // true
console.log(new Number(22).constructor === Number)   // true
console.log(new Function().constructor === Function) // true
console.log(new Date().constructor === Date)         // true
console.log(new RegExp().constructor === RegExp)     // true
console.log(new Error().constructor === Error)       // true      

2、如果修改了原型對象,一般會同時修改constructor屬性,防止引用的時候出錯。是以,修改原型對象時,一般要同時修改constructor屬性的指向。

function Rectangle(width, height){
    this.width = width;
    this.height = height;
    this.getArea = function(){
        return '矩形的面積為' + (width * height);
    }
}


var rect1 = new Rectangle(40, 20);
var rect2 = new Rectangle(50, 20);
var rect3 = new Rectangle(60, 20);
console.log(rect1.getArea());
console.log(rect2.getArea());
console.log(rect3.getArea());      
  • 如上代碼,每次執行個體化出一個對象,都會添加getArea方法,是三個對象共有且不變的,是以将getArea放在構造函數中就會在建立對象時被多次添加,浪費記憶體!
  • 是以我們将getArea添加到原型對象上就減少了多次添加,執行個體化對象會沿着原型鍊查找到此屬性
  • 實作了共享屬性:
function Rectangle(width, height){
    this.width = width;
    this.height = height;
}


// 直接替換原型對象,但是要記得添加上構造函數屬性
Rectangle.prototype = {
    constructor: Rectangle,
    getArea: function(){
        return '矩形的面積為' + (this.width * this.height);
    }
}


// 修改特性
Object.defineProperties(Rectangle.prototype, {
    constructor: {
        enumerable: false,
        configurable: false,
        writable: false
    },
    getArea: {
        enumerable: false,
        configurable: false,
        writable: false
    }
})


var rect1 = new Rectangle(40, 20);
var rect2 = new Rectangle(50, 20);
var rect3 = new Rectangle(60, 20);
console.log(rect1.getArea());
console.log(rect2.getArea());
console.log(rect3.getArea());      

很多情況下,我們可以使用instanceof運算符或對象的constructor屬性來檢測對象是否為數組。

如很多JS架構就是使用這兩種方法來判斷對象是否為數組類型。 但是檢測在跨架構(cross-frame)頁面中的數組時,會失敗。

原因就是在不同架構(iframe)中建立的數組不會互相共享其prototype屬性。例如:

<script>
     window.onload=function(){
         var iframe_arr=new window.frames[0].Array;
         console.log(iframe_arr instanceof Array);     // false
         console.log(iframe_arr.constructor == Array); // false
     }
</script>      

4、Object.prototype.toString.call()

  • Object.prototype.toString(o)是 Object 的原型方法,
  1. 擷取對象o的class屬性。這是一個内部屬性,
  2. 連接配接字元串:[object + 結果(1)],格式為 [object Xxx] ,其中 Xxx 就是對象的類型。
  • 對于 Object 對象,直接調用 toString() 就能傳回 [object Object] 。而對于其他對象,則需要通過 call / apply 來調用才能傳回正确的類型資訊。
console.log(Object.prototype.toString.call(1))          // [object Number]
  console.log(Object.prototype.toString.call(1n))         // [object BigInt]
  console.log(Object.prototype.toString.call('123'))      // [object String]
  console.log(Object.prototype.toString.call(true))       // [object Boolean]
  console.log(Object.prototype.toString.call(undefined))  // [object Undefined]
  console.log(Object.prototype.toString.call(null))       // [object Null]
  console.log(Object.prototype.toString.call({}))         // [object Object]
  console.log(Object.prototype.toString.call([]))         // [object Array]
  console.log(Object.prototype.toString.call(function a() {}))  // [object Function]
  console.log(Object.prototype.toString.call(Symbol()))         // [object Symbol]
  console.log(Object.prototype.toString.call(Math))             // [object Math]
  console.log(Object.prototype.toString.call(JSON))             // [object JSON]
  console.log(Object.prototype.toString.call(new Date()))       // [object Date]
  console.log(Object.prototype.toString.call(new RegExp()))     // [object RegExp]
  console.log(Object.prototype.toString.call(new Error))        // [object Error]
  console.log(Object.prototype.toString.call(window)            // [object Window]
  console.log(Object.prototype.toString.call(document))         // [object HTMLDocument]      

封裝一個準确判斷資料類型的函數

function __getType(object){
    return Object.prototype.toString.call(object).match(/^\[object\s(.*)\]$/)[1];
  };      

可以解決上面的跨架構問題。

<script>
      window.onload=function(){
          var iframe_arr=new window.frames[0].Array;
          console.log(Object.prototype.toString.call(iframe_arr)))  // "[object Array]"
      }
</script>      

繼續閱讀