1. undefined和undeclared
訪問未被聲明的變量,會(huì)報(bào)ReferenceError,
var a;
a; // undefined
b; // Uncaught ReferenceError: b is not defined
但是typeof運(yùn)算符并不會(huì)報(bào)錯(cuò),
對(duì)于值為undefined的變量和未被聲明的變量,都會(huì)返回一個(gè)字符串'undefined'。
var a;
typeof a; // 'undefined'
typeof b; // 'undefined'
在最外層作用域中,檢測(cè)一個(gè)變量是否被定義,除了使用typeof b;之外,
也可以使用window.hasOwnProperty('b');,
但是typeof更具通用性,它還可以用來檢測(cè)當(dāng)前詞法環(huán)境中是否聲明過某個(gè)變量。
function f(){
var a;
function g(){
typeof b; // 'undefined'
// 這里就無法使用window.hasOwnProperty來檢測(cè)了
// ...
}
g();
}
f();
2. 數(shù)組的空白單元
以下數(shù)組a的第0個(gè)元素是空白單元(empty slot)。
a = [];
a[1] = undefined;
a; // [empty, undefined]
a[0]; // undefined
a[1]; // undefined
a.hasOwnProperty(0); // false
a.hasOwnProperty(1); // true
0 in a; // false
1 in a; // true
2.1 創(chuàng)建空白單元的其他辦法
(1)使用Array構(gòu)造器來創(chuàng)建的數(shù)組,其元素也是空白單元,
a = Array(3);
a; // [empty × 3]
(2)還有一個(gè)辦法是使用[,,,]來創(chuàng)建空白單元,
a = [,,,]; // 由于最后一個(gè)逗號(hào)會(huì)被省略
a; // [empty × 3]
(3)修改數(shù)組的length屬性也可以創(chuàng)建空白單元,
a = [];
a.length = 3;
a; // [empty × 3]
2.2 空白單元的性質(zhì)
(1)空白單元的值為undefined
(2)數(shù)組對(duì)該下標(biāo)的hasOwnProperty為false
(3)使用in運(yùn)算符檢測(cè)數(shù)組的對(duì)象屬性,返回false
(4)forEach,filter,every,some,map,迭代時(shí),會(huì)忽略空白單元
(5)for...of會(huì)遍歷空白單元
(6)Array.from,擴(kuò)展運(yùn)算符...,keys,values,entries,會(huì)將空白單元轉(zhuǎn)為undefined
(7)join,toString會(huì)將空白視為空字符串
3. 數(shù)組的非數(shù)值索引
a = [0,1];
a['x'] = 2;
a; // [0, 1, x: 2]
a.length; // 2
為數(shù)組添加非數(shù)值索引,并不會(huì)自動(dòng)調(diào)整數(shù)組的長度。
該索引,會(huì)變成數(shù)組的對(duì)象屬性。
值得注意的是,如果非數(shù)值索引,可以被轉(zhuǎn)為非NaN的正數(shù)的話,
就相當(dāng)于給數(shù)組添加一個(gè)轉(zhuǎn)換后的數(shù)值索引。
a = [0,1];
a['2'] = 2; // Number('2') -> 2
a; // [0, 1, 2]
a.length; // 3
a['3y'] = 3; // Number('3y') -> NaN
a; // [0, 1, 2, 3y: 3]
a.length; // 3
a['-4'] = 4;
a; // [0, 1, 2, 3y: 3, -4: 4]
a.length; // 3
注:
(1)在寫法上,Number(x)相當(dāng)于+x,
(2)NaN是唯一一個(gè)與自己都不===的值,即,
NaN !== NaN // true
而window.isNaN是有問題的,
window.isNaN(NaN); // true
window.isNaN('x'); // true,這里也為true
4. 數(shù)值后面的點(diǎn)
(1)數(shù)值前面的0可以省略,
a = 0.42; // 0.42
a = .42; // 0.42
(2)數(shù)值后面的點(diǎn)可以省略
a = 42.0; // 42
a = 42.; // 42
注意,42.0和42.結(jié)果都是整數(shù)42。
此外,數(shù)字后面如果寫了.,那么后面的字符會(huì)優(yōu)先考慮為它的小數(shù)部分,
42.toFixed(3); // Uncaught SyntaxError: Invalid or unexpected token
42..toFiexed(3); // '42.000'
42 .toFixed(3); // '42.000'
對(duì)于42.toFixed(3);,由于.后面的t不是一個(gè)合法的數(shù)字,所以就報(bào)語法錯(cuò)了。
而42..toFiexed(3);相當(dāng)于(42.).toFiexed(3);,是符合語法的表達(dá)式。
此外42 .toFixed(3);也是符合語法的,注意42后面有一個(gè)空格。
5. Number.EPSILON
Number.EPSILON是ES6引入表示的機(jī)器精度(machine epsilon)的數(shù)值,
它的大小為,Math.pow(2,-52),即,2.220446049250313e-16。
使用它可以在判斷兩個(gè)浮點(diǎn)數(shù),在機(jī)器精度的誤差范圍內(nèi)是否相等,
a = 0.1;
b = 0.2;
a + b === 0.3; // false
a + b; // 0.30000000000000004
a + b - 0.3 < Number.EPSILON; // true
6. 整數(shù)的安全范圍
Number.MAX_SAFE_INTEGER; // 9007199254740991,即,Math.pow(2,53)-1
Number.MIN_SAFE_INTEGER; // -9007199254740991
Number.MAX_SAFE_INTEGER + 1; // 9007199254740992
Number.MAX_SAFE_INTEGER + 2; // 9007199254740992
Number.MAX_SAFE_INTEGER + 3; // 9007199254740994
Number.MAX_SAFE_INTEGER + 4; // 9007199254740996
Number.isSafeInteger(Number.MAX_SAFE_INTEGER); // true
Number.isSafeInteger(Number.MAX_SAFE_INTEGER + 1); // false
Number.MIN_SAFE_INTEGER和Number.MAX_SAFE_INTEGER界定了安全整數(shù)的范圍,
而Number.isSafeInteger是ES6添加的,用于判斷一個(gè)整數(shù)是否安全的方法。
7. 正負(fù)無窮,正負(fù)零
1/0; // Infinity
-1/0; // -Infinity
Number.POSITIVE_INFINITY === Infinity; // true
Number.NEGATIVE_INFINITY === -Infinity; // true
Infinity === Infinity; // true
Infinity === -Infinity; // false
0/1; // 0
0/-1; // -0
0 === -0; // true
1/0; // Infinity
1/-0; // -Infinity
我們看到正零和負(fù)零是相等的,0 === -0,因此只能通過Infinity來進(jìn)行區(qū)分,
1/0===Infinity而1/-0===-Infinity,
Infinity與-Infinity是不相等的。
注:
ES6新增了Object.is,
來判斷兩個(gè)值是否為相同(the same value)。
Object.is(0, -0); // false
Object.is(-0, -0); // true
Object.is(NaN, NaN); // true
8. 值的復(fù)制和引用
原始值,包含除了Object類型之外的所有其他值。
即,Null,Undefined,Boolean,Number,String,Symbol類型的值。
原始值的傳遞方式是復(fù)制,例如,
a = 1;
b = a;
b++;
a; // 1
b; // 2
Object類型的值傳遞方式是引用,例如,
a = [];
b = a;
b.push(1);
a; // [1]
b; // [1]
8.1 構(gòu)造函數(shù)的返回值
我們知道,如果一個(gè)函數(shù)不返回值,則相當(dāng)于返回undefined,
而如果一個(gè)構(gòu)造函數(shù)返回了一個(gè)原始值,則會(huì)丟棄該原始值,返回this。
相反,如果一個(gè)構(gòu)造函數(shù)返回了一個(gè)Object類型的值,則會(huì)丟棄this。
function F(){
return 1;
}
obj = new F; // F {},即,F(xiàn)的實(shí)例
function F(){
return [1];
}
obj = new F; // [1],丟棄this,obj不是F的實(shí)例
8.2 對(duì)原始值調(diào)用Object(...),可以獲取對(duì)應(yīng)的包裝對(duì)象
下面拿Symbol類型的值(原始值),進(jìn)行介紹。
首先需要了解的是,Symbol('x')和Symbol.for('x'),創(chuàng)建了兩種不同的symbol原始值,
Symbol('x') === Symbol('x'); // false
Symbol.for('x') === Symbol.for('x'); // true
Symbol.for('x')會(huì)創(chuàng)建全局唯一的符號(hào),而Symbol('x')每次都會(huì)創(chuàng)建一個(gè)新符號(hào),
雖然他們的字符串表示都是'Symbol(x)'。
其次,Symbol類型值的包裝對(duì)象,不能通過new Symbol來獲得,
new Symbol('x'); // Uncaught TypeError: Symbol is not a constructor
只能通過Object(symbol)來獲得,
s = Symbol.for('x');
obj = Object(s); // Symbol {Symbol(x)},obj是Symbol構(gòu)造函數(shù)的實(shí)例
obj instanceof Symbol; // true
最后,在非嚴(yán)格模式下,this指向的原始值會(huì)自動(dòng)轉(zhuǎn)換為它的包裝對(duì)象,
function f(){
return this;
}
a = 1;
obj = f.call(sa; // Number {1},obj是Number的實(shí)例
obj instanceof Number; // true
即,這里隱式調(diào)用了Object()方法。
| 原始值 | Object(...) | valueOf(...) |
|---|---|---|
| null | new Object | new Object |
| undefined | new Object | new Object |
| 1 | new Number(1) | 1 |
| 'x' | new String('x') | 'x' |
| true | new Boolean(true) | true |
| Symbol.for('x') | Object(Symbol.for('x')) | Symbol(x) |
如上表,除了null和undefined之外,
Object(...)可以將原始值轉(zhuǎn)換為對(duì)應(yīng)的包裝對(duì)象,
而valueOf可以將包裝對(duì)象轉(zhuǎn)換為對(duì)應(yīng)的原始值。
8.3 toPrimitive,valueOf和toString
Symbol.toPrimitive是一個(gè)語言內(nèi)置的符號(hào),
當(dāng)一個(gè)對(duì)象需要轉(zhuǎn)換為原始值的時(shí)候,首先會(huì)調(diào)用它自己的Symbol.toPrimitive方法。
例如,我們可以為對(duì)象定義一個(gè)Symbol.toPrimitive方法,
來影響它被轉(zhuǎn)換為原始值的方式。
obj1 = {};
+obj1; // NaN
`${obj1}`; // '[object Object]'
obj1+''; // '[object Object]'
obj1[Symbol.toPrimitive]; // undefined
obj1[Symbol.toPrimitive] = function(hint){
switch(hint){
case 'number':
return 1;
case 'string':
return 'x';
default:
return hint;
}
};
+obj1; // 1,hint === 'number'
`${obj1}`; // 'x',hint === 'string'
obj1 + ''; // 'default',hint === 'default'
obj1 + 0; // 'default',hint === 'default'
如果Symbol.toPrimitive方法沒有被定義,就會(huì)調(diào)用toString或valueOf來獲取原始值,
當(dāng)hint是string時(shí),會(huì)依次調(diào)用toString,以及valueOf,直到返回一個(gè)非Object的值為止。
當(dāng)hint是default或number時(shí),會(huì)依次調(diào)用valueOf,以及toString,直到返回一個(gè)非Object的值為止。
obj1 = {};
obj1.toString = function(){
return true;
};
obj1.valueOf = function(){
return false;
};
+obj1; // 0,hint === 'number',調(diào)用valueOf返回false,再+false為0
`${obj1}`; // 'true',hint === 'string',調(diào)用toString返回true,再`${true}`為'true'
obj1 + ''; // 'false',hint === 'default',調(diào)用valueOf返回false,再false+''為'false'
obj1 + 0; // 'false',hint === 'default',調(diào)用valueOf返回false,再false+0為0