JavaScript的資料型別及其檢測

浪裡行舟發表於2019-02-16

一、JavaScript有幾種型別的值?

Javascript有兩種資料型別,分別是基本資料型別和引用資料型別。其中基本資料型別包括Undefined、Null、Boolean、Number、String、Symbol (ES6新增,表示獨一無二的值),而引用資料型別統稱為Object物件,主要包括物件、陣列和函式。接下來我們分別看下兩者的特點。

二、基本資料型別

1.值是不可變的

var name = `java`;
name.toUpperCase(); // 輸出 `JAVA`
console.log(name); // 輸出  `java`

由此可得,基本資料型別的值是不可改變的

2.存放在棧區

原始資料型別直接儲存在棧(stack)中的簡單資料段,佔據空間小、大小固定,屬於被頻繁使用資料,所以放入棧中儲存。

3.值的比較

var a = 1;
var b = true;
console.log(a == b);    // true
console.log(a === b);   // false

== : 只進行值的比較,會進行資料型別的轉換。
=== : 不僅進行值得比較,還要進行資料型別的比較。

三、引用資料型別

1.值是可變的

var a={age:20};
a.age=21;
console.log(a.age)//21

上面程式碼說明引用型別可以擁有屬性和方法,並且是可以動態改變的。

2.同時儲存在棧記憶體和堆記憶體

引用資料型別儲存在堆(heap)中的物件,佔據空間大、大小不固定,如果儲存在棧中,將會影響程式執行的效能;引用資料型別在棧中儲存了指標,該指標指向堆中該實體的起始地址。當直譯器尋找引用值時,會首先檢索其在棧中的地址,取得地址後從堆中獲得實體。

3.比較是引用的比較

當從一個變數向另一個變數賦引用型別的值時,同樣也會將儲存在變數中的物件的值複製一份放到為新變數分配的空間中。

var a={age:20};
var b=a;
b.age=21;
console.log(a.age==b.age)//true

上面我們講到基本型別和引用型別儲存於記憶體的位置不同,引用型別儲存在堆中的物件,與此同時,在棧中儲存了指標,而這個指標指向正是堆中實體的起始位置。變數a初始化時,a指標指向物件{age:20}的地址,a賦值給b後,b又指向該物件{age:20}的地址,這兩個變數指向了同一個物件。因此,改變其中任何一個變數,都會相互影響。

此時,如果取消某一個變數對於原物件的引用,不會影響到另一個變數。

var a={age:20};
var b=a;
a = 1;
b // {age:20}

上面程式碼中,a和b指向同一個物件,然後a的值變為1,這時不會對b產生影響,b還是指向原來的那個物件。

四、檢驗資料型別

1.typeof

typeof返回一個表示資料型別的字串,返回結果包括:number、boolean、string、symbol、object、undefined、function等7種資料型別,但不能判斷null、array等

typeof Symbol(); // symbol 有效
typeof ``; // string 有效
typeof 1; // number 有效
typeof true; //boolean 有效
typeof undefined; //undefined 有效
typeof new Function(); // function 有效
typeof null; //object 無效
typeof [] ; //object 無效
typeof new Date(); //object 無效
typeof new RegExp(); //object 無效

陣列和物件返回的都是object,這時就需要使用instanceof來判斷

2.instanceof

instanceof 是用來判斷A是否為B的例項,表示式為:A instanceof B,如果A是B的例項,則返回true,否則返回false。instanceof 運算子用來測試一個物件在其原型鏈中是否存在一個建構函式的 prototype 屬性。

[] instanceof Array; //true
{} instanceof Object;//true
new Date() instanceof Date;//true
new RegExp() instanceof RegExp//true

關於陣列的型別判斷,還可以用ES6新增Array.isArray()

Array.isArray([]);   // true

instanceof 三大弊端

  • 對於基本資料型別來說,字面量方式建立出來的結果和例項方式建立的是有一定的區別的
console.log(1 instanceof Number)//false
console.log(new Number(1) instanceof Number)//true

從嚴格意義上來講,只有例項建立出來的結果才是標準的物件資料型別值,也是標準的Number這個類的一個例項;對於字面量方式建立出來的結果是基本的資料型別值,不是嚴謹的例項,但是由於JS的鬆散特點,導致了可以使用Number.prototype上提供的方法。

  • 只要在當前例項的原型鏈上,我們用其檢測出來的結果都是true。在類的原型繼承中,我們最後檢測出來的結果未必準確
var arr = [1, 2, 3];
console.log(arr instanceof Array) // true
console.log(arr instanceof Object);  // true
function fn(){}
console.log(fn instanceof Function)// true
console.log(fn instanceof Object)// true
  • 不能檢測null 和 undefined

對於特殊的資料型別null和undefined,他們的所屬類是Null和Undefined,但是瀏覽器把這兩個類保護起來了,不允許我們在外面訪問使用

3.嚴格運算子===

只能用於判斷null和undefined,因為這兩種型別的值都是唯一的

var a = null
typeof a // "object"
a === null // true

undefined 還可以用typeof來判斷

var b = undefined;
typeof b === "undefined" // true
b === undefined // true

4.constructor

constructor作用和instanceof非常相似。但constructor檢測 Object與instanceof不一樣,還可以處理基本資料型別的檢測。

var aa=[1,2];
console.log(aa.constructor===Array);//true
console.log(aa.constructor===RegExp);//false
console.log((1).constructor===Number);//true
var reg=/^$/;
console.log(reg.constructor===RegExp);//true
console.log(reg.constructor===Object);//false 

constructor 兩大弊端

  • null 和 undefined 是無效的物件,因此是不會有 constructor 存在的,這兩種型別的資料需要通過其他方式來判斷。
  • 函式的 constructor 是不穩定的,這個主要體現在把類的原型進行重寫,在重寫的過程中很有可能出現把之前的constructor給覆蓋了,這樣檢測出來的結果就是不準確的
function Fn(){}
Fn.prototype = new Array()
var f = new Fn
console.log(f.constructor)//Array

5.Object.prototype.toString.call()

Object.prototype.toString.call() 最準確最常用的方式。首先獲取Object原型上的toString方法,讓方法執行,讓toString方法中的this指向第一個引數的值。

關於toString重要補充說明

  • 本意是轉換為字串,但是某些toString方法不僅僅是轉換為字串
  • 對於Number、String,Boolean,Array,RegExp、Date、Function原型上的toString方法都是把當前的資料型別轉換為字串的型別(它們的作用僅僅是用來轉換為字串的)
  • Object上的toString並不是用來轉換為字串的。

Object上的toString它的作用是返回當前方法執行的主體(方法中的this)所屬類的詳細資訊即”[object Object]”,其中第一個object代表當前例項是物件資料型別的(這個是固定死的),第二個Object代表的是this所屬的類是Object。

Object.prototype.toString.call(``) ;   // [object String]
Object.prototype.toString.call(1) ;    // [object Number]
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(new Function()) ; // [object Function]
Object.prototype.toString.call(new Date()) ; // [object Date]
Object.prototype.toString.call([]) ; // [object Array]
Object.prototype.toString.call(new RegExp()) ; // [object RegExp]
Object.prototype.toString.call(new Error()) ; // [object Error]
Object.prototype.toString.call(document) ; // [object HTMLDocument]
Object.prototype.toString.call(window) ; //[object global] window是全域性物件global的引用

文章於2018.10.10重新修改,希望對大家有些許幫忙!

如果覺得文章對你有些許幫助,歡迎在我的GitHub部落格點贊和關注,感激不盡!

參考資料

【文章】[[ JS 進階 ] 基本型別 引用型別 簡單賦值 物件引用](https://segmentfault.com/a/11…(推薦)

JS判斷資料型別的三種方法

JS中的資料型別及判斷

Javascript 判斷變數型別的陷阱 與 正確的處理方式

判斷JS資料型別的四種方法

相關文章