原始型別與包覆器的差異

說明

JavaScript中,有五種原始型別:數值、字串、布林、nullundefined,除此nullundefined之外,其餘三種都有原始型別包覆物件(primitive wrapper objects)。 其對應的三種包覆物件有對應的內建建構式,分別為Number()String()Boolean()

兩者的差異

最簡單的辨別方式

用包覆器建立的數值,型別都會是object;直接以原始型別指派給變數的話,其變數的型別則為對應的原始型別。

1
2
3
4
5
6
7
8
9
10
11
12
13
var bool = true,
num = 999,
str = "example";
typeof bool; // "boolean"
typeof num; // "number"
typeof str; // "string"

var boolObj = new Boolean(true),
numObj = new Number(999),
strObj = new String("example");
typeof boolObj; // "object"
typeof numObj; // "object"
typeof strObj; // "object"

Boolean包覆器

你覺得下面的結果會是什麼? 提示:它用建構式,所以會是一個物件。

1
2
var boolObj = new Boolean(false);
console.log(boolObj);

答案就是true。 因為無論你放入任何初始化的直進去建構式,一定都會被new運算子回傳一個物件。 而在js中,物件都被視為truthy。 換句話說,用new Boolean()回傳的值,全部都是true,沒有例外。

如果不用new運算子的話,則回傳和truthyfalsy的分類結果一樣的對應值:

1
2
3
4
5
6
7
8
9
10
11
12
13
var boolObjArray = [
Boolean(NaN), // false
Boolean(Number.NEGATIVE_INFINITY), // true
Boolean(Number.POSITIVE_INFINITY), // true
Boolean(null), // false
Boolean(undefined), // false
Boolean(0), // false
Boolean(4), // true
Boolean(''), // false
Boolean('test'), // true
Boolean(true), // true
Boolean(false) // false
];

仔細想想,這樣的用法好像跟!!運算子很相似:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
var boolObjArray = [
Boolean(NaN), // false
Boolean(Number.NEGATIVE_INFINITY), // true
Boolean(Number.POSITIVE_INFINITY), // true
Boolean(null), // false
Boolean(undefined), // false
Boolean(0), // false
Boolean(4), // true
Boolean(''), // false
Boolean('test'), // true
Boolean(true), // true
Boolean(false) // false
], originalArray = [
NaN,
Number.NEGATIVE_INFINITY,
Number.POSITIVE_INFINITY,
null,
undefined,
0,
4,
'',
'test',
true,
false
], isSame = boolObjArray.every(function(bool, index) {
return bool === !!originalArray[index];
});

console.log(isSame); // true

Number包覆器

相較於Boolean()Number()似乎就比較有用一些。 它的主要用途是作為數值判定和型別轉換,請參考 MDN - Number#說明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Number(0) // 0
Number(123) // 123
Number(Infinity) // Infinity
Number(Number.POSITIVE_INFINITY) // Infinity
Number(Number.NEGATIVE_INFINITY) // -Infinity
Number(Number.EPSILON) // 2.220446049250313e-16
Number(Number.MAX_SAFE_INTEGER) // 9007199254740991
Number(Number.MAX_VALUE) // 1.7976931348623157e+308
Number(Number.MIN_SAFE_INTEGER) // -9007199254740991
Number(Number.MIN_VALUE) // 5e-324
Number(null) // 0
Number('') // 0
Number('123') // 123
Number(new Date('Sep 14, 2017 23:36:45')) // 1505403405000
Number(true) // 1
Number(false) // 0
Number(Boolean(true)) // 1
Number(Boolean(false)) // 0

Number(Number.NaN) // NaN
Number(NaN) // NaN
Number(undefined) // NaN
Number(function(){}) // NaN
Number({}) // NaN
Number('123abc') // NaN
Number(Math) // NaN

String包覆器

String()如果傳入eval()函式的話,要特別注意。 eval()會把String物件視作是一個物件包含字串,而非一個字串。 所以要使用valueOf()或是toString()String物件轉換成原始型別字串。

1
2
3
4
eval('5 + 5') // 10
eval(new String('6 + 6')) // 6 + 6
eval(new String('7 + 7').toString()) // 14
eval(new String('8 + 8').valueOf()) // 16

簡單一點,其實可以多加上原始型別字串,這樣也能達到一樣的效果。

1
eval(new String('8 + 8') + '8') // 24

結論

  • 使用new運算子,在typeof的結果都會是object;反之,都會是自身的原始型別(booleannumberstring)。
  • 使用Boolean()時,不要用new運算子。
  • Boolean()!!運算子功能相似,目的是要將物件轉換成布林值。
  • Number()主要功能是將可轉換成數值的物件轉換成數值,否則回傳NaN物件。
  • Number()可將Date物件轉換成時間戳記。
  • String()在使用時,要注意在eval()中,需要做原始型別字串的轉換,否則會達不到預期的結果。