第一章 類型
js中的類型
js語言中的類型分為基本類型和對(duì)象類型
類型有:(除對(duì)象外,其他統(tǒng)稱為基本類型)
- 空值(null)
- 未定義(undefined)
- 布爾值(boolean)
- 數(shù)字(number)
- 字符串(string)
- 對(duì)象(object)
- 符號(hào)(symbol,ES6中新增)
如何判別值的類型
typeof運(yùn)算符可以查看值的類型,他返回的是類型的字符串值,但是這七種值和他們的字符串值并不一一對(duì)應(yīng)
typeof undefined === "undefined"; // true
typeof true === "boolean"; // true
typeof 42 === "number"; // true
typeof "42" === "string"; // true
typeof { life: 42 } === "object"; // true
// ES6中新加入的類型
typeof Symbol() === "symbol"; // true
以上六種判別均正常,但是null例外
typeof null === "object"; // true
我們需要使用復(fù)合條件來檢測(cè) null 值的類型
var a = null;
(!a && typeof a === "object"); // true
還有一種情況:
typeof function a(){ /* .. */ } === "function"; // true
typeof [1,2,3] === "object"; // true
undefined 和 undeclared
變量在未持有值的時(shí)候?yàn)?undefined。此時(shí) typeof 返回 "undefined
var a;
typeof a; // "undefined"
var b = 42;
var c;
// later
b = c;
typeof b; // "undefined"
typeof c; // "undefined"
大多數(shù)開發(fā)者傾向于將 undefined 等同于 undeclared(未聲明),但在 JavaScript 中它們完全
是兩回事。
已在作用域中聲明但還沒有賦值的變量,是 undefined 的。
相反,還沒有在作用域中聲明過的變量,是 undeclared 的。
var a;
a; // undefined
b; // ReferenceError: b is not defined
更讓人抓狂的是 typeof 處理 undeclared 變量的方式。例如:
var a;
typeof a; // "undefined"
typeof b; // "undefined"
對(duì)于 undeclared(或者 not defined)變量,typeof 照樣返回 "undefined"。
請(qǐng)注意雖然 b 是
一個(gè) undeclared 變量,但 typeof b 并沒有報(bào)錯(cuò)。這是因?yàn)?typeof 有一個(gè)特殊的安全防范機(jī)制。
typeof Undeclared
該安全防范機(jī)制對(duì)在瀏覽器中運(yùn)行的 JavaScript 代碼來說還是很有幫助的,
因?yàn)槎鄠€(gè)腳本文件會(huì)在共享的全局命名空間中加載變量。
// 這樣會(huì)拋出錯(cuò)誤
if (DEBUG) {
console.log( "Debugging is starting" );
}
// 這樣是安全的
if (typeof DEBUG !== "undefined") {
console.log( "Debugging is starting" );
}
//這不僅對(duì)用戶定義的變量(比如 DEBUG)有用,對(duì)內(nèi)建的 API 也有幫助:
if (typeof atob === "undefined") {
atob = function() { /*..*/ };
}
還有一種不用通過 typeof 的安全防范機(jī)制的方法,就是檢查所有全局變量是否是全局對(duì)象
的屬性,瀏覽器中的全局對(duì)象是 window。所以前面的例子也可以這樣來實(shí)現(xiàn):
if (window.DEBUG) {
// ..
}
if (!window.atob) {
// ..
}
與 undeclared 變量不同,訪問不存在的對(duì)象屬性(甚至是在全局對(duì)象 window 上)不會(huì)產(chǎn)生
ReferenceError 錯(cuò)誤。
一些開發(fā)人員不喜歡通過 window 來訪問全局對(duì)象,尤其當(dāng)代碼需要運(yùn)行在多種 JavaScript
環(huán)境中時(shí)(不僅僅是瀏覽器,還有服務(wù)器端,如 node.js 等),因?yàn)榇藭r(shí)全局對(duì)象并非總是
window。
小結(jié)
JavaScript 有 七 種 內(nèi) 置 類 型:null、undefined、boolean、number、string、object 和
symbol,可以使用 typeof 運(yùn)算符來查看。
變量沒有類型,但它們持有的值有類型。類型定義了值的行為特征。
很多開發(fā)人員將 undefined 和 undeclared 混為一談,但在 JavaScript 中它們是兩碼事。
undefined 是值的一種。undeclared 則表示變量還沒有被聲明過。
遺憾的是,JavaScript 卻將它們混為一談,在我們?cè)噲D訪問 "undeclared" 變量時(shí)這樣報(bào)錯(cuò):ReferenceError: a is not defined,并且 typeof 對(duì) undefined 和 undeclared 變量都返回
"undefined"。
然而,通過 typeof 的安全防范機(jī)制(阻止報(bào)錯(cuò))來檢查 undeclared 變量,有時(shí)是個(gè)不錯(cuò)的
辦法。
第二章 值
數(shù)組
和其他強(qiáng)類型語言不同,在 JavaScript 中,數(shù)組可以容納任何類型的值,可以是字符串、
數(shù)字、對(duì)象(object),甚至是其他數(shù)組(多維數(shù)組就是通過這種方式來實(shí)現(xiàn)的)
使用 delete 運(yùn)算符可以將單元從數(shù)組中刪除,但是請(qǐng)注意,單元?jiǎng)h除后,數(shù)
組的 length 屬性并不會(huì)發(fā)生變化。第 5 章將詳細(xì)介紹 delete 運(yùn)算符。
數(shù)組通過數(shù)字進(jìn)行索引,但有趣的是它們也是對(duì)象,所以也可以包含字符串鍵值和屬性
(但這些并不計(jì)算在數(shù)組長(zhǎng)度內(nèi))
var a = [ ];
a[0] = 1;
a["foobar"] = 2;
a.length; // 1
a["foobar"]; // 2
a.foobar; // 2
這里有個(gè)問題需要特別注意,如果字符串鍵值能夠被強(qiáng)制類型轉(zhuǎn)換為十進(jìn)制數(shù)字的話,它
就會(huì)被當(dāng)作數(shù)字索引來處理。
var a = [ ];
a["13"] = 42;
a.length; // 14
在數(shù)組中加入字符串鍵值 / 屬性并不是一個(gè)好主意。建議使用對(duì)象來存放鍵值 / 屬性值,
用數(shù)組來存放數(shù)字索引值。
類數(shù)組
有時(shí)需要將類數(shù)組(一組通過數(shù)字索引的值)轉(zhuǎn)換為真正的數(shù)組,這一般通過數(shù)組工具函數(shù)(如 indexOf(..)、concat(..)、forEach(..) 等)來實(shí)現(xiàn)。
例如,一些 DOM 查詢操作會(huì)返回 DOM 元素列表,它們并非真正意義上的數(shù)組,但十分
類似。另一個(gè)例子是通過 arguments 對(duì)象(類數(shù)組)將函數(shù)的參數(shù)當(dāng)作列表來訪問(從
ES6 開始已廢止)。
工具函數(shù) slice(..) 經(jīng)常被用于這類轉(zhuǎn)換:
function foo() {
var arr = Array.prototype.slice.call( arguments );
arr.push( "bam" );
console.log( arr );
}
foo( "bar", "baz" ); // ["bar","baz","bam"]
用 ES6 中的內(nèi)置工具函數(shù) Array.from(..) 也能實(shí)現(xiàn)同樣的功能:
...
var arr = Array.from( arguments );
...
字符串
許多數(shù)組函數(shù)用來處理字符串很方便。雖然字符串沒有這些函數(shù),但可以通過“借用”數(shù)
組的非變更方法來處理字符串:
var a = "foo";
var b = ["f","o","o"];
a.join; // undefined
a.map; // undefined
var c = Array.prototype.join.call( a, "-" );
var d = Array.prototype.map.call( a, function(v){
return v.toUpperCase() + ".";
} ).join( "" );
c; // "f-o-o"
d; // "F.O.O."
另一個(gè)不同點(diǎn)在于字符串反轉(zhuǎn)(JavaScript 面試常見問題)。數(shù)組有一個(gè)字符串沒有的可變更成員函數(shù) reverse():
a.reverse; // undefined
b.reverse(); // ["!","o","O","f"]
b; // ["f","O","o","!"]
可惜我們無法“借用”數(shù)組的可變更成員函數(shù),因?yàn)樽址遣豢勺兊模?/p>
Array.prototype.reverse.call( a );
// 返回值仍然是字符串"foo"的一個(gè)封裝對(duì)象(參見第3章):(
// 在瀏覽器中會(huì)報(bào)以下錯(cuò)誤

一個(gè)變通(破解)的辦法是先將字符串轉(zhuǎn)換為數(shù)組,待處理完后再將結(jié)果轉(zhuǎn)換回字符串:
這種方法的確簡(jiǎn)單粗暴,但對(duì)簡(jiǎn)單的字符串卻完全適用
var c = a
// 將a的值轉(zhuǎn)換為字符數(shù)組
.split( "" )
// 將數(shù)組中的字符進(jìn)行倒轉(zhuǎn)
.reverse()
// 將數(shù)組中的字符拼接回字符串
.join( "" );
c; // "oof"
數(shù)字
數(shù)字的語法
var a = 42;
var b = 42.3;
小數(shù)點(diǎn)前面的 0 可以省略:
var a = 42.0;
var b = 42.;
默認(rèn)情況下大部分?jǐn)?shù)字都以十進(jìn)制顯示,小數(shù)部分最后面的 0 被省略,如:
var a = 42.300;
var b = 42.0;
a; // 42.3
b; // 42
特別大和特別小的數(shù)字默認(rèn)用指數(shù)格式顯示,與 toExponential() 函數(shù)的輸出結(jié)果相同。
例如:
var a = 5E10;
a; // 50000000000
a.toExponential(); // "5e+10"
var b = a * a;
b; // 2.5e+21
var c = 1 / a;
c; // 2e-11
由于數(shù)字值可以使用 Number 對(duì)象進(jìn)行封裝(參見第 3 章),因此數(shù)字值可以調(diào)用 Number.prototype 中的方法(參見第 3 章)。例如,tofixed(..) 方法可指定小數(shù)部分的顯示位數(shù):
var a = 42.59;
a.toFixed( 0 ); // "43"
a.toFixed( 1 ); // "42.6"
a.toFixed( 2 ); // "42.59"
a.toFixed( 3 ); // "42.590"
a.toFixed( 4 ); // "42.5900"
請(qǐng)注意,上例中的輸出結(jié)果實(shí)際上是給定數(shù)字的字符串形式,如果指定的小數(shù)部分的顯示
位數(shù)多于實(shí)際位數(shù)就用 0 補(bǔ)齊。