1. 基本類型轉換
由于Javascript中存在七種基本類型:number
, string
, boolean
, object
, null
, undefined
, symbol
,在使用過程中會有意識和無意識的在 運行時 存在相互轉換。
1.1 toString
1.1.1 較大數和較小數
當數字處于較大或較小時,由于Javascript會將數字轉換為科學記數法方式記錄,這個時候如果對其進行轉換為字符串,那么顯示結果是科學記數法的字符串
console.log(String(0.0000000001)); // '1e-10'
console.log(String(10000000000000000000000)); // '1e+22'
1.1.2 對象的toString
一般對象的toString
方法,將會顯示該對象的[[class]]
Object.prototype.toString.call({}); // '[object Object]'
Object.prototype.toString.call(function(){}) // '[object Function]'
1.1.3 數組的toString
數組由于自身重寫過toString
方法,數組的toString
方法會返回逗號連接的字符串
Array.prototype.toString.call(['1', 'a', {'a': 1}]); // '1,a,[object Object]'
1.1.4 JSON.stringify()
JSON.stringify()
方法用于將數據轉換為JSON
格式的字符串,但是對于undefined
, function
, symbol
類型的數據,在轉換時會選擇拋棄,從而返回undefined
JSON.stringify(undefined); // undefined
JSON.stringify(function(){}); // undefined
JSON.stringify(Symbol('test')); // undefined
轉換對象在數組中時,這些值會被賦值為null
JSON.stringify([undefined, 1, function(){}]); // [null, 1, null]
如果轉換對象出現循環引用,那么在轉換的時候會拋出異常。
為了使得我們所有的對象在轉換成JSON
字符串的時候可以正常轉換,我們可以定義toJSON
方法,該方法在JSON.stringify
調用前會進行調用,對數據進行處理
var a = {a: 1, b: 2};
a.toJSON = function() {
return {a: 1}
}
JSON.stringify(a); // "{"a": "1"}"
JSON.stringify
一共可以接受三個參數,除了第一個參數為轉換對象以外,第二個參數可以接收了一個數組或者方法對轉換對象屬性進行過濾,第三個參數則可以將JSON
字符串進行格式化
var a = {a: 1, b: 2};
// 第二個參數使用數組進行過濾
JSON.stringify(a, ['a']); // "{"a": "1"}"
// 第二個參數使用function,自身對象會先調用方法一次后,每個屬性調用一次,返回undefined時則過濾掉該屬性
JSON.stringify(a, (k, v)=>{
if (k!=='a') {
return v;
}
}); // "{"b": 2}"
// 第三個參數進行格式化,接收數字時每個層級添加相應數目空格
JSON.stringify({a:1, b:2, c: {a: 1}}, null, 2);
// 第三個參數如果為字符串,則每個層級添加該字符串,能添加的字符串最大長度為10
JSON.stringify({a:1, b:2, c: {a: 1}}, null, '--');
1.2 toNumber
1.2.1 基本類型轉Number
基本類型轉為number
,按照以下規則轉換:
// null , false , '' 轉換為0
console.log(Number(null)); // 0
console.log(Number(false)); // 0
console.log(Number('')); // 0
// true轉換為1
console.log(Number(true)); // 1
// undefined轉換為NaN
console.log(Number(undefined)); // NaN
// 字符串如果是數字格式則會進行轉換
console.log(Number('123')); // 123
1.2.2 對象轉Number
遵循toPrimitive
的規則,如果對象存在valueOf
方法,則調用該方法,如果調用后返回的結果為基本類型,則按照 1.2.1 基本類型轉Number 規則進行轉換;如果返回不為基本類型或者不存在valueOf
方法,則調用toString
方法后,按照 1.2.1 基本類型轉換Number 規則進行處理
var a = [1];
// 轉為Number,首先使用a.valueOf()方法,返回[1]
// 繼續調用toString方法,返回'1'
// 再將字符串轉為數字1
console.log(Number(a)); // 1
// 如果重寫valueOf方法,則會返回超乎想象的結果
a.valueOf = () => {return 2};
console.log(Number(a)); // 2
1.3 toBoolean
boolean
類型轉換過程只會進行真假值的檢查,其中假值包括: false
, ''
, null
, undefined
, NaN
, +0
, -0
,假值將會轉換為false
,假值以外的其他值均為真值,轉換為true
console.log(Boolean(false)); // false
console.log(Boolean('')); // false
console.log(Boolean(null)); // false
console.log(Boolean(undefined)); // false
console.log(Boolean(NaN)); // false
console.log(Boolean(+0)); // false
console.log(Boolean(-0)); // false
1.4 特殊
對于一個特殊的對象Object.create(null)
,由于原型鏈的繼承關系,該對象不繼承Object
,所以不存在valueOf
和toString
方法,那么在進行轉換的時候將拋出異常
var a = Object.create(null);
console.log(Number(a)); // Uncaught TypeError: Cannot convert object to primitive value
2. 類型轉換
類型轉換可以分為顯示類型轉換和隱式類型轉換,但顯示和隱式只是相對的,在我們知道會進行轉換的時候他就是隱式,當我們知道會進行轉換以后其實就變為了顯式轉換了。
2.1 顯示類型轉換
2.1.1 String()和Number()轉換
按照 1. 基本類型轉換 的toString和toNumber的規則進行轉換
2.1.2 一元運算符轉換
使用一元運算符(+
, -
)會將數據轉換為number
類型,相當于Number(data)
console.log(+[]); // 0 ,使用toNumber的規則,相當于Number([])
2.1.3 位反運算符轉換
使用位反運算符(~
)會將數據轉換為number
類型,相當于-(Number(data) + 1)
console.log(~[]); // -1
利用位反運算符,可以一些特定方法返回值進行處理后,簡化判斷
// 例如indexOf匹配失敗會返回-1,就可以利用位反運算簡化判斷條件
if('abc'.indexOf('a') > -1) {}
// 相當于
if(~'abc'.indexOf('a')){}
2.1.4 字符串解析
使用parseInt
,parseFloat
可以將字符串(非字符串會優先轉換為字符串)解析為number
類型,不同于Number()
的強制類型轉換,解析過程會進行到不能解析為止,而Number()
轉換在判斷不能轉換到時候直接返回NaN
console.log(parseInt('12ab')); // 12
console.log(Number('12ab')); // NaN
在ES5以后,parseInt
默認使用了10進制作為基數進行轉換,可以通過第二個參數指定基數,此時對字符串解析過程會按照指定進制進行
console.log(parseInt('1a', 19)); // 29 因為按照19進制1和a都是有效的字符,返回結果為29
2.1.5 !轉換
使用'!',可以轉換為boolean
類型,轉換相當于Boolean(data)
console.log(!0); // true
console.log(!!0); //false
2.1.6 Symbol()對象轉換
Symbol
對象不能通過隱式轉換進行,如果要進行轉換必須使用構造方法來顯示轉換(似乎只能轉為字符串)。
var a = Symbol('1');
console.log(a + 1); Uncaught TypeError: Cannot convert a Symbol value to a number
console.log(String(a) + 1); // 'Symbol(1)1'
2.2 隱式類型轉換
2.2.1 算數運算轉換
在算數運算的過程中會進行數據轉換:
對于+
運算來說,如果兩個操作數中存在字符串,則會嘗試進行字符串連接操作,如果操作數為對象,則會使用toPrimitive
(使用valueOf
方法和toString
方法)轉為基本類型后,再進行判斷。如果不存在字符串,則全部轉為數字進行加法操作。
console.log(1 + ['1']); // 11 , 先將對象['1']根據toPrimitive規則轉為'1',操作變為 1 + '1' , 進行字符串拼接
對于-
運算來說,直接將所有操作數進行toNumber
操作后進行計算
console.log(1 - ['1']); // 0
2.2.2 邏輯語句中的類型轉換
作為邏輯語句中的判斷條件,將轉換為boolean
值進行處理
if(1) { console.log(1)}; // 由于Boolean(1)是true,所以會打印1
if(0) { console.log(1)}; // 由于Boolean(0)是false,所以不會打印1
2.2.3 ||和&&
||
和&&
的操作,返回結果并不是boolean
值,而是根據短路規則,判斷操作數的Boolean()
結果,返回兩個操作數的其中之一,其中||
在true
時進行短路返回,&&
在false
時進行短路返回
var a = 1 && 0;
console.log(a); // 0 , 由于第一個操作數Boolean(1)是true,所以返回第二個操作數
a = 0 && 1;
console.log(a); // 0 , 由于第一個操作數Boolean(0)是false,所以返回第一個操作數
a = 1 || 0;
console.log(a); // 1, 由于第一個操作數Boolean(1)是true,所以返回第一個操作數
a = 0 || 1;
console.log(a); // 1, 由于第一個操作數Boolean(0)是false,所以返回第二個操作數
3. 數據比較
3.1 寬松等于
使用==
進行比較的稱做寬松等于,區別于===
的嚴格等于,在于進行比較的時候,等式兩邊的數值在類型不相等的時候會嘗試進行強制轉換,而嚴格等于不會進行類型的轉換。
寬松等于的轉化規則如下:
- 如果兩個比較數類型一致且為基本類型,則直接進行比較,數字按照大小,字符串按照字母表順序比較。
- 如果兩個比較數類型不一致且為基本類型,則轉換為
number
進行比較。 - 如果兩個比較數據均為對象類型,則比較對象的引用是否相等
- 如果兩個數據中一個為對象,則根據
toPrimitive
規則,將其轉換為基本類型后,進行比較。
console.log('a' == 'a'); // true , 類型相同且為基本類型,則直接比較
console.log('1' == 1); // true , 類型不同但均為基本類型,將'1'轉為number進行比較
console.log({} == {}); // false , 兩個比較數均為對象,判斷引用是否相等
console.log('a' == ['a']); // true,一個比較數為對象,則將['a']轉換為'a'后進行比較
對于特殊的undefined
和null
console.log(undefined == null); // true
3.2 抽象比較
抽象比較也就是非等于比較,比較規則和寬松等于規則相同
console.log('a' < 'b'); // true, 類型相同,直接進行比較,字符串按照字母表順序
console.log('0' < 1); // true, 類型不同但均未基本類型,將'0'轉為number后進行比較
console.log('a' < ['b']); // true, 一個比較數為對象,則將['b']轉換為'b'后進行比較
4. 參考
《你不知道的Javascript(中卷)》