首頁  >  文章  >  web前端  >  深入理解js資料類型

深入理解js資料類型

小云云
小云云原創
2018-03-29 17:18:491429瀏覽

本文主要和大家分享深入理解js資料型,

在ECMAScript 規格中,共定義了7 種資料型別,分為基本型別兩大類,如下:

基本型別:String、Number、Boolean、Symbol、Undefined、Null 

參考類型:Object

基本型別也稱為簡單型,由於其佔據空間固定,是簡單的資料段,為了便於提升變數查詢速度,將其儲存在堆疊中,即按值存取。

參考類型也稱為複雜類型,由於其值的大小會改變,所以不能將其存放在堆疊中,否則會降低變數查詢速度,因此,其值儲存在堆(heap)中,而儲存在變數處的值,是一個指針,指向儲存物件的記憶體處,即按址存取。引用型別除 Object 外,還包括 Function 、Array、RegExp、Date 等等。

鑑於 ECMAScript 是鬆散類型的,因此需要有一種手段來偵測給定變數的資料類型。對於這個問題,JavaScript 也提供了多種方法,但遺憾的是,不同的方法得到的結果參差不齊。

以下介紹常用的4種方法,並對各個方法存在的問題進行簡單的分析。

1、typeof

typeof 是一個運算符,其右側接一個一元表達式,並傳回這個表達式的資料型別。傳回的結果以該類型的字串(全小寫字母)形式表示,包括以下 7 種:number、boolean、symbol、string、object、undefined、function 等。

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


有些時候,typeof 運算子會傳回一些令人困惑但技術上卻正確的值:

  • ##對於基本類型,除null 以外,均可傳回正確的結果。

  • 對於參考類型,除 function 以外,一律傳回 object 類型。

  • 對於 null ,傳回 object 類型。

  • 對於 function 傳回  function 類型。

其中,null 有屬於自己的資料型別Null , 引用型別中的陣列、日期、正規也都有屬於自己的具體型別,而typeof 對於這些型別的處理,只傳回了處於其原型鏈最頂端的Object 類型,沒有錯,但不是我們想要的結果。

2、instanceof

instanceof 是用來判斷A 是否為B 的實例,表達式為:A instanceof B,如果A 是B 的實例,則傳回true,否則回傳false。 這裡要特別注意的是:

instanceof 偵測的是原型,我們用一段偽程式碼來模擬其內部執行過程:

instanceof (A,B) = {
    var L = A.__proto__;
    var R = B.prototype;
    if(L === R) {
        //A的内部属性__proto__指向B的原型对象
        return true;
    }
    return false;
}


從上述過程可以看出,當A 的__proto__ 指向B 的prototype 時,就認為A 就是B 的實例,我們再來看幾個例子:

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


#我們發現,雖然instanceof 能夠判斷出[ ] 是Array的實例,但它認為[ ] 也是Object的實例,為什麼呢?

我們來分析一下 [ ]、Array、Object 三者之間的關係:

从 instanceof 能够判断出 [ ].__proto__  指向 Array.prototype,而 Array.prototype.__proto__ 又指向了Object.prototype,最终 Object.prototype.__proto__ 指向了null,标志着原型链的结束。因此,[]、Array、Object 就在内部形成了一条原型链:

从原型链可以看出,[] 的 __proto__  直接指向Array.prototype,间接指向 Object.prototype,所以按照 instanceof 的判断规则,[] 就是Object的实例。依次类推,类似的 new Date()、new Person() 也会形成一条对应的原型链 。因此instanceof 只能用来判断两个对象是否属于实例关系, 而不能判断一个对象实例具体属于哪种类型。

instanceof 操作符的问题在于,它假定只有一个全局执行环境。如果网页中包含多个框架,那实际上就存在两个以上不同的全局执行环境,从而存在两个以上不同版本的构造函数。如果你从一个框架向另一个框架传入一个数组,那么传入的数组与在第二个框架中原生创建的数组分别具有各自不同的构造函数。

var iframe = document.createElement('iframe');
document.body.appendChild(iframe);
xArray = window.frames[0].Array;
var arr =new xArray(1,2,3);// [1,2,3]
arr instanceof Array;// false


针对数组的这个问题,ES5 提供了 Array.isArray() 方法 。该方法用以确认某个对象本身是否为 Array 类型,而不区分该对象在哪个环境中创建。

if (Array.isArray(value)){
   //对数组执行某些操作
}


Array.isArray() 本质上检测的是对象的 [[Class]] 值,[[Class]] 是对象的一个内部属性,里面包含了对象的类型信息,其格式为 [object Xxx] ,Xxx 就是对应的具体类型 。对于数组而言,[[Class]] 的值就是 [object Array] 。

3、constructor

当一个函数 F被定义时,JS引擎会为F添加 prototype 原型,然后再在 prototype上添加一个 constructor 属性,并让其指向 F 的引用。如下所示:

当执行 var f = new F() 时,F 被当成了构造函数,f 是F的实例对象,此时 F 原型上的 constructor 传递到了 f 上,因此 f.constructor == F

可以看出,F 利用原型对象上的 constructor 引用了自身,当 F 作为构造函数来创建对象时,原型上的 constructor 就被遗传到了新创建的对象上, 从原型链角度讲,构造函数 F 就是新对象的类型。这样做的意义是,让新对象在诞生以后,就具有可追溯的数据类型。

同样,JavaScript 中的内置对象在内部构建时也是这样做的:

细节问题:

  • null 和 undefined 是无效的对象,因此是不会有 constructor 存在的,这两种类型的数据需要通过其他方式来判断。

  • 函数的 constructor 是不稳定的,这个主要体现在自定义对象上,当开发者重写 prototype 后,原有的 constructor 引用会丢失,constructor 会默认为 Object

为什么变成了 Object?

因为 prototype 被重新赋值的是一个 { }, { } 是 new Object() 的字面量,因此 new Object() 会将 Object 原型上的 constructor 传递给 { },也就是 Object 本身。

因此,为了规范开发,在重写对象原型时一般都需要重新给 constructor 赋值,以保证对象实例的类型不被篡改。

4、toString

toString() 是 Object 的原型方法,调用该方法,默认返回当前对象的 [[Class]] 。这是一个内部属性,其格式为 [object Xxx] ,其中 Xxx 就是对象的类型。

对于 Object 对象,直接调用 toString()  就能返回 [object Object] 。而对于其他对象,则需要通过 call / apply 来调用才能返回正确的类型信息。

Object.prototype.toString.call('') ;  // [object String]
Object.prototype.toString.call(1) ;   // [object Number]
Object.prototype.toString.call(true) ;// [object Boolean]
Object.prototype.toString.call(Symbol());//[object Symbol]
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 的引用

 相关推荐:

js数据类型详解

七种js数据类型分享

js数据类型的转换实例

以上是深入理解js資料類型的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn