類型
基本類型:Undefined汪榔、Null眉厨、Boolean宴合、String甫菠、Number灵寺、Symbol
引用類型:Object
1. 基本類型和引用類型的區(qū)別
上面這張圖片很好的解釋了值傳遞和引用傳遞的區(qū)別脐区。
1.1 不可變性
基本類型弥搞,在 ECMAScript 標準中论巍,它們被定義為 primitive values
,即原始值地沮,代表值本身是不可被改變的。
基本類型的值被直接存儲在棧中羡亩,在變量定義時摩疑,棧就為其分配好了內(nèi)存空間。由于棧中的內(nèi)存空間的大小是固定的畏铆,那么注定了存儲在棧中的變量就是不可變的雷袋。
以字符串為例,我們在調(diào)用操作字符串的方法時,沒有任何方法是可以直接改變字符串的楷怒。對其修改也只是修改變量指向的值蛋勺,而值的本身是不課改變的。
let str = 'str'
str += '1'
console.log(str); // str1
在上面的代碼 str
是變量名鸠删,'str'
則是一個 String 類型的值抱完,通過 +=
這個操作符會將一個新的 String 類型的值 'str1'
賦值給 str
這個變量。值本身是沒有改變的刃泡。
引用類型的值實際存儲在堆內(nèi)存中巧娱,它在棧中只存儲了一個固定長度的地址,這個地址指向堆內(nèi)存中的值烘贴。因此引用類型的值是可以修改的禁添。
1.2 復制
基本類型在復制的時候,會在棧中開辟一個新的內(nèi)存空間來存儲復制出來的值桨踪。這樣以來二者指向的內(nèi)存空間完全不同老翘,這兩個變量的操作就互不影響。
當我們復制引用類型的變量時锻离,實際上復制的是棧中存儲的地址酪捡,因此復制出來的變量實際上和之前的變量指向堆中同一個對象。這樣以來操作會相互影響纳账。
1.3 比較
對于原始類型逛薇,比較時會直接比較它們的值,如果值相等疏虫,即返回 true永罚。
對于引用類型,比較時會比較它們的引用地址卧秘,即使兩個變量在堆中存儲的對象具有的屬性值都是相等的呢袱,但是它們被存儲在了不同的存儲空間,因此比較值為 false翅敌。
1.4 值傳遞和引用傳遞
所有的函數(shù)的參數(shù)都是按值傳遞的羞福。
當函數(shù)參數(shù)是值類型的時候,修改函數(shù)參數(shù)的值是不能修改外部值蚯涮。
當函數(shù)參數(shù)是引用類型時治专,我們同樣將參數(shù)復制了一個副本到局部變量,只不過復制的這個副本是指向堆內(nèi)存中的地址而已遭顶,我們在函數(shù)內(nèi)部對對象的屬性進行操作张峰,實際上和外部變量指向堆內(nèi)存中的值相同,但是這并不代表著引用傳遞棒旗,依然是值傳遞喘批。
2. Undefined
2.1 語義
表示一個變量最原始的狀態(tài),代表未定義的值,而非人為操作的結(jié)果
2.2 本質(zhì)
undefined 是全局對象的一個屬性饶深。也就是說餐曹,它是全局作用域的一個變量,并非一個關(guān)鍵字敌厘。
2.3 獲取 undefined
- 變量被聲明了台猴,但沒有賦值時,就等于 undefined额湘。
- 調(diào)用函數(shù)時卿吐,應(yīng)該提供的參數(shù)沒有提供,該參數(shù)等于 undefined锋华。
- 對象沒有賦值的屬性嗡官,該屬性的值為 undefined。
- 函數(shù)沒有返回值時(沒有寫 return 或者 return 后面沒有東西)毯焕,默認返回 undefined衍腥。
2.4 void 0 代替 undefined
void 操作符 對任何表達式求值都返回 undefined ,這和函數(shù)執(zhí)行操作后沒有返回值的作用是一樣的纳猫,JavaScript 中的函數(shù)都有返回值婆咸,當沒有 return 操作時,就默認返回一個原始的狀態(tài)值芜辕,這個值就是 undefined尚骄,表明函數(shù)的返回值未被定義。
undefined 是一個變量而不是關(guān)鍵字侵续,為了避免被篡改一般使用 void 0 來獲取 undefined 值倔丈。
3. Null
3.1 語義
表示一個對象被人為的重置為空對象,而非一個變量最原始的狀態(tài)状蜗。它只有一個值需五,即 null。
3.2 本質(zhì)
null 在內(nèi)存里的表示棧中的變量沒有指向堆中的內(nèi)存對象轧坎。當一個對象被賦值了 null 以后宏邮,原來的對象在內(nèi)存中就處于游離狀態(tài),GC 會擇機回收該對象并釋放內(nèi)存缸血。因此蜜氨,如果需要釋放某個對象,就將變量設(shè)置為 null属百,即表示該對象已經(jīng)被清空记劝,目前無效狀態(tài)。
3.3 typeof null == 'object'
typeof 會將 null 誤判為 Object 類型族扰。
這是因為數(shù)據(jù)類型在底層都是以二進制形式表示的,二進制的前三位為 0 會被 typeof 判定為對象類型。
- 000 - 對象渔呵,數(shù)據(jù)是對象的應(yīng)用
- 1 - 整型怒竿,數(shù)據(jù)是31位帶符號整數(shù)
- 010 - 雙精度類型,數(shù)據(jù)是雙精度數(shù)字
- 100 - 字符串扩氢,數(shù)據(jù)是字符串
- 110 - 布爾類型耕驰,數(shù)據(jù)是布爾值
null 值的二進制表示全是 0 ,自然前三位當然也是 000录豺,因此朦肘,typeof 會誤以為是對象類型。
3.4 null 和 undefined 的區(qū)別
null 表示無值,即該處不應(yīng)該有值双饥,也沒有指向任何對象或者任何值媒抠,而 undefined 表示缺少值,表示應(yīng)該有值,但是還沒有定義咏花,所以先指向全局變量 window.undefined 或者 undefined 來代替那個還沒有定義的值趴生。
使用 == 只判斷值的時候,null 和 undefined 是一樣的昏翰,但是使用 === 的時候苍匆,需要做類型判斷,null 的類型為 Null, undefined 的類型為 Undefined
4. Boolean
4.1 語義
表示邏輯上的真和假棚菊,它有兩個值 true 和 false
4.2 轉(zhuǎn)化
在 JavaScript 中浸踩,所有類型的值都可以轉(zhuǎn)化為與 Boolean 等價的值。轉(zhuǎn)化規(guī)則如下:
- 所有對象都被當作 true
- 空字符串被當作 false
- null 和 undefined 被當作 false
- 數(shù)字 0 和 NaN 被當作 false
- 出去 2统求,3检碗,4 之外的值類型都被當做 true
只推薦使用 Boolean() 轉(zhuǎn)換函數(shù)。
4.3 返回 Boolean 的操作符
4.3.1 關(guān)系操作符:>
球订,>=
后裸,<
,<=
當關(guān)系操作符的操作數(shù)使用了非數(shù)值時冒滩,要進行數(shù)據(jù)轉(zhuǎn)換或完成某些奇怪的操作微驶。
- 如果兩個操作數(shù)都是數(shù)值,則執(zhí)行數(shù)值比較开睡。
- 如果兩個操作數(shù)都是字符串因苹,則逐個比較兩者對應(yīng)的字符編碼(charCode),直到分出大小為止 篇恒。
- 如果操作數(shù)是其他基本類型扶檐,則調(diào)用 Number() 將其轉(zhuǎn)化為數(shù)值,然后進行比較胁艰。
- NaN 與任何值比較款筑,均返回 false 智蝠。
- 如果操作數(shù)是對象,則調(diào)用對象的 valueOf 方法(如果沒有 valueOf 奈梳,就調(diào)用 toString 方法)杈湾,最后用得到的結(jié)果,根據(jù)前面的規(guī)則執(zhí)行比較攘须。
4.3.2 相等操作符: ==
漆撞,!=
,===
于宙,!==
如果兩個操作數(shù)類型相同則 ==
和 !=
跟 ===
和 !==
是等價的浮驳。
== 和 != 操作符都會先轉(zhuǎn)換操作數(shù),然后再比較它們的相等性捞魁。
- 如果有一個操作數(shù)是布爾值至会,則在比較相等性之前,先調(diào)用 Number() 將其轉(zhuǎn)換為數(shù)值署驻;
- 如果一個操作數(shù)是字符串奋献,另一個操作數(shù)是數(shù)值,在比較相等性之前旺上,先調(diào)用 Number() 將字符串轉(zhuǎn)換為數(shù)值瓶蚂;
- 如果一個操作數(shù)是對象,另一個操作數(shù)不是宣吱,則調(diào)用對象的 valueOf() 方法窃这,用得到的基本類型值按照前面的規(guī)則進行比較;
- null 和 undefined 是相等的征候。在比較相等性之前杭攻,不能將 null 和 undefined 轉(zhuǎn)換成其他任何值。
- 如果有一個操作數(shù)是 NaN疤坝,則相等操作符返回 false兆解,而不相等操作符返回 true;
- 如果兩個操作數(shù)都是對象跑揉,則比較它們的指針地址锅睛。如果都指向同一個對象,則相等操作符返回 true历谍;否則现拒,返回 false。
===
和!==
操作符最在比較之前不轉(zhuǎn)換操作數(shù)
-
===
: 類型相同望侈,并且值相等印蔬,才返回 true ,否則返回 false 脱衙。 -
!==
: 類型不同侥猬,或者值不相等例驹,就返回 true,否則返回 false 陵究。
4.3.3 邏輯操作符: &&眠饮,||奥帘,!
邏輯與
&&
和 邏輯或||
返回的不一定是布爾值铜邮,而是包含布爾值在內(nèi)的任意類型值。
邏輯操作符屬于短路操作符 寨蹋。在進行計算之前松蒜,會先通過 Boolean() 方法將兩邊的分項轉(zhuǎn)換為布爾值,然后分別遵循下列規(guī)則進行計算:
-
&&
:從左到右檢測每一個分項已旧,返回第一個布爾值為 false 的分項秸苗,并停止檢測 。如果沒有檢測到 false 項运褪,則返回最后一個分項惊楼。 -
||
:從左到右檢測每一個分項,返回第一個布爾值為 true 的分項秸讹,并停止檢測 檀咙。如果沒有檢測到 true 項,則返回最后一個分項璃诀。
布爾操作符
!
其求值過程如下
- 對分項求值弧可,得到一個任意類型值;
- 使用 Boolean() 把該值轉(zhuǎn)換為布爾值 true 或 false劣欢;
- 對布爾值取反棕诵,即 true 變 false,false 變 true
利用
!
的取反的特點凿将,使用!!
可以很方便的將一個任意類型值轉(zhuǎn)換為布爾值
4.3.4 條件語句:if校套,while,?
- 對表達式求值牧抵,得到一個任意類型值
- 使用 Boolean() 將得到的值轉(zhuǎn)換為布爾值 true 或 false
5. String
5.1 語義
String 類型是零個或多個 16 位無符號整數(shù)值(“元素”)的所有有序序列的集合笛匙,最大長度為 2^53-1 個元素。 String 類型通常用于表示正在運行的程序中的文本數(shù)據(jù)灭忠,在這種情況下膳算,String 中的每個元素都被視為 UTF-16 代碼單元值。每個元素被視為占據(jù)序列內(nèi)的位置弛作。這些位置用非負整數(shù)索引涕蜂。第一個元素(如果有)位于索引 0,索引為 1 的下一個元素(如果有)映琳,依此類推机隙。 String 的長度是其中的元素數(shù)(即 16 位值)蜘拉。
5.2 Unicode
現(xiàn)行的字符集國際標準,字符是以 Unicode 的方式表示的有鹿,每一個 Unicode 的碼點表示一個字符旭旭,理論上, Unicode 的范圍是無限的。UTF 是 Unicode 的編碼方式葱跋,規(guī)定了碼點在計算機中的表示方法持寄,常見的有 UTF16 和 UTF8。 Unicode的碼點通常用 U+??? 來表示娱俺,其中 ??? 是十六進制的碼點值稍味。 0-65536(U+0000 - U+FFFF)的碼點被稱為基本字符區(qū)域(BMP)。
UTF-16 比較好理解,就是任何字符對應(yīng)的數(shù)字都用兩個字節(jié)來保存.我們通常對 Unicode 的誤解就是把 Unicode 與 UTF-16 等同了.但是很顯然如果都是英文字母這做有點浪費.明明用一個字節(jié)能表示一個字符為啥整兩個啊.
于是又有個 UTF-8,這里的 8 非常容易誤導人,8 不是指一個字節(jié),難道一個字節(jié)表示一個字符?實際上不是.當用 UTF-8 時表示一個字符是可變的,有可能是用一個字節(jié)表示一個字符,也可能是 兩個,三個.當然最多不能超過 3個字節(jié)了.反正是根據(jù)字符對應(yīng)的數(shù)字大小來確定.
于是 UTF-8 和 UTF-16 的優(yōu)劣很容易就看出來了.如果全部英文或英文與其他文字混合,但英文占絕大部分,用 UTF-8 就比 UTF-16 節(jié)省了很多空間.而如果全部是中文這樣類似的字符或者混合字符中中文占絕大多數(shù). UTF-16 就占優(yōu)勢了,可以節(jié)省很多空間.
5.3 String 對象
在 JavaScript 中荠卷,字符串是基本數(shù)據(jù)類型模庐,本身不存任何操作方法 。為了方便的對字符串進行操作油宜,JavaScript 提供了一個 String 類型對象:String 對象 掂碱。它是一種特殊的引用類型,JS 引擎每當讀取一個字符串的時候慎冤,就會在內(nèi)部創(chuàng)建一個對應(yīng)的 String 對象疼燥,該對象提供了很多操作字符的方法,這就是為什么能對字符串調(diào)用方法的原因 粪薛。
5.4 String 對象的常見方法
- 字符操作:charAt悴了,charCodeAt,fromCharCode
- 字符串提任ナ佟:substr湃交,substring ,slice
- 位置索引:indexOf 藤巢,lastIndexOf
- 大小寫轉(zhuǎn)換:toLowerCase搞莺,toUpperCase
- 模式匹配:match,search掂咒,replace才沧,split
- 其他操作:concat,trim绍刮,localeCompare
charCodeAt 的作用是獲取字符的 Unicode 編碼温圆,俗稱 “Unicode 碼點”。通過 charCodeAt 獲取字符的 Unicode 編碼孩革,然后再把這個編碼轉(zhuǎn)化成二進制岁歉,就可以得到該字符的二進制表示。
var a = 'a';
var code = a.charCodeAt(0); // 97
code.toString(2); // 1100001
fromCharCode 是 String 對象上的靜態(tài)方法膝蜈,作用是根據(jù) Unicode 編碼返回對應(yīng)的字符锅移。
substring(start [, end])
substring 截取字符串的某個 start 位置到某個 end 位置(但 end 位置的字符不包括在結(jié)果中)的子串熔掺,如果沒有第 2 個 end 參數(shù),直到字符串末尾非剃。參數(shù)只接受正數(shù)和 0置逻,把負數(shù)或其它無效的數(shù)當作 0。
substr(start [, length])
substr 截取字符串的某個 start 位置起备绽,數(shù) length 個長度的字符才結(jié)束券坞。如果沒有第 2 個參數(shù),直到字符串末尾疯坤”剑可以接受 “負數(shù)”,表示從字符串尾部開始計數(shù)压怠。
slice(start [, end])
slice 與 substring 基本相同,不同的是參數(shù)能接受 “負數(shù)”飞苇,表示從結(jié)尾開始計數(shù)菌瘫。
6. Number
6.1 表示方式
JavaScript 中的數(shù)字類型只有 Number 一種,Number 類型采用 IEEE754 標準中的 “雙精度浮點數(shù)” 來表示一個數(shù)字布卡,不區(qū)分整數(shù)和浮點數(shù) 雨让。所以,1與1.0是相同的忿等,是同一個數(shù)栖忠。
6.2 存儲方式
在 IEEE754 中,雙精度浮點數(shù)采用 64 位存儲贸街,即 8 個字節(jié)表示一個浮點數(shù) 庵寞。其存儲結(jié)構(gòu)如下圖所示:
指數(shù)位可以通過下面的方法轉(zhuǎn)換為使用的指數(shù)值:
6.3 數(shù)值范圍
從存儲結(jié)構(gòu)中可以看出, 指數(shù)部分的長度是11個二進制薛匪,即指數(shù)部分能表示的最大值是 2047(211-1)捐川,取中間值進行偏移,用來表示負指數(shù)逸尖,也就是說指數(shù)的范圍是 [-1023,1024] 古沥。因此,這種存儲結(jié)構(gòu)能夠表示的數(shù)值范圍為 21024 到 2-1023 娇跟,超出這個范圍的數(shù)無法表示 岩齿。21024 和 2-1023 轉(zhuǎn)換為科學計數(shù)法如下所示:
1.7976931348623157 × 10308
5 × 10-324
因此,JavaScript 中能表示的最大值是 1.7976931348623157 × 10308苞俘,最小值為 5 × 10-324 盹沈。分別對應(yīng) Number 對象的 MAX_VALUE 屬性和 MIN_VALUE 屬性。
如果一個數(shù)大于等于 2 的 1024 次方苗胀,那么就會發(fā)生“正向溢出”襟诸,即 JavaScript 無法表示這么大的數(shù)瓦堵,這時就會返回 Infinity。
如果一個數(shù)小于等于 2 的 -1075 次方(指數(shù)部分最小值 -1023歌亲,再加上小數(shù)部分的 52 位)菇用,那么就會發(fā)生為“負向溢出”,即 JavaScript 無法表示這么小的數(shù)陷揪,這時會直接返回 0惋鸥。
6.4 精度丟失
計算機中的數(shù)字都是以二進制存儲的,如果要計算 0.1 + 0.2 的結(jié)果悍缠,計算機會先把 0.1 和 0.2 分別轉(zhuǎn)化成二進制卦绣,然后相加,最后再把相加得到的結(jié)果轉(zhuǎn)為十進制 飞蚓。
但有一些浮點數(shù)在轉(zhuǎn)化為二進制時滤港,會出現(xiàn)無限循環(huán) 。這時候為了表示該數(shù)會最末尾出進行 0 舍 1 入趴拧。最終導致精度丟失溅漾。
因此非整數(shù)的 Number 類型無法用 ==
(===
也不行) 來比較,因為有可能會發(fā)生精度丟失著榴。
精度丟失解決:正確的比較浮點數(shù)的方法是添履,檢查等式左右兩邊差的絕對值是否小于最小精度。
console.log( Math.abs(0.1 + 0.2 - 0.3) <= Number.EPSILON); // true
6.5 特殊數(shù)值
- Number.MAX_VALUE:JavaScript 中的最大值
- Number.MIN_VALUE:JavaScript 中的最小值
- Number.MAX_SAFE_INTEGER:最大安全整數(shù)脑又,為 253-1
- Number.MIN_SAFE_INTEGER:最小安全整數(shù)暮胧,為 -(253-1)(在安全整數(shù)范圍內(nèi)不會出現(xiàn)精度丟失(小數(shù)除外))
- Number.POSITIVE_INFINITY:對應(yīng) Infinity,代表正無窮
- Number.NEGATIVE_INFINITY:對應(yīng) -Infinity问麸,代表負無窮
- Number.EPSILON:是一個極小的值往衷,用于檢測計算結(jié)果是否在誤差范圍內(nèi)
- Number.NaN:表示非數(shù)字,NaN 與任何值都不相等口叙,包括 NaN 本身
- Infinity:表示無窮大炼绘,分 正無窮 Infinity 和 負無窮 -Infinity
指數(shù)部分為 2^11 – 1 且小數(shù)部分全 0,這個數(shù)字是 ±∞妄田。(符號位決定正負俺亮,代表正無窮 Infinity 和 負無窮 -Infinity)
指數(shù)部分為 2^11 – 1 且小數(shù)部分非全 0,這個數(shù)字是 NaN疟呐。它不是一個值脚曾,而是一批值。這也就解釋了為什么 NaN != NaN
启具。
6.6 數(shù)值轉(zhuǎn)換
6.6.1 Number()
對于不同數(shù)據(jù)類型的轉(zhuǎn)換本讥,Number() 的處理也不盡相同,其轉(zhuǎn)換規(guī)則如下:
- 如果是 Boolean 值,true 和 false 將分別被轉(zhuǎn)換為 1 和 0拷沸。
- 如果是數(shù)字值色查,只是簡單的傳入和返回。
- 如果是 null 值撞芍,返回 0秧了。
- 如果是 undefined,返回 NaN序无。
- 如果是字符串验毡,遵循下列規(guī)則:
- 如果字符串中只包含數(shù)字(包括前面帶正號或負號的情況),則將其轉(zhuǎn)換為十進制數(shù)值;
- 如果字符串中包含有效的浮點格式帝嗡,則將其轉(zhuǎn)換為對應(yīng)的浮點數(shù)值;
- 如果字符串中包含有效的十六進制格式晶通,則將其轉(zhuǎn)換為相同大小的十進制整數(shù)值;
- 如果字符串是空的(不包含任何字符),則將其轉(zhuǎn)換為 0;
- 如果字符串中包含除上述格式之外的字符哟玷,則將其轉(zhuǎn)換為 NaN狮辽。
- 如果是對象,則調(diào)用對象的 valueOf() 方法碗降,然后依照前面的規(guī)則轉(zhuǎn)換返回的值隘竭。如果轉(zhuǎn)換的結(jié)果是 NaN,則調(diào)用對象的 toString() 方法讼渊,然后再次依照前面的規(guī)則轉(zhuǎn)換返回的字符串值。
6.6.2 parseInt() 和 parseFloat()
parseInt() 只支持 16 進制的前綴 “0x”, 不支持其他進制尊剔。而且會忽略非數(shù)字字符爪幻。
parseFloat() 直接把原字符串作為十進制來解析。
他們都不支持科學計數(shù)法表示的字符串须误。
7. Symbol
7.1 語義
Symbol 是 ES6 新增的一種原始數(shù)據(jù)類型挨稿,它的字面意思是:符號、標記京痢。代表獨一無二的值 奶甘。
7.2 初始化
和其他基本類型不同的是,Symbol 作為基本類型祭椰,沒有對應(yīng)的包裝類型臭家,也就是說 Symbol 本身不是對象,而是一個函數(shù)方淤。因此钉赁,在生成 Symbol 類型值的時候,不能使用 new 操作符 携茂。
Symbol 函數(shù)可以接受一個字符串作為參數(shù)你踩,表示對 Symbol 值的描述,當有多個 Symbol 值時,比較容易區(qū)分带膜。
有時吩谦,我們希望重新使用同一個 Symbol 值,Symbol.for 方法可以做到這一點膝藕。它接受一個字符串作為參數(shù)式廷,然后全局搜索有沒有以該參數(shù)作為名稱的 Symbol 值。如果有束莫,就返回這個 Symbol 值懒棉,否則就新建并返回一個以該字符串為名稱的 Symbol 值。
Symbol.for() 也可以生成 Symbol 值览绿,它 和 Symbol() 的區(qū)別是:
- Symbol.for() 首先會在全局環(huán)境中查找給定的 key 是否存在策严,如果存在就返回,否則就創(chuàng)建一個以 key 為標識的 Symbol 值
- Symbol.for() 生成的 Symbol 會登記在全局環(huán)境中供搜索饿敲,而 Symbol() 不會妻导。
- Symbol.for() 永遠搜索不到 用 Symbol() 產(chǎn)生的值。
7.3 Symbol 特性
Symbol 作為屬性名怀各,該屬性不會出現(xiàn)在 for...in倔韭、for...of 循環(huán)中,也不會被 Object.keys()瓢对、Object.getOwnPropertyNames()寿酌、JSON.stringify() 返回。但是硕蛹,它也不是私有屬性醇疼,有一個 Object.getOwnPropertySymbols() 方法,專門獲取指定對象的所有 Symbol 屬性名法焰。
8. Object
8.1 語義
ECMA262 把對象定義為:無序?qū)傩缘募涎砭#鋵傩钥梢园局怠ο蠡蛘吆瘮?shù)埃仪。
8.2 對象拷貝
由于引用類型的變量只存指針乙濒,而對象本身存儲在堆中 。因此卵蛉,當把一個對象賦值給多個變量時颁股,就相當于把同一個對象地址賦值給了每個變量指針 。這樣毙玻,每個變量都指向了同一個對象豌蟋,當通過一個變量修改對象,其他變量也會同步更新桑滩。
8.2.1 淺拷貝
ES6 提供了一個原生方法用于對象的拷貝梧疲,即 Object.assign() 允睹。
需要注意的是,Object.assign() 拷貝的是屬性值幌氮。當屬性值是基本類型時缭受,沒有什么問題 ,但如果該屬性值是一個指向?qū)ο蟮囊酶没ィ仓荒芸截惸莻€引用值米者,而不會拷貝被引用的那個對象。
8.2.2 深拷貝
深拷貝指的是徹底的拷貝一個對象作為副本宇智,兩者之間的操作相互不受影響蔓搞,可以通過 JSON 的序列化和反序列化方法來實現(xiàn) 。
9. 類型轉(zhuǎn)化
9.1 裝箱和拆箱
了解裝箱拆箱之前需要先了解包裝類型随橘。
Number喂分、String、Boolean机蔗、Symbol 這些基本類型都有對應(yīng)的包裝類型蒲祈。我們使用這些基本類型調(diào)用方法的時候,就會自動進行裝箱和拆箱操作萝嘁。
基本包裝類型是一種特殊的引用類型梆掸。它和普通引用類型有一個很重要的區(qū)別,就是對象的生存期不同 牙言。使用 new 操作符創(chuàng)建的引用類型的實例酸钦,在執(zhí)行流離開當前作用域之前都一直保存在內(nèi)存中。而自動創(chuàng)建的基本包裝類型的對象咱枉,則只存在于一行代碼的執(zhí)行瞬間钝鸽,然后立即被銷毀。
9.1.1 裝箱轉(zhuǎn)換:把基本類型轉(zhuǎn)換為對應(yīng)的包裝類型
裝箱轉(zhuǎn)換會在我們利用基本類型調(diào)用方法的時候自動發(fā)生庞钢。
使用內(nèi)置的 Object 函數(shù),我們可以在 JavaScript 代碼中顯式調(diào)用裝箱能力因谎。
var symbolObject = Object(Symbol("a"));
console.log(typeof symbolObject); //object
console.log(symbolObject instanceof Symbol); //true
console.log(symbolObject.constructor == Symbol); //true
裝箱機制會頻繁產(chǎn)生臨時對象基括,在一些對性能要求較高的場景下,我們應(yīng)該盡量避免對基本類型做裝箱轉(zhuǎn)換财岔。
9.1.2 拆箱操作:把引用類型轉(zhuǎn)換為基本類型
從引用類型到基本類型的轉(zhuǎn)換风皿,也就是拆箱的過程中,會遵循 ECMAScript 規(guī)范規(guī)定的 toPrimitive 原則匠璧,一般會調(diào)用引用類型的 valueOf 和 toString 方法桐款,你也可以直接重寫 toPeimitive 方法。一般轉(zhuǎn)換成不同類型的值遵循的原則不同夷恍,例如:
- 引用類型轉(zhuǎn)換為 Number 類型魔眨,先調(diào)用 valueOf,再調(diào)用 toString
- 引用類型轉(zhuǎn)換為 String 類型,先調(diào)用 toString遏暴,再調(diào)用 valueOf
若valueOf和toString都不存在侄刽,或者沒有返回基本類型,則拋出TypeError異常朋凉。
我們可以直接調(diào)用包裝類型的valueOf或toString州丹,實現(xiàn)拆箱操作。
9.2 隱式類型轉(zhuǎn)換
9.2.1 規(guī)則
9.2.2 各種運算符
我們在對各種非 Number 類型運用數(shù)學運算符 (- * /)
時杂彭,會先將非Number類型轉(zhuǎn)換為 Number 類型;
注意 +
是個例外墓毒,執(zhí)行 +
操作符時:
- 1.當一側(cè)為 String 類型,被識別為字符串拼接亲怠,并會優(yōu)先將另一側(cè)轉(zhuǎn)換為字符串類型所计。
- 2.當一側(cè)為 Number 類型,另一側(cè)為原始類型赁炎,則將原始類型轉(zhuǎn)換為 Number 類型醉箕。
- 3.當一側(cè)為 Number 類型,另一側(cè)為引用類型徙垫,將引用類型和 Number 類型轉(zhuǎn)換成字符串后拼接讥裤。
其他運算符涉及的類型轉(zhuǎn)換可以查看上面 4.3 的相關(guān)總結(jié)。
10. 判斷數(shù)據(jù)類型
10.1 typeof
typeof 是一個操作符姻报,其右側(cè)跟一個一元表達式己英,并返回這個表達式的數(shù)據(jù)類型。返回的結(jié)果用該類型的字符串(全小寫字母)形式表示吴旋。
有些時候损肛,typeof 操作符會返回一些令人迷惑但技術(shù)上卻正確的值:
- 對于基本類型,除 null 以外荣瑟,均可以返回正確的結(jié)果治拿。
- 對于引用類型,除 function 以外笆焰,一律返回 object 類型。
- 對于 null 嚷掠,返回 object 類型狠轻。
- 對于 function 返回 function 類型檐蚜。
對于 null 來說這屬于最初的設(shè)計缺陷導致的。
10.2 instanceof
instanceof 是用來判斷 A 是否為 B 的實例鲫骗,表達式為:A instanceof B悲雳,如果 A 是 B 的實例挎峦,則返回 true,否則返回 false坦胶。
instanceof 只能用來判斷兩個對象是否屬于實例關(guān)系, 而不能判斷一個對象實例具體屬于哪種類型晴楔。
instanceof 操作符的還有一個問題就是:它假定只有一個全局執(zhí)行環(huán)境顿苇。如果網(wǎng)頁中包含多個框架,那實際上就存在兩個以上不同的全局執(zhí)行環(huán)境税弃,從而存在兩個以上不同版本的構(gòu)造函數(shù)纪岁。如果你從一個框架向另一個框架傳入一個數(shù)組,那么傳入的數(shù)組與在第二個框架中原生創(chuàng)建的數(shù)組分別具有各自不同的構(gòu)造函數(shù)则果,這時候 instanceof 就會返回 false幔翰。
10.3 toString
toString() 是 Object 的原型方法,調(diào)用該方法西壮,默認返回當前對象的 [[Class]]
遗增。這是一個內(nèi)部屬性,其格式為 [object Xxx] 款青,其中 Xxx 就是對象的類型做修。
對于 Object 對象,直接調(diào)用 toString() 就能返回 [object Object] 抡草。而對于其他對象饰及,則需要通過 call / apply 來調(diào)用才能返回正確的類型信息。
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]
這種方式是被很多人推薦使用的康震。