一、類型轉換基本規則
1、ToString
對於普通對象來說,除非自行定義toString方法,否則就會調用Object.prototype.toString()方法,如果對象有自己的toString方法,字符串化就會調用該方法並使用其返回值。
2、ToNumber
對於基本類型其中 true 轉換爲 1,false 轉換爲 0。undefined 轉換爲 NaN,null 轉換爲 0。對於對象類型會首先轉換成爲基本類型值如果返回的是非數字的基本類型值,則再遵循基本類型轉換規則將其強制轉換爲數字。
爲了將值轉換爲相應的基本類型值,抽象操作 ToPrimitive會首先檢查該值是否有 valueOf() 方法。 如果有並且返回基本類型值,再使用該值進行強制類型轉換。如果沒有就使用 toString()的返回值(如果存在)來進行強制類型轉換。如果 valueOf() 和 toString() 均不返回基本類型值,會產生 TypeError 錯誤。
var a = {
valueOf:function(){
return "42";
}
};
var b = {
toString:function(){
return "42";
}
};
var c = [4,2];
c.toString = function(){
return this.join(""); //"42"
};
Number(a); //42
Number(b); //42
Number(c); //42
Number(''); //0
Number([]); //0
Number(["abc"];//NaN
3.ToBoolean
JavaScript中的值可以分爲能被強制轉換爲false的值和能被強制轉換爲true的值兩類。
假值:
- undefined
- null
- false
- +0、-0、NaN
- ""
真值:除了以上列表的都可以理解爲是真值,所有對象都是真值
let a1 = new Boolean( false );
let b1 = new Number( 0 );
let c1 = new String( "" );
let d1 = Boolean(a1 && b1 && c1); //true
let a2 = [];
let b2 = {};
let c2 = function(){};
let d2 = Boolean(a2 && b2 && c2); //true
二、顯示強制類型轉換
1.字符串與數字之間的顯示轉換
字符串和數字之間的轉換是通過 String(..) 和 Number(..) 這兩個內建函數。
除了 String(..) 和 Number(..) 以外,還有其他方法可以實現字符串和數字之間的顯式 轉換:
var a = 42;
var b = a.toString();
var c = "3.14";
var d = +c;
b; // "42"
d; // 3.14
a.toString() 是顯式的(“toString”意爲“to a string”),不過其中涉及隱式轉換。因爲 toString() 對 42 這樣的基本類型值不適用,所以 JavaScript 引擎會自動爲 42 創建一個封裝對象,然後對該對象調用 toString()。這裏顯式轉換中含有隱式轉換。
2.顯示轉換爲布爾值
var a = "0";
var b = [];
var c = {};
var d = "";
var e = 0;
var f = null;
var g;
Boolean( a ); // true
Boolean( b ); // true
Boolean( c ); // true
Boolean( d ); // false
Boolean( e ); // false
Boolean( f ); // false
Boolean( g ); // false
雖然 Boolean(..) 是顯式的,但並不常用。顯式強制類型轉換爲布爾值最常用的方法是 !!
var a = "0";
var b = [];
var c = {};
var d = "";
var e = 0;
var f = null;
var g;
!!a; // true
!!b; // true
!!c; // true
!!d; // false
!!e; // false
!!f; // false
!!g; // false
三、隱式強制類型轉換
1.字符串和數字之間的隱式強制類型轉換
+ 運算符
即能用於數字加法,也能用於字符串拼接
var a = "42";
var b = "0";
a + b; // "420"
var c = 42;
var d = 0;
c + d; // 42
var x = [1,2];
var y = [3,4];
x + y; // "1,23,4"
如果 + 的其中一個操作數是字符串(或者通過對其調用 ToPrimitive 抽象操作得到字符串), 則執行字符串拼接;否則執行數字加法。因爲數組的 valueOf() 操作無法得到簡單基本類型值,於是它轉而調用 toString()。因此上例中的兩 個數組變成了 "1,2" 和 "3,4"。+ 將它們拼接後返回 "1,23,4"。
使用+運算符進行字符串轉換,將數字和空字符串 "" 相 + 來將其轉換爲字符串:
var a = 42;
var b = a + "";
b; // "42"
a + ""(隱式)和前面的String(a)(顯式)之間有一個細微的差別需要注意。根據 ToPrimitive抽象操作規則,a + ""會對a調用valueOf()方法,然後通過ToString抽象 操作將返回值轉換爲字符串。而 String(a) 則是直接調用 ToString()。它們最後返回的都是字符串,但如果 a 是對象而非數字結果可能會不一樣
var a = {
valueOf: function() { return 42; },
toString: function() { return 4; }
};
a + ""; // "42"
String( a ); // "4"
從字符串強制類型轉換爲數字的情況
-是數字減法運算符,因此a - 0會將a強制類型轉換爲數字。也可以使用a * 1和a /1,因爲這兩個運算符也只適用於數字,只不過這樣的用法不太常見
var a = [3];
var b = [1];
a - b; // 2
2.隱式強制類型轉換爲布爾值
下面的情況會發生 布爾值隱式強制類型轉換:
(1)if (..)語句中的條件判斷表達式。
(2)for ( .. ; .. ; .. )語句中的條件判斷表達式(第二個)。
(3) while (..) 和 do..while(..) 循環中的條件判斷表達式。
(4)? :中的條件判斷表達式。
(5) 邏輯運算符 ||(邏輯或)和 &&(邏輯與)左邊的操作數(作爲條件判斷表達式)。
以上情況中,非布爾值會被隱式強制類型轉換爲布爾值,遵循前面介紹過的 ToBoolean 抽象操作規則。
四、寬相等、嚴相等
寬相等== 允許在相等比較中進行強制類型轉換,而嚴相等=== 不允許。== 和 === 都會檢查操作數的類型。區別在於操作數類型不同時它們的處理方式不同。
1.字符串和數字之間的相等比較
var a = 42;
var b = "42";
a === b; // false
a == b; // true
因爲沒有強制類型轉換,所以 a === b 爲 false,42 和 "42" 不相等。而 a == b 是寬鬆相等,即如果兩個值的類型不同,則對其中之一或兩者都進行強制類型轉換。轉換規則如下:
(1) 如果 Type(x) 是數字,Type(y) 是字符串,則返回 x == ToNumber(y) 的結果。
(2) 如果 Type(x) 是字符串,Type(y) 是數字,則返回 ToNumber(x) == y 的結果。
2.其他類型和布爾類型之間的相等比較
var a = "42";
var b = true;
a == b; // false
(1) 如果 Type(x) 是布爾類型,則返回 ToNumber(x) == y 的結果;
(2) 如果 Type(y) 是布爾類型,則返回 x == ToNumber(y) 的結果。
上例中Type(x)是布爾值,所以ToNumber(x)將true強制類型轉換爲1,變成1 == "42",二者的類型仍然不同,"42" 根據規則被強制類型轉換爲 42,最後變成 1 == 42,結果爲 false。
3.null和undefined之間的相等比較
(1) 如果 x 爲 null,y 爲 undefined,則結果爲 true。
(2) 如果 x 爲 null,y 不是 undefined或者null,則結果爲 false。
(3) 如果 x 爲 undefined,y 爲 null,則結果爲 true。
(4)如果 x 爲 undefined,y 不是 undefined或者null,則結果爲 false。
var a = null;
var b;
a == b; // true
a == null; // true
b == null; // true
a == false; // false
b == false; // false
a == ""; // false
b == ""; // false
a == 0; // false
b == 0; // false
4.對象和非對象之間的相等比較
(1) 如果 Type(x) 是字符串或數字,Type(y) 是對象,則返回 x == ToPrimitive(y) 的結果;
(2) 如果 Type(x) 是對象,Type(y) 是字符串或數字,則返回 ToPromitive(x) == y 的結果。
var a = 42;
var b = [ 42 ];
a == b; // true
[ 42 ] 首先調用 ToPromitive 抽象操作,返回 "42",變成 "42" == 42,然後 又變成 42 == 42,最後二者相等
5.比較少見的情況
假值的相等比較
"0" == null; // false
"0" == undefined; // false
"0" == false; // true --
"0" == NaN; // false
"0" == 0; // true
"0" == ""; // false
false == null; // false
false == undefined; // false
false == NaN; // false
false == 0; // true --
false == ""; // true --
false == []; // true --
false == {}; // false
"" == null; // false
"" == undefined; // false
"" == NaN; // false
"" == 0; // true --
"" == []; // true --
"" == {}; // false
0 == null; // false
0 == undefined; // false
0 == NaN; // false
0 == []; // true --
0 == {}; // false
安全運用隱式強制類型轉換
-
如果兩邊的值中有 true 或者 false,千萬不要使用 ==
-
如果兩邊的值中有 []、"" 或者 0,儘量不要使用 ==。
這時最好用 === 來避免不經意的強制類型轉換。這兩個原則可以讓我們避開幾乎所有強制 類型轉換的坑。
五、 Object.is(..) 判斷兩個值是否相等
以下情況返回true:
- 兩個值都是 undefined
- 兩個值都是 null
- 兩個值都是 true 或者都是 false
- 兩個值是由相同個數的字符按照相同的順序組成的字符串
- 兩個值指向同一個對象
- 兩個值都是數字並且
- 都是正零 +0
- 都是負零 -0
- 都是 NaN
- 都是除零和 NaN 外的其它同一個數字
對於 ES6 之前的版本,Object.is(..) 有一個簡單的 polyfill:
if (!Object.is) {
Object.is = function(v1, v2) {
// 判斷是否是-0
if (v1 === 0 && v2 === 0) {
return 1 / v1 === 1 / v2;
}
// 判斷是否是NaN
if (v1 !== v1) {
return v2 !== v2;
}
// 其他情況
return v1 === v2;
};
}
能使用 == 和 ===時就儘量不要使用 Object.is(..),因爲前者效率更高、更爲通用。Object.is(..) 主要用來處理那些特殊的相等比較。