JavaScript之判斷數據類型

Javascript 中的數據類型判斷實際上是一個JavaScript很是基礎問題,但不管是平常實際編程過程當中和仍是面試時,這都是一個很是常見的問題。javascript

不少人被問到如何判斷一個變量數據類型這個問題時,大概都能回答上幾種方法,可是一深刻的問題各類判斷方法的區別、優劣、侷限、原理,卻一時半會理不清楚了,因此就借這一篇文章,複習一下JavaScript中這個基礎但很容易出錯的問題。html

typeof

咱們都使用 typeof 是用來判斷數據類型的命令, 在常規的場景中足以應付數據類型判斷的需求:前端

var obj = {
   name: 'zhangxiang'
};

function foo() {
    console.log('this is a function');
}

var arr = [1,2,3];

console.log(typeof 1);  // number
console.log(typeof '1');  //string
console.log(typeof true);  //boolean
console.log(typeof null); //object
console.log(typeof undefined); //undefined
console.log(typeof obj); //object
console.log(typeof foo);  //function
console.log(typeof arr);   //object

由結果可知typeof能夠測試出numberstringbooleanundefinedfunction,而對於null數組對象,typeof均檢測出爲object,不能進一步判斷它們的類型。java

因此通常來講, typeof 會使用在比較簡單的場景, 好比你幾乎能夠肯定數據是哪一類數據而後稍微加以區分的時候.node

順便吐槽一句,typeof null === 'object' 是JavaScript的一個bug,那麼多個版本還一直不願改過來,什麼鬼...面試

instanceof

instanceof 其實適合用於判斷自定義的類實例對象, 而不是用來判斷原生的數據類型, 舉個例子:編程

// a.html
<script>
  var a = [1,2,3];
</script>
// main.html
<iframe src="a.html"></iframe>

<script>
  var frame = window.frames[0];
  var a = frame.a;
  console.log(a instanceof Array);  // false
  console.log(a.contructor === Array);  //false
  console.log(a instanceof frame.Array); // true
</script>

是什麼緣由致使上面的結果呢? 其實 iframe 之間不會共享原型鏈, 由於他們有獨立的執行環境, 因此 frame a 中的數組 a 不會是本執行環境的實例對象. 經過特性嗅探一樣不靠譜, 像經過 contructorsegmentfault

Sort, slice 等等的特有的數組(或者其餘數據類型)方法或屬性, 萬一對象中也有 sort, slice 屬性, 就會發生誤判. 因此最靠譜的方法是使用 Object.prototype.toString 方法.數組

Object.prototype.toString

在任何值上調用 Object 原生的 toString() 方法,都會返回一個 [object NativeConstructorName] 格式的字符串。
須要注意的是,可是它不能檢測非原生構造函數的構造函數名。瀏覽器

function foo(){};

Object.prototype.toString.call(1);  '[object Number]'
Object.prototype.toString.call('1'); '[object String]'
Object.prototype.toString.call(NaN); '[object Number]'
Object.prototype.toString.call(foo);  '[object Function]'
Object.prototype.toString.call([1,2,3]); '[object Array]'
Object.prototype.toString.call(undefined); '[object Undefined]'
Object.prototype.toString.call(null); '[object Null]'
Object.prototype.toString.call(true); '[object Boolean]'

每一個類在內部都有一個 [[Class]] 屬性,這個屬性中就指定了上述字符串中的構造函數名。

Object.prototype.toString 的原理是當調用的時候, 就取值內部的 [[Class]] 屬性值, 而後拼接成 '[object ' + [[Class]] + ']' 這樣的字符串並返回. 而後咱們使用 call 方法來獲取任何值的數據類型.

constructor

console.log(bool.constructor === Boolean);// true
console.log(num.constructor === Number);// true
console.log(str.constructor === String);// true
console.log(arr.constructor === Array);// true
console.log(obj.constructor === Object);// true
console.log(fun.constructor === Function);// true

console.log(haoxl.constructor === Student);// false
console.log(haoxl.constructor === Person);// true

undefined和null沒有contructor屬性

constructor不能判斷undefined和null,而且使用它是不安全的,由於contructor的指向是能夠改變的

有用的數據類型判斷函數

Array.isArray()
用於肯定傳遞的值是不是一個Array。若是對象是Array,則返回true,不然爲false。

Array.isArray([1, 2, 3]);  
// true
Array.isArray({foo: 123}); 
// false
Array.isArray("foobar");   
// false
Array.isArray(undefined);  
// false

isNaN()和Number.isNaN
isNaN()函數用來肯定一個值是否爲 NaN。
注:isNaN函數內包含一些很是有趣的規則;
你也可使用 ECMAScript 2015 中定義的Number.isNaN()來判斷。

與 JavaScript 中其餘的值不一樣, NaN不能經過相等操做符(== 和 ===)來判斷 ,由於 NaN == NaN 和 NaN === NaN 都會返回 false。 所以,isNaN 就頗有必要了。

isNaN(NaN); // true

OK, 成功了,看似很完美,可是接着看如下例子

isNaN('A String'); // true

isNaN(undefined); // true

isNaN({}); // true

會發現,很明顯不是 NaN 的 value 也被誤判成 NaN 了。

這個BUG已經存在了20年,從JavaScript最開始就一直存在。很明顯當初的設計者,在設計isNaN()的時候,侷限了在 "Not a Number" 這一字面意思上了:只要不是number就會返回 true。

因而 ES6 爲了彌補這一BUG(而不是修正,由於isNaN存在時間太長,有可能不少功能都是基於這個BUG之上的)引入了 Number.isNaN().

Number.isNaN(NaN); // true

Number.isNaN('A String'); // false

Number.isNaN(undefined); // false

Number.isNaN({}); // false

沒有ES6的狀況下,能夠採用如下polyfill

if (!Number.isNaN) {
  Number.isNaN = function(n) {
    return (
      typeof n === "number" &&
      window.isNaN( n )
    );
  };
}

判斷是不是 DOM 元素

在實際項目裏面, 有時或許咱們須要判斷是不是 DOM 元素對象, 那麼在判斷的時候利用的是 DOM 對象特有的 nodeType 屬性:

isElement: function(obj){
  return !!(obj && obj.nodeType === 1);
}

判斷是不是對象

isObject: function(obj){
  var type = typeof obj;
  return type === 'function' || typeof === 'object' && obj !== null;
}

這裏的對象是狹義的, 是一般所指的 key-value 型的集合, 或者是 function 函數而且不爲 null.

判斷是不是 arguments 對象

判斷一個對象是否是 arguments 對象能夠經過 Object.prototype.toString 來判斷, 可是低版本的瀏覽器不支持, 他們返回的是 [object Object], 因此須要兼容:

isArguments: function(obj){
  return Object.prototype.toString.call(obj) === '[object Arguments]' || (obj != null && Object.hasOwnProperty.call(obj, 'callee'));
}

兼容作法原理是經過對象的 hasOwnProperty 方法來判斷對象是否擁有 callee 屬性從而判斷是否是 arguments 對象.


參考:https://juejin.im/post/59c753...


推薦閱讀:
【專題:JavaScript進階之路】
JavaScript之深刻理解閉包
JavaScript之「use strict」
JavaScript之new運算符
JavaScript之call()理解
JavaScript之對象屬性


我是Cloudy,年輕的前端攻城獅一枚,愛專研,愛技術,愛分享。
我的筆記,整理不易,感謝閱讀、點贊和收藏。
文章有任何問題歡迎你們指出,也歡迎你們一塊兒交流前端各類問題!
相關文章
相關標籤/搜索