摘要:強(qiáng)制類型轉(zhuǎn)換作為程序員,你一定獲取過當(dāng)前系統(tǒng)的時間戳。比如對于變量而言,此次強(qiáng)制類型轉(zhuǎn)換是隱式的。然而則是非常典型的顯式強(qiáng)制類型轉(zhuǎn)換。隱式強(qiáng)制類型轉(zhuǎn)換大部分被詬病的強(qiáng)制類型轉(zhuǎn)換都是隱式強(qiáng)制類型轉(zhuǎn)換。
JavaScript 強(qiáng)制類型轉(zhuǎn)換
作為 JavaScript 程序員,你一定獲取過當(dāng)前系統(tǒng)的時間戳。在 ES5 引入 Date.now() 靜態(tài)方法之前,下面這段代碼你一定不會陌生:
var timestamp = +new Date(); // timestamp 就是當(dāng)前的系統(tǒng)時間戳,單位是 ms
你肯定聽說過 JavaScript 的強(qiáng)制類型轉(zhuǎn)換,你能指出這段代碼里哪里用到了強(qiáng)制類型轉(zhuǎn)換嗎?
幾乎所有 JavaScript 程序員都接觸過強(qiáng)制類型轉(zhuǎn)換 —— 不論是有意的還是無意的。強(qiáng)制類型轉(zhuǎn)換導(dǎo)致了很多隱蔽的 BUG,但是強(qiáng)制類型轉(zhuǎn)換同時也是一種非常有用的技術(shù),我們不應(yīng)該因噎廢食。
在本文中我們來詳細(xì)探討一下 JavaScript 的強(qiáng)制類型轉(zhuǎn)換,以便我們可以在避免踩坑的情況下最大化利用強(qiáng)制類型轉(zhuǎn)換的便捷。
類型轉(zhuǎn)換和強(qiáng)制類型轉(zhuǎn)換類型轉(zhuǎn)換發(fā)生在靜態(tài)類型語言的編譯階段,而強(qiáng)制類型轉(zhuǎn)換發(fā)生在動態(tài)類型語言的運(yùn)行時(runtime),因此在 JavaScript 中只有強(qiáng)制類型轉(zhuǎn)換。
強(qiáng)制類型轉(zhuǎn)換一般還可分為 隱式強(qiáng)制類型轉(zhuǎn)換(implicit coercion)和 _顯式強(qiáng)制類型轉(zhuǎn)換(explicit coercion)_。
從代碼中可以看出轉(zhuǎn)換操作是隱式的還是顯式的,顯式強(qiáng)制類型轉(zhuǎn)換很容易就能看出來,而隱式強(qiáng)制類型轉(zhuǎn)換可能就沒有這么明顯了。
比如:
var a = 21; var b = a + ""; var c = String(a);
對于變量 b 而言,此次強(qiáng)制類型轉(zhuǎn)換是隱式的。+ 操作符在其中一個操作數(shù)是字符串時進(jìn)行的是字符串拼接操作,因此數(shù)字 21 會被轉(zhuǎn)換為相應(yīng)的字符串 "21"。
然而 String(21) 則是非常典型的顯式強(qiáng)制類型轉(zhuǎn)換。
這兩種強(qiáng)制轉(zhuǎn)換類型的操作都是將數(shù)字轉(zhuǎn)換為字符串。
不過“顯式”還是“隱式”都是相對而言的。比如如果你知道 a + "" 是怎么回事,那么對你來說這可能就是“顯式”的。反之,如果你不知道 String(a) 可以用來字符串強(qiáng)制類型轉(zhuǎn)換,那么它對你來說可能就是“隱式”的。
抽象值操作在介紹強(qiáng)制類型轉(zhuǎn)換之前,我們需要先了解一下字符串、數(shù)字和布爾值之間類型轉(zhuǎn)換的基本規(guī)則。在 ES5 規(guī)范中定義了一些“抽象操作”和轉(zhuǎn)換規(guī)則,在這我們介紹一下 ToPrimitive、ToString、ToNumber 和 ToBoolean。注意,這些操作僅供引擎內(nèi)部使用,和平時 JavaScript 代碼中的 .toString() 等操作不一樣。
ToPrimitive你可以將 ToPrimitive 操作看作是一個函數(shù),它接受一個 input 參數(shù)和一個可選的 PreferredType 參數(shù)。ToPrimitive 抽象操作會將 input 參數(shù)轉(zhuǎn)換成一個原始值。如果一個對象可以轉(zhuǎn)換成不止一種原始值,可以使用 PreferredType 指定抽象操作的返回類型。
根據(jù)不同的輸入類型,ToPrimitive 的轉(zhuǎn)換操作如下:
輸入類型 | 操作 / 返回值 |
---|---|
Undefined | 自身(無轉(zhuǎn)換操作) |
Null | 自身(無轉(zhuǎn)換操作) |
Boolean | 自身(無轉(zhuǎn)換操作) |
Number | 自身(無轉(zhuǎn)換操作) |
String | 自身(無轉(zhuǎn)換操作) |
Object | 返回 Object 的 default value。Object 的 default value 通過在該對象上傳遞 PreferredType 參數(shù)給內(nèi)部操作 [[DefaultValue]](hint) 獲得。[[DefaultValue]](hint) 的實(shí)現(xiàn)請往下看。 |
在對象 O 上調(diào)用內(nèi)部操作 [[DefaultValue]] 時,根據(jù) hint 的不同,其執(zhí)行的操作也不同,簡化版(具體可參考 ES5 規(guī)范 8.12.8 節(jié))如下:
ToString
如果 hint 是 String;
如果 O 的 toString 屬性是函數(shù);
將 O 設(shè)置為 this 值并調(diào)用 toString 方法,將返回值賦值給 val;
如果 val 是原始值類型則返回;
如果 O 的 valueOf 屬性是函數(shù);
將 O 設(shè)置為 this 值并調(diào)用 valueOf 方法,將返回值賦值給 val;
如果 val 是原始值類型則返回;
拋出 TypeError 錯誤。
如果 hint 是 Number;
如果 O 的 valueOf 屬性是函數(shù);
將 O 設(shè)置為 this 值并調(diào)用 valueOf 方法,將返回值賦值給 val;
如果 val 是原始值類型則返回;
如果 O 的 toString 屬性是函數(shù);
將 O 設(shè)置為 this 值并調(diào)用 toString 方法,將返回值賦值給 val;
如果 val 是原始值類型則返回;
拋出 TypeError 錯誤。
如果 hint 參數(shù)為空;
如果 O 是 Date 對象,則和 hint 為 String 時一致;
否則和 hint 為 Number 時一致。
原始值的字符串化的規(guī)則如下:
null 轉(zhuǎn)化為 "null";
undefined 轉(zhuǎn)化為 "undefined";
true 轉(zhuǎn)化為 "true";
false 轉(zhuǎn)化為 "false";
數(shù)字的字符串化遵循通用規(guī)則,如 21 轉(zhuǎn)化為 "21",極大或者極小的數(shù)字使用指數(shù)形式,如:
var num = 3.912 * Math.pow(10, 50); num.toString(); // "3.912e50"
對于普通對象,如果對象有自定義的 toString() 方法,字符串化時就會調(diào)用該自定義方法并使用其返回值,否則返回的是內(nèi)部屬性 [[Class]] 的值,比如 "object [Object]"。需要注意的是,數(shù)組默認(rèn)的 toString() 方法經(jīng)過了重新定義,其會將所有元素字符串化之后再用 "," 連接起來,如:
var arr = [1, 2, 3]; arr.toString(); // "1,2,3"ToNumber
在 ES5 規(guī)范中定義的 ToNumber 操作可以將非數(shù)字值轉(zhuǎn)換為數(shù)字。其規(guī)則如下:
true 轉(zhuǎn)換為 1;
false 轉(zhuǎn)換為 0;
undefined 轉(zhuǎn)換為 NaN;
null 轉(zhuǎn)換為 0;
針對字符串的轉(zhuǎn)換基本遵循數(shù)字常量的相關(guān)規(guī)則。處理失敗則返回 NaN。
對象會先被轉(zhuǎn)換為原始值,如果返回的是非數(shù)字的原始值,則再遵循上述規(guī)則將其強(qiáng)制轉(zhuǎn)換為數(shù)字。
在將某個值轉(zhuǎn)換為原始值的時候,會首先執(zhí)行抽象操作 ToPrimitive,如果結(jié)果是數(shù)字則直接返回,如果是字符串再根據(jù)相應(yīng)規(guī)則轉(zhuǎn)換為數(shù)字。
參照上述規(guī)則,現(xiàn)在我們可以一步一步來解釋本文開頭的那行代碼了。
var timestamp = +new Date(); // timestamp 就是當(dāng)前的系統(tǒng)時間戳,單位是 ms
其執(zhí)行步驟如下:
new 操作符比 + 操作符優(yōu)先級更高,因此先執(zhí)行 new Date() 操作,生成一個新的 Date 實(shí)例;
一元操作符 + 在其操作數(shù)為非數(shù)字時,會對其進(jìn)行隱式強(qiáng)制類型轉(zhuǎn)換為數(shù)字:
hint 是 Number;
Date 實(shí)例的 valueOf 屬性指向的是 Date.prototype.valueOf,是一個函數(shù);
將 this 指向 Date 實(shí)例并調(diào)用 valueOf 函數(shù),獲得返回值;
返回值是一個數(shù)字,直接將其返回;
將隱式強(qiáng)制類型轉(zhuǎn)換獲得的值賦值給 timestamp 變量。
有了以上知識,我們就可以實(shí)現(xiàn)一些比較好玩的東西了,比如將數(shù)字和對象相加:
var a = { valueOf: function() { return 18; } }; var b = 20; +a; // 18 Number(a); // 18 a + b; // 38 a - b; // -2
順帶提一下,從 ES5 開始,使用 Object.create(null) 創(chuàng)建的對象,其 [[Prototype]] 屬性為 null 因此沒有 valueOf() 和 toString() 方法,因此無法進(jìn)行強(qiáng)制類型轉(zhuǎn)換。請看如下示例:
var a = {}; var b = Object.create(null); +a; // NaN +b; // Uncaught TypeError: Cannot convert object to primitive value a + ""; // "[object Object]" b + ""; // Uncaught TypeError: Cannot convert object to primitive valueToBoolean
JavaScript 中有兩個關(guān)鍵字 true 和 false,分別表示布爾類型的真和假。我們經(jīng)常會在 if 語句中將 0 作為假值條件,1 作為真值條件,這也利用了強(qiáng)制類型轉(zhuǎn)換。我們可以將 true 強(qiáng)制類型轉(zhuǎn)換為 1,false 強(qiáng)制類型轉(zhuǎn)換為 0,反之亦然。然而 true 和 1 并不是一回事,false 和 0 也一樣。
假值在 JavaScript 中值可以分為兩類:
可以被強(qiáng)制類型轉(zhuǎn)換為 false 的值
其他(被強(qiáng)制類型轉(zhuǎn)換為 true 的值)
在 ES5 規(guī)范中下列值被定義為假值:
undefined
null
false
+0、-0 和 NaN
""
假值的布爾強(qiáng)制類型轉(zhuǎn)換結(jié)果為 false。
在假值列表以外的值都是真值。
?? 例外規(guī)則難免有例外。剛說了除了假值列表以外的所有其他值都是真值,然而你可以在現(xiàn)代瀏覽器的控制臺中執(zhí)行下面幾行代碼試試:
Boolean(document.all); typeof document.all;
得到的結(jié)果應(yīng)該是 false 和 "undefined"。然而如果你直接執(zhí)行 document.all 得到的是一個類數(shù)組對象,包含了頁面中所有的元素。document.all 實(shí)際上不能算是 JavaScript 語言的范疇,這是瀏覽器在特定條件下創(chuàng)建一些外來(exotic)值,這些就是“假值對象”。
假值對象看起來和普通對象并無二致(都有屬性,document.all 甚至可以展為數(shù)組),但是其強(qiáng)制類型轉(zhuǎn)換的結(jié)果卻是 false。
在 ES5 規(guī)范中,document.all 是唯一一個例外,其原因主要是為了兼容性。因?yàn)槔洗a可能會這么判斷是否是 IE:
if (document.all) { // Internet Explorer }
在老版本的 IE 中,document.all 是一個對象,其強(qiáng)制類型轉(zhuǎn)換結(jié)果為 true,而在現(xiàn)代瀏覽器中,其強(qiáng)制轉(zhuǎn)換結(jié)果為 false。
真值除了假值以外都是真值。
比如:
var a = "false"; var b = "0"; var c = """"; var d = Boolean(a && b && c); d; // ?
d 是 true 還是 false 呢?
答案是 true。這些值都是真值,相信不需要過多分析。
同樣,以下幾個值一樣都是真值:
var a = []; var b = {}; var c = function() {};顯式強(qiáng)制類型轉(zhuǎn)換
顯式強(qiáng)制類型轉(zhuǎn)換非常常見,也不會有什么坑,JavaScript 中的顯式類型轉(zhuǎn)換和靜態(tài)語言中的很相似。
字符串和數(shù)字之間的顯式轉(zhuǎn)換字符串和數(shù)字之間的相互轉(zhuǎn)換靠 String() 和 Number() 這兩個內(nèi)建函數(shù)實(shí)現(xiàn)。注意在調(diào)用時沒有 new 關(guān)鍵字,只是普通函數(shù)調(diào)用,不會創(chuàng)建一個新的封建對象。
var a = 21; var b = "2.71828"; var c = String(a); var d = Number(b); c; // "21" d; // 2.71828
除了直接調(diào)用 String() 或者 Number() 方法之外,還可以通過別的方式顯式地進(jìn)行數(shù)字和字符串之間的相互轉(zhuǎn)換:
var a = 21; var b = "2.71828"; var c = a.toString(); var d = +b; c; // "21" d; // 2.71828
雖然 a.toString() 看起來很像顯式的,然而其中涉及了隱式轉(zhuǎn)換,因?yàn)?21 這樣的原始值是沒有方法的,JavaScript 自動創(chuàng)建了一個封裝對象,并調(diào)用了其 toString() 方法。
+b 中的 + 是一元運(yùn)算符,+ 運(yùn)算符會將其操作數(shù)轉(zhuǎn)換為數(shù)字。而 +b 是顯式還是隱式就取決于開發(fā)者自身了,本文之前也提到過,顯式還是隱式都是相對的。
顯式轉(zhuǎn)換為布爾值和字符串與數(shù)字之間的相互轉(zhuǎn)換一樣,Boolean() 可以將參數(shù)顯示強(qiáng)制轉(zhuǎn)換為布爾值:
var a = ""; var b = 0; var c = null; var d = undefined; var e = "0"; var f = []; var g = {}; Boolean(a); // false Boolean(b); // false Boolean(c); // false Boolean(d); // false Boolean(e); // true Boolean(f); // true Boolean(g); // true
不過我們很少會在代碼中直接用 Boolean() 函數(shù),更常見的是用 !! 來強(qiáng)制轉(zhuǎn)換為布爾值,因?yàn)榈谝粋€ ! 會將操作數(shù)強(qiáng)制轉(zhuǎn)換為布爾值,并反轉(zhuǎn)(真值反轉(zhuǎn)為假值,假值反轉(zhuǎn)為真值),而第二個 ! 會將結(jié)果反轉(zhuǎn)回原值:
var a = ""; var b = 0; var c = null; var d = undefined; var e = "0"; var f = []; var g = {}; !!a; // false !!b; // false !!c; // false !!d; // false !!e; // true !!f; // true !!g; // true
不過更常見的情況是類似 if(...) {} 這樣的代碼,在這個上下文中,如果我們沒有使用 Boolean() 或者 !! 轉(zhuǎn)換,就會自動隱式地進(jìn)行 ToBoolean 轉(zhuǎn)換。
三元運(yùn)算符也是一個很常見的布爾隱式強(qiáng)制類型轉(zhuǎn)換的例子:
var a = 21; var b = "hello"; var c = false; var d = a ? b : c; d; // "hello"
在執(zhí)行三元運(yùn)算的時候,先對 a 進(jìn)行布爾強(qiáng)制類型轉(zhuǎn)換,然后根據(jù)結(jié)果返回 : 前后的值。
隱式強(qiáng)制類型轉(zhuǎn)換大部分被詬病的強(qiáng)制類型轉(zhuǎn)換都是隱式強(qiáng)制類型轉(zhuǎn)換。但是隱式強(qiáng)制類型轉(zhuǎn)換真的一無是處嗎?并不一定,引擎在一定程度上簡化了強(qiáng)制類型轉(zhuǎn)換的步驟,這對于有些情況來說并不是好事,而對于另一些情況來說可能并不一定是壞事。
字符串和數(shù)字之間的隱式強(qiáng)制類型轉(zhuǎn)換在上一節(jié)我們已經(jīng)介紹了字符串和數(shù)字之間的顯式強(qiáng)制類型轉(zhuǎn)換,在這一節(jié)我們來說說他們兩者之間的隱式強(qiáng)制類型轉(zhuǎn)換。
+ 運(yùn)算符既可以用作數(shù)字之間的相加也可以通過重載用于字符串拼接。我們可能覺得如果 + 運(yùn)算符兩邊的操作數(shù)有一個或以上是字符串就會進(jìn)行字符串拼接。這種想法并不完全錯誤,但也不是完全正確的。比如以下代碼可以驗(yàn)證這句話是正確的:
var a = 21; var b = 4; var c = "21"; var d = "4"; a + b; // 25 c + d; // "214"
但是如果 + 運(yùn)算符兩邊的操作數(shù)不是字符串呢?
var arr0 = [1, 2]; var arr1 = [3, 4]; arr0 + arr1; // ???
上面這條命令的執(zhí)行結(jié)果是 "1,23,4"。a 和 b 都不是字符串,為什么 JavaScript 會把 a 和 b 都轉(zhuǎn)換為字符串再進(jìn)行拼接?
根據(jù) ES5 規(guī)范 11.6.1 節(jié),如果 + 兩邊的操作數(shù)中,有一個操作數(shù)是字符串或者可以通過以下步驟轉(zhuǎn)換為字符串,+ 運(yùn)算符將進(jìn)行字符串拼接操作:
如果一個操作數(shù)為對象,則對其調(diào)用 ToPrimitive 抽象操作;
ToPrimitive 抽象操作會調(diào)用 [[DefaultValue]](hint),其中 hint 為 Number。
這個操作和上面所述的 ToNumber 操作一致,不再重復(fù)。
在這個操作中,JavaScript 引擎對其進(jìn)行 ToPrimitive 抽象操作的時候,先執(zhí)行 valueOf() 方法,但是由于其 valueOf() 方法返回的是數(shù)組,無法得到原始值,轉(zhuǎn)而調(diào)用 toString() 方法,toString() 方法返回了以 , 拼接的所有元素的字符串,即 1,2 和 3,4,+ 運(yùn)算符再進(jìn)行字符串拼接,得到結(jié)果 1,23,4。
簡單來說,只要 + 的操作數(shù)中有一個是字符串,或者可以通過上述步驟得到字符串,就進(jìn)行字符串拼接操作;其余情況執(zhí)行數(shù)字加法。
所以以下這段代碼可謂隨處可見:
var a = 21; a + ""; // "21"
利用隱式強(qiáng)制類型轉(zhuǎn)換將非字符串轉(zhuǎn)換為字符串,這樣轉(zhuǎn)換非常方便。不過通過 a + "" 和直接調(diào)用 String(a) 之間并不是完全一樣,有些細(xì)微的差別需要注意一下。a + "" 會對 a 調(diào)用 valueOf() 方法,然后再通過上述的 ToString 抽象操作轉(zhuǎn)換為字符串。而 String(a) 則會直接調(diào)用 toString()。
雖然返回值都是字符串,然而如果 a 是對象的話,結(jié)果可能出乎意料!
比如:
var a = { valueOf: function() { return "21"; }, toString: function() { return "6"; } }; a + ""; // "42" String(a); // "6"
不過大部分情況下也不會寫這么奇怪的代碼,如果你真的要擴(kuò)展 valueOf() 或者 toString() 方法的話,請留意一下,因?yàn)槟憧赡軣o意間影響了強(qiáng)制類型轉(zhuǎn)換的結(jié)果。
那么從字符串轉(zhuǎn)換為數(shù)字呢?請看下面的例子:
var a = "2.718"; var b = a - 0; b; // 2.718
由于 - 操作符不像 + 操作符有重載,- 只能進(jìn)行數(shù)字減法操作,因此如果操作數(shù)不是數(shù)字的話會被強(qiáng)制轉(zhuǎn)換為數(shù)字。當(dāng)然,a * 1 和 a / 1 也可以,因?yàn)檫@兩個運(yùn)算符也只能用于數(shù)字。
把 - 用于對象會怎么樣呢?比如:
var a = [3]; var b = [1]; a - b; // 2
- 只能執(zhí)行數(shù)字減法,因此會對操作數(shù)進(jìn)行強(qiáng)制類型轉(zhuǎn)換為數(shù)字,根據(jù)前面所述的步驟,數(shù)組會調(diào)用其 toString() 方法獲得字符串,然后再轉(zhuǎn)換為數(shù)字。
布爾值到數(shù)字的隱式強(qiáng)制類型轉(zhuǎn)換假設(shè)現(xiàn)在你要實(shí)現(xiàn)這么一個函數(shù),在它的三個參數(shù)中,如果有且只有一個參數(shù)為真值則返回 true,否則返回 false,你該怎么寫?
簡單一點(diǎn)的寫法:
function onlyOne(x, y, z) { return !!((x && !y && !z) || (!x && y && !z) || (!x && !y && z)); } onlyOne(true, false, false); // true onlyOne(true, true, false); // false onlyOne(false, false, true); // true
三個參數(shù)的時候代碼好像也不是很復(fù)雜,那如果是 20 個呢?這么寫肯定過于繁瑣了。我們可以用強(qiáng)制類型轉(zhuǎn)換來簡化代碼:
function onlyOne(...args) { return ( args.reduce( (accumulator, currentValue) => accumulator + !!currentValue, 0 ) === 1 ); } onlyOne(true, false, false, false); // true onlyOne(true, true, false, false); // false onlyOne(false, false, false, true); // true
在上面這個改良版的函數(shù)中,我們使用了數(shù)組的 reduce() 方法來計算所有參數(shù)中真值的數(shù)量,先使用隱式強(qiáng)制類型轉(zhuǎn)換把參數(shù)轉(zhuǎn)換成 true 或者 false,再通過 + 運(yùn)算符將 true 或者 false 隱式強(qiáng)制類型轉(zhuǎn)換成 1 或者 0,最后的結(jié)果就是參數(shù)中真值的個數(shù)。
通過這種改良版的代碼,我們可以很簡單的寫出 onlyTwo()、onlyThree() 的函數(shù),只需要改一個數(shù)字就好了。這無疑是一個很大的提升。
隱式強(qiáng)制類型轉(zhuǎn)換為布爾值在以下情況中會發(fā)生隱式強(qiáng)制類型轉(zhuǎn)換:
if (...) 語句中的條件判斷表達(dá)式;
for (..; ..; ..) 語句中的條件判斷表達(dá)式,也就是第二個;
while (..) 和 do..while(..) 循環(huán)中的條件判斷表達(dá)式;
.. ? .. : .. 三元表達(dá)式中的條件判斷表達(dá)式,也就是第一個;
邏輯或 || 和邏輯與 && 左邊的操作數(shù),作為條件判斷表達(dá)式。
在這些情況下,非布爾值會通過上述的 ToBoolean 抽象操作被隱式強(qiáng)制類型轉(zhuǎn)換為布爾值。
|| 和 &&JavaScript 中的邏輯或和邏輯與運(yùn)算符和其他語言中的不太一樣。在別的語言中,其返回值類型是布爾值,然而在 JavaScript 中返回值是兩個操作數(shù)之一。因此在 JavaScript 中,|| 和 && 被稱作選擇器運(yùn)算符可能更合適。
根據(jù) ES5 規(guī)范 11.11 節(jié):
|| 和 && 運(yùn)算符的返回值不一定是布爾值,而是兩個操作數(shù)中的其中一個。
比如:
var a = 21; var b = "xyz"; var c = null; a || b; // 21 a && b; // "xyz" c || b; // "xyz" c && b; // null
如果 || 或者 && 左邊的操作數(shù)不是布爾值類型的話,則會對左邊的操作數(shù)進(jìn)行 ToBoolean 操作,根據(jù)結(jié)果返回運(yùn)算符左邊或者右邊的操作數(shù)。
對于 || 來說,左邊操作數(shù)的強(qiáng)制類型轉(zhuǎn)換結(jié)果如果為 true 則返回運(yùn)算符左邊的操作數(shù),如果是 false 則返回運(yùn)算符右邊的操作數(shù)。
對于 && 來說則剛好相反,左邊的操作數(shù)強(qiáng)制類型轉(zhuǎn)換結(jié)果如果為 true 則返回運(yùn)算符右邊的操作數(shù),如果是 false 則返回運(yùn)算符左邊的操作數(shù)。
|| 和 && 返回的是兩個操作數(shù)之一,而非布爾值。
在 ES6 的函數(shù)默認(rèn)參數(shù)出現(xiàn)之前,我們經(jīng)常會看到這樣的代碼:
function foo(x, y) { x = x || "x"; y = y || "y"; console.log(x + " " + y); } foo(); // "x y" foo("hello"); // "hello y"
看起來和我們預(yù)想的一致。但是,如果是這樣調(diào)用呢?
foo("hello world", ""); // ???
上面的執(zhí)行結(jié)果是 hello world y,為什么?
在執(zhí)行到 y = y || "y" 的時候,JavaScript 對運(yùn)算符左邊的操作數(shù)進(jìn)行了布爾隱式強(qiáng)制類型轉(zhuǎn)換,其結(jié)果為 false,因此運(yùn)算結(jié)果為運(yùn)算符右邊的操作數(shù),即 "y",因此最后打印出來到日志是 "hello world y" 而非我們預(yù)想的 hello world。
所以這種方式需要確保傳入的參數(shù)不能有假值,否則就可能和我們預(yù)想的不一致。如果參數(shù)中可能存在假值,則應(yīng)該有更加明確的判斷。
如果你看過壓縮工具處理后的代碼的話,你可能經(jīng)常會看到這樣的代碼:
function foo() { // 一些代碼 } var a = 21; a && foo(); // a 為假值時不會執(zhí)行 foo()
這時候 && 就被稱為守護(hù)運(yùn)算符(guard operator),即 && 左邊的條件判斷表達(dá)式結(jié)果如果不是 true 則會自動終止,不會判斷操作符右邊的表達(dá)式。
所以在 if 或者 for 語句中我們使用 || 和 && 的時候,if 或者 for 語句會先對 || 和 && 操作符返回的值進(jìn)行布爾隱式強(qiáng)制類型轉(zhuǎn)換,再根據(jù)轉(zhuǎn)換結(jié)果來判斷。
比如:
var a = 21; var b = null; var c = "hello"; if (a && (b || c)) { console.log("hi"); }
在這段代碼中,a && (b || c) 的結(jié)果實(shí)際是 "hello" 而非 true,然后 if 再通過隱式類型轉(zhuǎn)換為 true 才執(zhí)行 console.log("hi")。
Symbol 的強(qiáng)制類型轉(zhuǎn)換ES6 中引入了新的基本數(shù)據(jù)類型 —— Symbol。然而它的強(qiáng)制類型轉(zhuǎn)換有些不一樣,它支持顯式強(qiáng)制類型轉(zhuǎn)換,但是不支持隱式強(qiáng)制類型轉(zhuǎn)換。
比如:
var s = Symbol("hi"); String(s); // "Symbol(hi)" s + ""; // Uncaught TypeError: Cannot convert a Symbol value to a string
而且 Symbol 不能強(qiáng)制轉(zhuǎn)換為數(shù)字,比如:
var s = Symbol("hi"); s - 0; // Uncaught TypeError: Cannot convert a Symbol value to a number
Symbol 的布爾強(qiáng)制類型轉(zhuǎn)換都是 true。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/94453.html
摘要:如果有并且返回基本類型值,再使用該值進(jìn)行強(qiáng)制類型轉(zhuǎn)換。四寬松相等和嚴(yán)格相等允許在相等比較中進(jìn)行強(qiáng)制類型轉(zhuǎn)換,而不允許。這時最好用來避免不經(jīng)意的強(qiáng)制類型轉(zhuǎn)換。這兩個原則可以讓我們避開幾乎所有強(qiáng)制類型轉(zhuǎn)換的坑。 一、類型轉(zhuǎn)換規(guī)則 1.ToString 對于普通對象來說,除非自行定義toString方法,否則就會調(diào)用Object.prototype.toString()方法,如果對象有自己的...
摘要:顯示的調(diào)用轉(zhuǎn)換過程稱為顯式強(qiáng)制類型轉(zhuǎn)換,隱式的情況稱為隱式強(qiáng)制類型轉(zhuǎn)換。隱式強(qiáng)制類型轉(zhuǎn)換讓代碼變得晦澀難懂而又便捷而奇妙。事實(shí)上,允許在比較中進(jìn)行強(qiáng)制類型轉(zhuǎn)換,而不允許。如果有并且返回基本類型值,就使用該值進(jìn)行強(qiáng)制類型轉(zhuǎn)換。 JavaScript是一種非常靈活的現(xiàn)代編程語言,靈活到使用者極其容易被忽視的它那廣闊的世界以及它帶給我們的無限遐想空間。本文將對JavaScript最最基礎(chǔ)也最...
摘要:核心點(diǎn)中的強(qiáng)制類型轉(zhuǎn)換總是返回標(biāo)量基本類型值。數(shù)字化對象在強(qiáng)制轉(zhuǎn)換為數(shù)字的時候,會優(yōu)先調(diào)用方法,如果返回基本類型的值,則直接使用該返回值如果返回值不是基本類型,則會繼續(xù)調(diào)用方法,如果返回基本類型的值,則直接使用該返回值,否則報錯。 最近在讀《你不知道的javascript》系列圖書,收獲蠻大,感慨也挺多的。是的,關(guān)于javascript,你不是不知道,而是真的不知道。?就比如類型轉(zhuǎn)換,...
摘要:拆封想要等到封裝對象中基本類型值,我們可以使用方法獲取。值類型轉(zhuǎn)換上面兩種方式,第一種我們稱為顯示強(qiáng)制類型轉(zhuǎn)換第二種稱之為隱式強(qiáng)制類型轉(zhuǎn)換。介紹強(qiáng)制與隱式類型轉(zhuǎn)換時,我們需要掌握對字符串?dāng)?shù)字和布爾類型的轉(zhuǎn)換規(guī)則。 前面兩章介紹了幾大數(shù)據(jù)類型以及值類型,接下來的這個知識點(diǎn),我覺得它對于javascript程序員來說是很重要的, 認(rèn)識封裝對象 在開始之前,我們先看一個例子,以便之后更輕松的...
摘要:第三章強(qiáng)制類型轉(zhuǎn)換強(qiáng)制類型轉(zhuǎn)換將其他數(shù)據(jù)類型轉(zhuǎn)換成類型將其他數(shù)據(jù)類型轉(zhuǎn)換為強(qiáng)制類型轉(zhuǎn)換指將一個數(shù)據(jù)類型強(qiáng)制轉(zhuǎn)換為其他數(shù)據(jù)類型類型轉(zhuǎn)換主要是指將其他數(shù)據(jù)類型轉(zhuǎn)換為將其他數(shù)據(jù)類型轉(zhuǎn)換為方式一調(diào)用被轉(zhuǎn)換數(shù)據(jù)的方法該方法不會影響到原變量但是注 ??????????????????????????????第三章強(qiáng)制類型轉(zhuǎn)換 ???????Cast type:強(qiáng)制類型轉(zhuǎn)換 ??????將其他數(shù)據(jù)類...
閱讀 2553·2023-04-25 19:47
閱讀 3397·2019-08-29 17:18
閱讀 861·2019-08-29 15:26
閱讀 3369·2019-08-29 14:17
閱讀 1151·2019-08-26 13:49
閱讀 3348·2019-08-26 13:22
閱讀 3035·2019-08-26 10:44
閱讀 2702·2019-08-23 16:51