包裝類
JS 提供了三個主要的包裝類:String
、Number
、Boolean
。如果嘗試把原始類型(string
、number
、boolean
)數據當成對象使用,JS 會自動將其轉換為對應包裝類的實例。
我們先來看一下 “基本類型數據” 及 “其包裝類的實例” 之間的異同:
const strPrimitive = "I am a string";
const strObject = new String("I am a string");console.log(strPrimitive); // 'I am a string'
console.log(strObject); // { String: 'I am a string' }
① typeof:
typeof strPrimitive; // "string"
typeof strObject; // "object"
② instanceof:
strPrimitive instanceof String; // false
strObject instanceof String; // true
③ Object.prototype.toString.call:
Object.prototype.toString.call(strPrimitive); // [object String]
Object.prototype.toString.call(strObject); // [object String]
④ 調用屬性/方法:
strPrimitive.name = "PrimitiveStr";
strObject.name = "ObjectStr";console.log(strPrimitive.name); // undefined
console.log(strObject.name); // 'ObjectStr'console.log(strPrimitive); // 'I am a string'
console.log(strObject); // { String: 'I am a string', name: 'ObjectStr' }console.log(strPrimitive.length); // 13
console.log(strObject.length); // 13
可以看到,strPrimitive.name
輸出的是 undefined
,而沒有報錯,這就是因為 JS 會在必要的時候將原始類型數據轉換為對應包裝類的實例。我們看如下解析:
strPrimitive.name = "PrimitiveStr";
// 執行這一步時, 相當于執行 new String(strPrimitive).name = 'superman'
// 但是沒有變量接收 new String(strPrimitive) 這個實例, 所以該實例又會被垃圾回收機制處理掉console.log(strPrimitive.name);
// 執行這一步時, 相當于執行 console.log(new String(strPrimitive).name)
// 但是這個新建的 String 實例并沒有 name 屬性, 所以打印 undefinedconsole.log(strPrimitive.length);
// 執行這一步時, 相當于執行 console.log(new String(strPrimitive).length)
// 這個新建的 String 實例有 length 屬性, 所以打印 13
除了使用包裝類 Boolean、Number 和 String 創建對應實例。還能使用 Object 創建,它能根據參數的類型返回對應包裝類的實例:
const obj = new Object("some text");
console.log(obj instanceof String); // true
如果參數是字符串,則會創建 String 的實例;如果是數值,則會創建 Number 的實例;如果是布爾值,則會得到 Boolean 的實例。
原始類型數據中,undefined
、null
沒有構造函數,所以它們沒有包裝類。沒有包裝類的原始類型數據被當成對象使用會直接報錯:
const a = null;
console.log(a.name); // TypeError: Cannot read properties of null
Boolean
創建一個 Boolean 實例:
const booleanObject = new Boolean(true); // 傳入 true / false 作為參數
valueOf 方法會被重寫,返回布爾值 true / false;toString 也會被重寫,返回字符串 “true” / “false”:
console.log(booleanObject.valueOf()); // true
console.log(booleanObject.toString()); // "true"
Boolean 實例在 ECMAScript 中用得很少。不僅如此,它們還很容易引起誤會,尤其是在布爾表達式中使用 Boolean 實例時:
const falseObject = new Boolean(false);
const result = falseObject && true;
console.log(result); // trueconst falseValue = false;
result = falseValue && true;
console.log(result); // false
在這段代碼中,我們創建一個值為 false 的 Boolean 實例。然后,在一個布爾表達式中通過 && 操作將這個實例與一個原始值 true 組合起來。在布爾算術中,false && true 等于 false。可是,這個表達式是對 falseObject 對象而不是對它的值(false)做 && 操作。因為所有對象在布爾表達式中都會自動轉換為 true,因此 falseObject 在這個表達式里實際上表示原始值 true 。那么 true && true 當然是 true。
Number
創建一個 Number 實例:
const numberObject = new Number(10); // 傳入一個數值作為參數
valueOf、toLocaleString 和 toString 方法會被重寫。valueOf 方法返回 Number 實例表示的原始數值,另外兩個方法返回數值字符串。
toString 方法可選地接收一個表示基數的參數,并返回對應基數形式的數值字符串:
const num = 10;
console.log(num.toString()); // "10"
console.log(num.toString(2)); // "1010"
console.log(num.toString(8)); // "12"
console.log(num.toString(10)); // "10"
console.log(num.toString(16)); // "a"
String
創建一個 String 實例:
const stringObject = newString("hello world"); // 傳入一個字符串作為參數
3 個繼承的方法 valueOf、toLocaleString 和 toString 都返回實例表示的原始字符串值。
每個 String 對象都有一個 length 屬性,表示字符串中字符的數量。來看下面的例子:
const stringValue = "hello world";
console.log(stringValue.length); // "11"
這個例子輸出了字符串 “hello world” 中包含的字符數量:11。注意,即使字符串中包含雙字節字符(而不是單字節的 ASCII 字符),也仍然會按單字符來計數。