1.1 該如何對待 JavaScript
JavaScript 就是 JavaScript。要用 JavaScript 的方式寫 JavaScript失受。不要強(qiáng)制使用任何范式,特別是 OOP纯续。
JavaScript 中最強(qiáng)大的兩點是對象和函數(shù)蔬充。JavaScript 的對象不需要類即可存在。多數(shù)情況下普通對象就可以完成任務(wù)笛匙。原型繼承(prototypal inheritance)也足以快速滿足常見需求侨把。真正的 OOP 語法、類妹孙、繼承只有在必要的使用才需要使用秋柄,且此時應(yīng)使用 class
語法,不要自己制造新語法糖蠢正。
JavaScript 函數(shù)是一等公民骇笔。JavaScript 是第一個主流的 lambda 語言。
1.2 基礎(chǔ)語法
兩種注釋:/* */
和 //
嚣崭。
除非必要笨触,否則不要使用分號。
1.3 變量定義
常量(ES6)
const MYCONSTANT = "After declaration no changing"
變量
定義變量雹舀,可以使用 let
(ES6) 和 var
芦劣。
二者的區(qū)別是 let
定義的變量具有塊級作用域,var
定義的變量具有詞法作用域(函數(shù)作用域)说榆。塊級作用域由塊級語句(if
/ for
/ while
/ try
)或大括號({}
)形成虚吟。而函數(shù)作用域指一個函數(shù)內(nèi)都是同一個作用域,不管里面是否有嵌套的塊娱俺。
現(xiàn)在稍味,一律使用 let
,不要使用 var
荠卷。
例子模庐,下面兩個 x
是兩個獨(dú)立的變量,在各自的作用域中油宜。(內(nèi)層變量與外層同名是不好的習(xí)慣掂碱,這里僅是演示,不要這樣做I髟)
let x = 10
{
let x = 20
console.log(x) // 20
}
console.log(x) // 10
下面的代碼疼燥,兩個 a
實際是同一個。
function test() {
var a = 10
if(something) {
var a = 20
}
console.log(a) // 20
}
上面的例子也說明蚁堤,var
允許在被一個作用域內(nèi)被重復(fù)定義醉者,但 let
不允許。
function test() {
var b = 1
var b = 2 // 可以
let a = 10
let a = 20 // 報錯!
}
var
跟 let
還有一個區(qū)別撬即,var
允許定義在使用之后立磁,而 let
不允許。原因是剥槐,var
定義的變量在運(yùn)行時會被提到函數(shù)作用域的頂部先執(zhí)行唱歧,不管它出現(xiàn)的實際位置。比如
function test() {
console.log(b) // undefined 但不報錯
b = 20
console.log(b) // 20
var b = 10
console.log("hello")
var c = 20
}
因為在運(yùn)行時(或者說預(yù)編譯期)粒竖,所有 var
會被提到函數(shù)頂部(賦值不會提前)颅崩,效果相對于:
function test() {
var b,c
console.log(b) // undefined 但不報錯
b = 20
console.log(b) // 20
b = 10
console.log("hello")
c = 20
}
因此過去,使用 var
的時代蕊苗,建議把所有的變量定義放在函數(shù)開始沿后,以免造成錯覺。(CoffeeScript 等方言編譯結(jié)果會自動這樣做朽砰。)
最后再強(qiáng)調(diào)得运,let
定義的變量必須先定義再使用。
1.4 JavaScript 類型概述
JavaScript 是弱類型的锅移、動態(tài)類型的。弱類型的意思是饱搏,定義變量時不需要指定變量類型非剃。動態(tài)類型的意思是,一個變量可以持有不同類型的值推沸,如:
let a = 10
a = "aaa"
狹義講备绽,JavaScript 類型包括:布爾、數(shù)字鬓催、字符串肺素、undefined、對象宇驾、函數(shù)倍靡。
注意 null
、數(shù)組课舍、正則表達(dá)式的類型都是 "object"
塌西。
typeof true // "boolean"
typeof 1 // "number"
typeof "a" // "string"
typeof (new Date()) // "object"
typeof null // "object"
typeof undefined // "undefined"
typeof [] // "object"
typeof {} // "object"
typeof /good/ // "object"
typeof function(){} // "function"
布爾有兩個值 true
和 false
。
對象通過引用來傳遞筝尾。它們永遠(yuǎn)不會被拷貝捡需。
字面量
字面量包括數(shù)字字面量、字符串字面量筹淫、對象字面量站辉、數(shù)組字面量、函數(shù)字面量、正則表達(dá)式字面量饰剥。
對象字面量殊霞,如:{a: 1, b: true}
。數(shù)組字面量捐川,如:[1, 2, 'a', 4]
脓鹃。
正則表達(dá)式字面量被兩個斜杠包圍。如:/(.*)[a-z]/
古沥。
1.5 數(shù)字
JavaScript 只有一種數(shù)字類型瘸右。內(nèi)部表示為 64 位的浮點數(shù)。沒有單獨(dú)的整數(shù)岩齿,因此 1 和 1.0 是相同值太颤。
二進(jìn)制浮點數(shù)不能正確處理十進(jìn)制小數(shù)。因此 0.1 + 0.2
不等于 0.3
盹沈。這是 JavaScript 經(jīng)常被報告的 BUG龄章,并且它是遵循二進(jìn)制浮點數(shù)算術(shù)標(biāo)準(zhǔn)(IEEE 754)而有意導(dǎo)致的結(jié)果。
幸好浮點運(yùn)算中的整數(shù)運(yùn)算是精確的乞封,可以通過擴(kuò)大精度避免小數(shù)做裙。如將貨幣單位設(shè)為分而不是元。
如果確定只有整數(shù)肃晚,可以直接使用 ===
>
等比較兩個變量或值是否相等锚贱。如果有小數(shù)部分,判斷相等最好使用插值法:
if( a - b > 1e-10 )...
NaN
和 Infinity
JavaScript 在運(yùn)算發(fā)生上溢关串、下溢或除零操作時不會報錯拧廊。
當(dāng)運(yùn)算符結(jié)果超過了 JavaScript 所能表示的數(shù)字上限(溢出)(1.79769313486231570e+308
),結(jié)果為一個特殊的無窮大的值晋修,即 Infinity
吧碾。同樣的,當(dāng)負(fù)數(shù)超過能表示的范圍墓卦,結(jié)果為負(fù)無窮大倦春,用 -Infinity
表示。無窮大的值的加減乘除運(yùn)算的結(jié)果仍為無窮大趴拧。
下溢即運(yùn)算結(jié)果無限接近于零比 JavaScript 能表示的最小值還小的時候發(fā)生溅漾。此時 JavaScript 返回零。
被零整除并不報錯著榴,返回 Infinity
或 -Infinity
添履。
但零除以零是沒有意義的,結(jié)果是 NaN
脑又。無窮大除以無窮大暮胧、給任意負(fù)數(shù)開方運(yùn)算或算數(shù)運(yùn)算符與不是數(shù)字或無法轉(zhuǎn)換為數(shù)字的操作數(shù)一起使用時都將返回 NaN
锐借。typeof NaN === 'number'
。
只能使用 isNaN
函數(shù)判斷一個值是否為數(shù)字。NaN
不等于自己。
NaN === NaN // false
NaN !== NaN // true
isNaN(NaN) // true
isNaN(0) // false
isNaN('oops') // true
isNaN('0') // false
判斷一個值是否可用于數(shù)字運(yùn)算的最佳方式是使用 isFinite()
函數(shù)叶圃,因為它會篩選掉 NaN
和 Infinity
。不幸的是布轿,如果操作數(shù)不是數(shù)字,isFinite()
會試圖將操作數(shù)轉(zhuǎn)換為數(shù)字来颤。因此汰扭,最好定義函數(shù):
function isNumber(value) {
return typeof value === 'number' && isFinite(value)
}
1.6 字符串
字符串字面量可以被包圍在單引號或雙引號中。
Javascript 沒有字符類型(只有字符串)福铅。
字符串有一個 length
屬性萝毛。
字符串是不可變的。
可以使用 +
連接字符串滑黔。
模板字符串(ES6)
`This is a pretty little template string.`
// 多行
`In ES5 this is
not legal.`
// 插值
let name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`
// 綜合例子:
GET`http://foo.org/bar?a=${a}&b=$笆包
Content-Type: application/json
X-Credentials: ${credentials}
{ "foo": ${foo},
"bar": ${bar}}`(myOnReadyStateChangeHandler);
1.7 對象
對象字面量即通過 {}
定義的對象。
在對象字面量中略荡,若屬性名是一個合法的 JavaScript 標(biāo)識符且不是保留字庵佣,可以不使用引號括住屬性名 。例如汛兜,"first–name"
的引號是必需的秧了,first_name
可以不用引號。
ES5 允許對象字面量最后由一個多余的逗號序无,但在 IE 下會報錯。
增強(qiáng)的對象字面量(ES6)
簡寫 foo: foo
形式的賦值:
var a = 1
var obj = { a: a }
// 等價于
var obj = { a }
定義方法:
var obj = {
work: function() {}
}
// 等價于
var obj = {
work() {}
}
調(diào)用父類方法:
var obj = {
toString() {
return "d " + super.toString()
}
}
計算的(動態(tài)的)屬性名:
var fieldName = "aaa"
var obj = {[fieldName]: 1}
// 等價于
var obj = {}
obj[fieldName] = 1
訪問字段
如果字符串是一個合法的 JavaScript 標(biāo)識符且非保留字衡创,可以使用 .
否則應(yīng)使用中括號帝嗡。
stooge["first-name"]
flight.departure.IATA
如果檢索的屬性不存在,返回 undefined
璃氢;注意不是 null
哟玷。
嘗試從不存在的對象(undefined
)讀取字段將拋出 TypeError
異常∫灰玻可以使用 &&
預(yù)作判斷巢寡。
flight.equipment // undefined
flight.equipment.model // throw "TypeError"
flight.equipment && flight.equipment.model // undefined
1.8 null
和 undefined
判斷一個值是否是 null
,直接使用 ===
my_value === null
undefined
這個值盡量不要自己使用椰苟。但仍有時候值可能是 undefined
抑月。比如訪問一個不存在的屬性,返回 undefined
舆蝴。沒有返回值的方法谦絮,賦值給一個變量题诵,得到 undefined
。
要判斷一個屬性是否存在层皱,值為 null
也算存在性锭,可以用 in
運(yùn)算符:
var a = { b: 1, d: null }
"b" in a // true
"c" in a // false
"d" in a // true
如果一定要判斷值是否為 undefined
,可以檢測 typeof v === "undefined"
叫胖。
1.9 數(shù)組
JavaScript 中沒有真正的數(shù)組草冈,而是提供一些類數(shù)組的對象。它把數(shù)組下表轉(zhuǎn)換為字符串瓮增,用其作為屬性怎棱。它明顯比真正的數(shù)組慢。
JavaScript 本身對于數(shù)組和對象的區(qū)別是混亂的钉赁。typeof
運(yùn)算符報告數(shù)組的類型是 'object'
蹄殃,這沒有什么意義。
數(shù)組字面量
方括號內(nèi)你踩,逗號分隔诅岩。
var empty = []
var numbers = [ 'zero', 'one', 'two', 'three', 'four' ]
empty[1] // undefined
numbers[1] // 'one'
empty.length // 0
numbers.length // 5
數(shù)組下標(biāo)以 0 開始。
數(shù)組字面量繼承自(原型是)Array.prototype
带膜。
允許數(shù)組元素為混合類型(JavaScript 是弱類型):
var misc = [ 'string', 98.6, true, false, null, undefined, ['nested', 'array'] ]
JavaScript 沒有多維數(shù)組吩谦,但支持元素為數(shù)組的數(shù)組:
var matrix = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8]
];
matrix[2][1] // 7
讀寫數(shù)組元素
下標(biāo)運(yùn)算符 []
將其中的表達(dá)式轉(zhuǎn)換為一個字符串,轉(zhuǎn)換使用表達(dá)式的 toString
方法膝藕。產(chǎn)生的字符串將作為屬性名式廷。
因為數(shù)字的下標(biāo)最終會被轉(zhuǎn)換為一個“鍵”。因此數(shù)組允許越界訪問芭挽,或者說任意訪問滑废。例如開始有:
var arr = ["a", "b"]
讀取 arr[10]
時,因為以 10 為鍵的元素不存在袜爪,因此得到 undefined
蠕趁。
arr[10] = "xxx"
,給數(shù)組第 11 個元素賦值辛馆,相對于將數(shù)組長度擴(kuò)大到 11俺陋。此時 arr[2]
到 arr[9]
都是 undefined
。length
屬性為 11昙篙。
直接給 length
賦值腊状,相當(dāng)于將數(shù)組設(shè)為指定大小。這個可以比原數(shù)組更大(相對于擴(kuò)大)或更刑伞(相對于截短)缴挖。
向數(shù)組最后追加元素的方法是 push()
:
numbers.push('go')
// numbers is ['zero', 'one', 'two', 'shi', 'go']
刪除
使用 delete
刪除數(shù)組元素,但會留下“空洞”焚辅。delete
不是從數(shù)組中移除一個元素(后續(xù)元素上提)醇疼,而是將指定位置上的值置為 undefined
硕并。
delete numbers[2]
// numbers is ['zero', 'one', undefined, 'shi', 'go']
splice()
用于去除數(shù)組中一部分。第一個參數(shù)指定開始刪除的位置秧荆,第二個參數(shù)指定刪除個數(shù)倔毙。
numbers.splice(2, 1)
// numbers is ['zero', 'one', 'shi', 'go']
要對被刪除的元素之后的每個元素調(diào)整下標(biāo)值,對大數(shù)組來說效率很低乙濒。