1. 程式人生 > >細數判斷數據類型的各種方法

細數判斷數據類型的各種方法

萬能 n) scrip .com 得到 則表達式 https his 可用

數據類型的分類

要想判斷數據類型,首先要知道數據類型的分類。數據類型分為基本數據類型和引用數據類型。

基本數據類型

基本數據類型有 種,ES6中新加了第 種基本數據類型——Symbol 類型。

  • 數值 (number): 整數和小數。
  • 字符串 (string): 文本
  • 布爾值 (boolean):true 和 false 。
  • undefined: 表示‘未定義’或不存在。一般情況下變量在聲明後未賦值前都是undefined。
  • null: 空值。
  • symbol: ES6 引入的新原始數據類型,表示獨一無二的值。

引用數據類型

引用類型數據也會統稱為對象,即廣義的對象,通常除了基本數據類型的其它數據都屬於引用類型數據。

  • 對象 (object): 狹義的對象,{key1:value1, key2:value2,...}
  • 數組 (array): [value1,value2,...]
  • 函數 (function)
  • 日期 (date)
  • 正則表達式 (RegExp)
  • ......

數據類型綜合判斷的各種方法

typeof 運算符

typeof 返回字符串,numberstringbooleansymbolundefinedfunction,所有其它的引用類型數據都返回 objectnull 也返回 object

typeof 666          // "number"
typeof 'dora'       // "string"
typeof true         // "boolean"
typeof Symbol()     // "symbol"
typeof undefined    // "undefined"
typeof null         // "object"
typeof function(){} // "function"
typeof []           // "object"
typeof /dora/       // "object"

優點

可利用判斷 undefined 來檢查一個沒有聲明的變量,而不報錯。實際編程中,這個特點通常用在判斷語句中。

// 錯誤的寫法
if (v) {
  // ...
}
// ReferenceError: v is not defined

// 正確的寫法
if (typeof v === "undefined") {
  // 這種寫法在 v 沒有聲明的時候不會報錯。
}

註意

ES6中引入了 let 之後,這個方法也不是萬能的了。當變量在代碼塊內用 let 聲明的時候,會形成“暫時性死區”(temporal dead zone,簡稱 TDZ),此時這個方法就沒用了,typeof 還是會報錯。

typeof x; // ReferenceError
let x;

缺點

不能準確的判斷引用類型數據的具體類型,除了函數外,其余的都是返回object

typeof {}     // "object"
typeof []     // "object"

此時,在需要判斷數組或者對象時,就不適用了。

Object.prototype.toString.call(value)

Object.prototype.toString() 方法返回對象的類型字符串,因此可以用來判斷一個值的類型。

var obj = {};
obj.toString()  // "[object Object]"

上面代碼調用空對象的toString方法,結果返回一個字符串 object Object,其中第二個Object表示該值的 構造函數

由於實例對象可能會自定義toString方法,覆蓋掉 Object.prototype.toString方法,所以為了得到類型字符串,最好直接使用Object.prototype.toString方法。通過函數的call方法,可以在任意值上調用這個方法,幫助我們判斷這個值的類型。

Object.prototype.toString.call(value)

上面代碼表示對value這個值調用Object.prototype.toString方法。

返回值

不同數據類型的Object.prototype.toString方法返回值如下:

  • 數值:返回 [object Number]
  • 字符串:返回 [object String]
  • 布爾值:返回 [object Boolean]
  • undefined:返回 [object Undefined]
  • null:返回 [object Null]
  • Symbol類型:返回 [object Symbol]
  • 數組:返回 [object Array]
  • arguments 對象:返回 [object Arguments]
  • 函數:返回 [object Function]
  • Error 對象:返回 [object Error]
  • Date 對象:返回 [object Date]
  • RegExp 對象:返回 [object RegExp]
  • 其他對象:返回 [object Object]
Object.prototype.toString.call(2)          // "[object Number]"
Object.prototype.toString.call('')         // "[object String]"
Object.prototype.toString.call(true)       // "[object Boolean]"
Object.prototype.toString.call(undefined)  // "[object Undefined]"
Object.prototype.toString.call(null)       // "[object Null]"
Object.prototype.toString.call(Symbol())   // "[object Symbol]"
Object.prototype.toString.call(Math)       // "[object Math]"
Object.prototype.toString.call({})         // "[object Object]"
Object.prototype.toString.call([])         // "[object Array]"

封裝實用函數

利用這個特性,可以封裝一個比typeof運算符更準確的類型判斷函數。

var type = function (o){
  var s = Object.prototype.toString.call(o);
  return s.match(/\[object (.*?)\]/)[1].toLowerCase();
};

type({});           // "object"
type([]);           // "array"
type(5);            // "number"
type(null);         // "null"
type();             // "undefined"
type(/dora/);       // "regexp"
type(new Date());   // "date"

在上面這個type函數的基礎上,還可以加上專門判斷某種類型數據的方法。

var dataArr = ['Null', 'Undefined', 'Object', 'Array', 'String', 'Number', 'Boolean', 'Function', 'RegExp'];
  dataArr.forEach(function (t) {
    type['is' + t] = function (o) {
      return type(o) === t.toLowerCase();
    };
  });

  type.isObject({});    // true
  type.isNumber(NaN);   // true
  type.isRegExp(/abc/); // true

instanceof 運算符

instanceof 運算符返回一個布爾值,表示對象是否為某個構造函數的實例。

function People(){}
var person = new People();
person instanceof People // true

判斷原理

遍訪對象的原型鏈上的每個原型對象,如果遍訪到這個原型對象,是某個構造函數的prototype,那麽就認為對象是這個構造函數的實例,返回true。因此同一個實例對象,可能會對多個構造函數都返回true,因為繼承的子類實例也是父類的實例。

var d = new Date();
d instanceof Date    // true
d instanceof Object  // true

特殊情況

有一種特殊情況,就是左邊對象的原型鏈上,只有null對象。這時,instanceof判斷會失真。

var obj = Object.create(null);
typeof obj              // "object"
obj instanceof Object   // false

上面代碼中,Object.create(null)返回一個新對象obj,它的原型是null。右邊的構造函數Objectprototype屬性,不在左邊的原型鏈上,因此instanceof就認為obj不是Object的實例。

只要一個對象的原型不是nullinstanceof運算符的判斷就不會失真。

類型判斷

instanceof運算符只能用於對象,不適用原始類型的值,且對於undefinednullinstanceof運算符總是返回false

'hello' instanceof String      // false
undefined instanceof Object    // false
null instanceof Object         // false

可用於對象,無論是 JavaScript 內置對象或是自定義構造函數生成的對象,都可進行判斷。

[] instanceof Array                   // true
({}) instanceof Object                // true
(function(){}) instanceof Function    // true
/a/ instanceof RegExp                 // true
new Date() instanceof Date            // true
person instanceof People              // true

constructor 屬性

prototype對象有一個constructor屬性,默認指向prototype對象所在的構造函數。由於constructor屬性定義在prototype對象上面,意味著可以被所有實例對象繼承。因此,正常情況下,所有對象實例都有一個constructor屬性,屬性值指向構造此對象實例的構造函數。

[].constructor === Array       // true
[].constructor === Object      // false
window.constructor === Window  //true

name屬性

如果不能確定對象實例的constructor屬性是什麽函數,可通過函數的name屬性,從實例得到構造函數的名稱。

function Foo() {}
var f = new Foo();
f.constructor.name // "Foo"

類型判斷

基本數據類型

nullundefined是無效的對象,因此是不會有constructor存在的,這兩種類型的數據需要通過typeof來判斷。

numberstringboolean三種數據類型有對應的NumberStringBoolean三個原生對象(包裝對象)。因此,也可用 constructor進行判斷。symbol類型也可判斷。

(333).constructor.name    // "Number"
''.constructor.name       // "String"
false.constructor.name    // "Boolean"
Symbol().constructor.name // "Symbol"

引用數據類型

JavaScript 內置對象或是自定義構造函數生成的對象,都可進行判斷。

new Date().constructor === Date   //true
[].constructor === Array          //true

function F(){};
var f = new F();
f.constructor === F          // true
f.constructor === Object     // false

不穩定因素

constructor屬性表示原型對象與構造函數之間的關聯關系,有時開發者會因業務關系重寫prototype,原有的constructor會丟失,若沒有同時修改constructor屬性,引用的時候就會出錯,constructor會默認為Object

function Person(name) {
  this.name = name;
}

Person.prototype.constructor === Person // true

Person.prototype = {
  method: function () {}
};

Person.prototype.constructor === Person // false
Person.prototype.constructor === Object // true

因此,修改原型對象時,一般要同時修改constructor屬性的指向,或者只在原型對象上添加方法,不要重寫prototype

總結

typeof
typeof 可用來判斷基本數據類型和函數,不可以對引用數據類型進行具體的判斷。

Object.prototype.toString.call(value)
Object.prototype.toString.call(value) 可用於判斷多種數據類型:基本數據類型和 JavaScript 內置對象,然而對於一些自定義構造函數生成的對象就不能進行判斷了。

instanceof
instanceof 運算符不適用判斷原始類型的值,只能用於判斷對象,無論是 JavaScript 內置對象或是自定義構造函數生成的對象,都可進行判斷。然而由於繼承的存在,instanceof 判斷也不完全準確,只能用來判斷兩個對象是否屬於原型鏈的關系,而不一定能獲取對象的具體類型。

constructor
constructor 屬性可準確的判斷對象實例是由哪個構造函數生成的,但自定義構造函數生成的對象,往往會因為重寫prototype造成constructor屬性指向不準確,因此使用的時候也要註意一下。

一些其它的具體類型判斷

判斷變量是否為對象(引用類型)

Object(x)的參數為對象時,總是返回該對象,不做轉換;當參數為原始類型時,會轉換為對應的包裝對象的實例,參數為空或者undefined或者null時,返回一個空對象。

function isObject(value) {
  return value === Object(value);
}
isObject([]); // true
isObject(true); // false

判斷是不是 NaN

所有數據類型中,只有NaN不等於它本身

function isNaN(value) {
  return value !== value;
}
isNaN(NaN);  // true

判斷數組的方法 Array.isArray()

除了上文提到的三種方法(toString()instanceofconstructor)可判斷外,還有一個Array構造函數自帶的方法isArray()
可判斷。

Array.isArray(x)

如果x是數組,則為true; 否則為false

Array.isArray([]);                // true
Array.isArray(new Array());       // true
Array.isArray(Array.prototype);   // true  鮮為人知的事實:其實 Array.prototype 也是一個數組。

使用之前需檢測一下兼容性,對於不兼容的瀏覽器可使用下面的代碼創建該方法。

if (!Array.isArray) {
  Array.isArray = function(arg) {
    return Object.prototype.toString.call(arg) === '[object Array]';
  };
}

原文地址:https://segmentfault.com/a/1190000016888845

細數判斷數據類型的各種方法