一、型別轉換先說型別
型別轉換指將一種型別轉換為另一種型別,那我們首先來說說JavaScript中的型別。
1.1原始(Primitive)資料型別
- Null
- Undefined
- Boolean
- String
- Number
- Symbol
- BigInt
BigInt是一種新的資料型別,用於當整數值大於Number資料型別支援的範圍時。這種資料型別允許我們安全地對大整數執行算術操作,表示高解析度的時間戳,使用大整數id,等等,而不需要使用庫。 重要的是要記住,不能使用Number和BigInt運算元的混合執行算術運算,需要通過顯式轉換其中的一種型別。 此外,出於相容性原因,不允許在BigInt上使用一元加號(+)運算子。
1.2引用(Object)資料型別
javaScript中內建了很多物件。
- Array
- Array
- ArrayBuffer
- AsyncFunction
- Atomics
- BigInt
- BigInt64Array
- BigUint64Array
- Boolean
- DataView
- Date
- Error
- EvalError
- Float32Array
- Float64Array
- Function
- Generator
- GeneratorFunction
- Infinity
- Int16Array
- Int32Array
- Int8Array
- InternalError
- Intl
- Intl.Collator
- Intl.DateTimeFormat
- Intl.ListFormat
- Intl.Locale
- Intl.NumberFormat
- Intl.PluralRules
- Intl.RelativeTimeFormat
- JSON
- Map
- Math
- NaN
- Number
- Object
- Promise
- Proxy
- RangeError
- ReferenceError
- Reflect
- RegExp
- Set
- SharedArrayBuffer
- String
- Symbol
- SyntaxError
- TypeError
- TypedArray
- URIError
- Uint16Array
- Uint32Array
- Uint8Array
- Uint8ClampedArray
- WeakMap
- WeakSet
- WebAssembly
- decodeURI()
- decodeURIComponent()
- encodeURI()
- encodeURIComponent()
- escape()
- eval()
- globalThis
- isFinite()
- isNaN()
- null
- parseFloat
- parseInt
- undefined
- unescape()
- uneval()
詳情請參考MDN,
大家不要看javaScript的內建物件這麼多,轉換時只需要把這麼統統當做一個型別引用型別進行轉換就行,在javaScript內部中轉換也不會考慮這麼多。
二、自動裝箱
為了方便操作基本資料型別, ECMAScript還提供了三個特殊的引用型別,基本包裝型別,String、Boolean、Number。有了這三個型別,在需要的時候,原始型別會自動轉換成相應的包裝物件(這個過程叫自動裝箱)。自動裝箱就是臨時建立一個包裝物件,將原始型別的值封裝起來,以便呼叫包裝物件的函式。但是原來那個變數的值不會有任何變化!
var s1 = "some text";
var s2 = s1.substring(2);
複製程式碼
字串是基本資料型別,為撒能呼叫方法了,這其實在後臺進行了一系列的操作
- 建立String型別的一個例項
- 在例項上呼叫指定的方法。
- 銷燬這個例項。
var s1 = new String("some text");
var s2 = s1.substring(2);
s1 = null;
複製程式碼
當然,你可以將Boolean 、Number 、String 這三個函式當作建構函式來使用,通過手動new包裝類來裝箱(得到包裝物件):
// 手動裝箱
var s1 = new String("some text");
s1.toUpperCase();
typeof s1;
// "object"
複製程式碼
三、型別轉換的規則
四、內部用於實現型別轉換的4個函式
4.1 ToPrimitive ( input [ , PreferredType ] )
// ECMA-262, section 9.1, page 30. Use null/undefined for no hint,
// (1) for number hint, and (2) for string hint.
function ToPrimitive(x, hint) {
// Fast case check.
if (IS_STRING(x)) return x;
// Normal behavior.
if (!IS_SPEC_OBJECT(x)) return x;
if (IS_SYMBOL_WRAPPER(x)) throw MakeTypeError(kSymbolToPrimitive);
if (hint == NO_HINT) hint = (IS_DATE(x)) ? STRING_HINT : NUMBER_HINT;
return (hint == NUMBER_HINT) ? DefaultNumber(x) : DefaultString(x);
}
// ECMA-262, section 8.6.2.6, page 28.
function DefaultNumber(x) {
if (!IS_SYMBOL_WRAPPER(x)) {
var valueOf = x.valueOf;
if (IS_SPEC_FUNCTION(valueOf)) {
var v = % _CallFunction(x, valueOf);
if (IsPrimitive(v)) return v;
}
var toString = x.toString;
if (IS_SPEC_FUNCTION(toString)) {
var s = % _CallFunction(x, toString);
if (IsPrimitive(s)) return s;
}
}
throw MakeTypeError(kCannotConvertToPrimitive);
}
// ECMA-262, section 8.6.2.6, page 28.
function DefaultString(x) {
if (!IS_SYMBOL_WRAPPER(x)) {
var toString = x.toString;
if (IS_SPEC_FUNCTION(toString)) {
var s = % _CallFunction(x, toString);
if (IsPrimitive(s)) return s;
}
var valueOf = x.valueOf;
if (IS_SPEC_FUNCTION(valueOf)) {
var v = % _CallFunction(x, valueOf);
if (IsPrimitive(v)) return v;
}
}
throw MakeTypeError(kCannotConvertToPrimitive);
}
複製程式碼
ToPrimitive將input裝換為基本資料型別,PreferredType要麼不傳,要麼是number、string。
4.1.1 PreferredType為number
- 如果input本身就是原始型別,直接返回input。
- 呼叫input.valueOf(),如果結果是原始型別,則返回這個結果。
- 呼叫input.toString(),如果結果是原始型別,則返回這個結果。
- 丟擲TypeError異常。
4.1.2 PreferredType為string
- 如果input本身就是原始型別,直接返回input。
- 呼叫input.toString(),如果結果是原始型別,則返回這個結果。
- 呼叫input.valueOf(),如果結果是原始型別,則返回這個結果。
- 丟擲TypeError異常。
4.1.3 PreferredType不傳入
- 如果input是內建的Date型別,PreferredType 視為String
- 否則PreferredType 視為 Number。
來看看這道網上的面試題
({}) + 1
複製程式碼
+號操作符,只有當左右兩邊的型別相同(都為string或者number)是才進行操作。所以會經歷如下步驟:
- {}和1都會呼叫ToPrimitive,1原始型別直接返回。
- {}內部呼叫DefaultNumber,使用valueOf方法,返回object。
- 在呼叫toString方法,返回[object, object]。
- 所以最後的結果就是[object, object]1。
這一類轉換換湯不換藥,轉換規則都是這樣的。
4.2 ToBoolean ( argument )
4.3 ToNumber( argument )
4.4 ToString( argument )
來源:ECMA-262草案/ 2019年11月7日 ECMAScript®2020語言規範
五、隱式型別裝換
在執行過程中當js內部期望得到某種型別的值,而實際在那裡的值是其他的型別,就會發生隱式型別轉換。系統內部會自動呼叫我們前面說ToBoolean ( argument )、ToNumber ( argument )、ToString ( argument ),嘗試轉換成期望的資料型別。
5.1 期望得到boolean的值
if ( !undefined && !null && !0 && !NaN && !'') {
// xxxx
}
複製程式碼
因為在if的括號中,js期望得到boolean的值,所以對括號中每一個值都使用ToBoolean ( argument ),將它們轉化成boolean。
5.2 期望得到number的值
3 * { valueOf: function () { return 5 } };
複製程式碼
因為在乘號的兩端,js期望得到number型別的值,所以對右邊的那個物件使用ToNumber ( argument ),得到結果5,再與乘號左邊的3相乘。
5.3 加號有別於其他運算子
- 如果有一邊是字串,就把另外一邊也轉換為字串
- 如果一方不是字串或者資料,就轉換為資料或者字串
處了加號運算子,其他運算子,只要其中一方資料,那麼另一方就被轉換為數字
六、顯示型別裝換
手動呼叫Boolean(value)、Number(value)、String(value)完成的型別轉換。
Boolean('some text'); // true
Number("2019"); // 2019
String({a: 1}); // "[object Object]"
複製程式碼
前面兩個型別轉換沒有什麼好解釋的,我們看看最後一個String({a: 1});在內部發生的時候
- 執行轉換String({a: 1})。
- 執行內部的ToString({a: 1})。
- {a: 1}不是原始型別,執行ToPrimitive({a: 1}, hint string)。
- 呼叫toString方法,返回"[object, object]"。
- 執行ToString("[object, object]"),返回"[object, object]"。
參考文章: