第二章 語法
數(shù)字
JavaScript 只有一個數(shù)字類型啰脚,它在內(nèi)部被表示為64位的浮點(diǎn)數(shù),和Java的double數(shù)字類型一樣。它沒有分離出整數(shù)類型,所以1和1.0的值相同匙握,完全避免了一大堆因數(shù)字類型導(dǎo)致的錯誤。
字符串
字符串字面量可以被包在一對單引號或雙引號中陈轿,可能包含0個或多個字符圈纺。
JavaScript沒有字符類型,要表示一個字符麦射,只需要創(chuàng)建一個字符的字符串即可蛾娶。
字符串是不可變的,一旦字符串被創(chuàng)建潜秋,就永遠(yuǎn)無法改變它蛔琅,兩個包含著完全相同的字符且字符順序也相同的字符串被認(rèn)為是相同的字符串。
'c' + 'a' + 't' === 'cat' // true
語句
每個<script>標(biāo)簽提供一個被編譯且立即執(zhí)行的編譯單元
與其他語言不同峻呛,JavaScript中的代碼塊不會創(chuàng)建新的作用域罗售,因此變量應(yīng)該被定義在函數(shù)的頭部,而不是在代碼塊中杀饵。
var scope = 'global'
function f() {
console.log(scope) // 輸出'undefined'莽囤,而不是'global'
var scope = 'local' // 變量在這里賦初始值谬擦,但變量本身在函數(shù)體內(nèi)任何地方均是有定義的
console.log(scope) // 輸出'local'
}
等價于:
var scope = 'global'
function f() {
var scope
console.log(scope) // 輸出'undefined'切距,而不是'global'
scope = 'local' // 變量在這里賦初始值,但變量本身在函數(shù)體內(nèi)任何地方均是有定義的
console.log(scope) // 輸出'local'
}
布爾值的判斷中惨远,以下列出的值被當(dāng)作假谜悟,除此外话肖,均為真:
- false
- null
- 空字符串' '
- 數(shù)字 0
- 數(shù)字 NaN
typeof 運(yùn)算符產(chǎn)生的值有:
- number
- string
- boolean
- undefined
- function
- object
第三章 對象
JavaScript的簡單數(shù)據(jù)類型包括數(shù)字、字符串葡幸、布爾值(true和false)最筒、null值和undefined值,其他所有的值都是對象蔚叨。
數(shù)字床蜘、字符串、布爾值“貌似”對象蔑水,因?yàn)樗鼈儞碛蟹椒ㄐ暇猓鼈兪遣豢勺兊摹?br>
對象是屬性的容器,其中每個屬性都擁有名字和值搀别。屬性的名字和屬性的值沒有限制丹擎。對象適合用于匯集和管理數(shù)據(jù)。對象可以包含其他對象歇父,所以它們可以容易地表示成樹狀或圖形結(jié)構(gòu)蒂培。
對象
如果屬性名是一個合法的JavaScript標(biāo)識符且不是保留字,則并不強(qiáng)制要求用引號括住屬性名榜苫。
var empty_object = {}
var stooge = {
"first-name": "Jerome", // 連接符(-)是不合法的护戳,要加引號
last_name: "Howard", // 下劃線(_)是合法的,可以不用加引號
}
對象嵌套
var flight = {
airline: "Oceanic",
number: 815,
departure: {
IATA: "SYD",
time: "2004-09-22 14:55",
city: "Sydney"
},
arrival: {
IATA: "LAX"
time: "2004-09-23 10:42",
city: "Los Angeles"
}
}
檢索
stooge["first-name"] // 采用在[]后綴中括住一個字符串表達(dá)式
flight.departure.IATA // 如果字符串表達(dá)式是一個字符串字面量垂睬,并且是一個合法的JavaScript標(biāo)識符且不是保留字灸异,那么可以用 . 表示法代替
// 嘗試檢索一個并不存在的成員屬性的值,將返回undefined
stooge["middle-name"] // undefined羔飞,
flight.status // undefined肺樟,同上
// ||運(yùn)算符可以用來填充默認(rèn)值
var middle = stooge["middle-name"] || "(none)"
// 嘗試從undefined的成員屬性中取值將會導(dǎo)致TypeError異常。這時可以通過&&運(yùn)算符來避免錯誤逻淌。
flight.equipment // undefined
flight.equipment.model // throw "TypeError"
flight.equipment && flight.equipment.model // undefined
更新
對象里的值可以通過賦值語句來更新么伯。如果屬性名已經(jīng)存在于對象里,那么這個屬性的值就會被替換卡儒。如果對象之前沒有擁有那個屬性名田柔,那么該屬性就被擴(kuò)充到對象中。
引用
對象通過引用來傳遞骨望,它們永遠(yuǎn)不會被復(fù)制硬爆。
原型
每個對象都連接到一個原型對象,并且它可以從中繼承屬性擎鸠。所有通過對象字面量創(chuàng)建的對象連接到Object.prototype缀磕,它是JavaScript中的標(biāo)配對象。
當(dāng)創(chuàng)建一個新對象時,我們可以選擇某個對象作為它的原型袜蚕。
if (typeof Object.beget !=== 'function') {
Object.create = function(o) {
var F = function() {}
F.prototype = o
return new F()
}
}
var another_stooge = Object.create(stooge)
// 原型連接在更新時是不起作用的糟把。當(dāng)我們對某個對象作出改變時,不會觸及該對象的原型
another_stooge['first-name'] = 'Harry'
another_stooge['middle-name'] = 'Moses'
another_stooge.nickname = 'Moe'
// 原型關(guān)系是一種動態(tài)的關(guān)系牲剃。如果我們添加一個新的屬性到原型中遣疯,該屬性會立即對所有基于該原型創(chuàng)建的對象可見。
stooge.profession = 'actor'
another_stooge.profession // 'actor'
刪除
delete 運(yùn)算符可以用來刪除對象的屬性凿傅。如果對象包含該屬性缠犀,那么該屬性就會被移除。它不會觸及原型鏈中的任何對象聪舒。
刪除對象的屬性可能會讓來自原型鏈中的屬性透現(xiàn)出來:
another_stooge.nickname // 'Moe'
// 刪除 another_stooge 的 nickname 屬性夭坪,從而暴露出原型的nickname屬性
delete another_stooge.nickname
another_stooge.nickname // 'Curly'
減少全局變量污染
最小化使用全局變量的方法之一是為你的應(yīng)用只創(chuàng)建一個唯一的全局變量。
第四章 函數(shù)
JavaScript設(shè)計(jì)得最出色的就是它的函數(shù)的實(shí)現(xiàn)过椎,它幾乎接近于完美室梅。
函數(shù)用于代碼復(fù)用、信息隱藏和組合調(diào)用疚宇,函數(shù)用于指定對象的行為亡鼠。一般來說,所謂編程敷待,就是將一組需求分解成一組函數(shù)與數(shù)據(jù)結(jié)構(gòu)的技能间涵。
函數(shù)對象
JavaScript中的函數(shù)就是對象。對象是“名/值”對的集合并擁有一個連接到原型對象的隱藏連接榜揖。對象字面量產(chǎn)生的對象連接到Object.prototype勾哩。函數(shù)對象連接到 Function.prototype(該原型對象本身連接到Object.prototype)。每個函數(shù)在創(chuàng)建時會附加兩個隱藏屬性:函數(shù)的上下文和實(shí)現(xiàn)函數(shù)行為的代碼举哟。
函數(shù)字面量
當(dāng)沒有給函數(shù)命名思劳,就是匿名函數(shù),如下例妨猩。
// 創(chuàng)建一個名為 add 的變量潜叛,并用來把兩個數(shù)字相加的函數(shù)賦值給它
var add = function(a, b) {
return a + b
}
調(diào)用
除了聲明時定義的形式參數(shù),每個函數(shù)還接收兩個附加的參數(shù):
- this
- arguments
其中參數(shù) this 在面向?qū)ο缶幊讨蟹浅V匾瑁闹等Q于調(diào)用的模式威兜。在 JavaScript 中一共有四種調(diào)用模式:
- 方法調(diào)用模式
- 函數(shù)調(diào)用模式
- 構(gòu)造器調(diào)用模式
- apply 調(diào)用模式
其中,這些模式在如何初始化關(guān)鍵參數(shù) this 上存在差異庐椒。
當(dāng)實(shí)際參數(shù)(arguments)的個數(shù)與形式參數(shù)(parameters)的個數(shù)不匹配時椒舵,不會導(dǎo)致運(yùn)行時錯誤。如果實(shí)際參數(shù)值過多了约谈,超出的參數(shù)值會被忽略笔宿。如果實(shí)際參數(shù)值過少犁钟,缺失的值會被替換為 undefined。對參數(shù)值不會進(jìn)行類型檢查:任何類型的值都可以被傳遞給任何參數(shù)措伐。
方法調(diào)用模式
當(dāng)一個函數(shù)被保存為對象的一個屬性時特纤,我們稱它為一個方法军俊。當(dāng)一個方法被調(diào)用時侥加,this 被綁定到該對象。 this 到對象的綁定發(fā)生在調(diào)用的時候粪躬。這個“超級”延遲綁定(very late binding)使得函數(shù)可以對this高度復(fù)用担败。通過this可取得它們所屬對象的上下文的方法稱為公共方法(public method)
// 創(chuàng)建 my Object 對象。它有一個 value 屬性和一個 increment 方法镰官。
var myObject = {
value: 0,
increment: function(inc) {
this.value += typeof inc === 'number' ? inc : 1;
}
}
myObject.increment()
document.writeln(myObject.value) // 1
myObject.increment(2)
document.writeln(myObject.value) // 3
函數(shù)調(diào)用模式
var add = add(3, 4)
以此模式調(diào)用函數(shù)時提前,this 被綁定到全局對象。這時語言設(shè)計(jì)上的一個錯誤泳唠。倘若語言設(shè)計(jì)正確狈网,那么當(dāng)內(nèi)部函數(shù)被調(diào)用時,this 應(yīng)該仍然綁定到外部函數(shù)的 this 變量笨腥。這個設(shè)計(jì)的后果就是方法不能利用內(nèi)部函數(shù)來幫助它工作拓哺,因?yàn)閮?nèi)部函數(shù)的 this 被綁定到了錯誤的值,所以不能共享該方法對對象的訪問權(quán)脖母。解決方法:該方法定義一個變量并給它賦值為 this士鸥,那么內(nèi)部函數(shù)就可以通過那個變量訪問到 this。
myObject.double = function() {
var self = this // this 綁定到myObject
var helper = function() {
self.value = add(self.value, self.value) // 內(nèi)部函數(shù)綁定到全局對象
}
helper()
}
myObject.double()
document.writeln(myObject.value) // 6
構(gòu)造器調(diào)用模式
JavaScript 是一門基于原型繼承的語言谆级。這意味著對象可以直接從其他對象繼承屬性烤礁。該語言是無類型的。
當(dāng)今大多數(shù)語言都是基于類的語言肥照。盡管原型繼承極富表現(xiàn)力脚仔,但它并未被廣泛理解。JavaScript 本身對它原型的本質(zhì)也缺乏信心舆绎,所以它提供了一套和基于類的語言類似的對象構(gòu)建語法玻侥。
// 構(gòu)造器調(diào)用模式
var Quo = function(string) {
this.status = string
}
Quo.prototype.get_status = function() {
return this.status
}
var myQuo = new Quo('confused')
document.writeln(myQuo.get_status())
一個函數(shù),如果創(chuàng)建的母的就是希望結(jié)合 new 前綴來調(diào)用亿蒸,那它就被稱為構(gòu)造器函數(shù)凑兰。按照約定,它們保存在以大寫格式命名的變量里边锁。如果調(diào)用構(gòu)造器函數(shù)時沒有在前面加上 new姑食,可能會發(fā)生非常糟糕的事情,既沒有編譯時警告茅坛,也沒有運(yùn)行時警告音半,所以大寫約定非常重要则拷。
Apply 調(diào)用模式
// 《JavaScript 權(quán)威指南》中對 call() 和 apply() 的解釋如下:
f.call(o)
f.apply(o)
// 每行代碼和下面代碼的功能類似(假設(shè)對象o中不存在名為m的屬性)
o.m = f
o.m()
delete o.m
參數(shù)
當(dāng)函數(shù)被調(diào)用時,會得到一個“免費(fèi)”配送的參數(shù)曹鸠,那就是arguments數(shù)組煌茬。函數(shù)可以通過此參數(shù)訪問所有它被調(diào)用時傳遞給它的參數(shù)列表。這使得編寫一個無須指定參數(shù)個數(shù)的函數(shù)稱為可能:
var sum = function() {
var i, sum = 0
for (var i = 0; i < arguments.length; i++) {
sum += arguments[i]
}
return sum
}
document.writeln(sum(4, 8, 15, 16, 23, 42))
因?yàn)檎Z言的一個設(shè)計(jì)錯誤彻桃,arguments 并不是一個真正的數(shù)組坛善。它只是一個“類似數(shù)組(array-like)”的對象。arguments 擁有一個 length 屬性邻眷,但它沒有任何數(shù)組的方法眠屎。
返回
一個函數(shù)總會返回一個值。如果沒有指定返回值肆饶,則返回 undefined改衩。
如果函數(shù)調(diào)用時在前面加上了 new 前綴,且返回值不是一個對象驯镊,則返回 this (該新對象)葫督。
異常
throw 語句中斷函數(shù)的執(zhí)行。它應(yīng)該跑出一個 exception 對象板惑,該對象包含一個用來識別異常類型的 name 屬性和一個描述性的 message 屬性橄镜。你也可以添加其他的屬性。
var add = function(a, b) {
if (typeof a !== 'number' || typeof b !== 'number') {
throw {
name: 'TypeError',
message: 'add needs numbers',
}
}
return a + b
}
add(3, 6)
var try_it = function() {
try {
add('seven')
} catch(e) {
console.log(e.name + ': ' + e.message)
}
}
try_it()
如果在 try 代碼塊內(nèi)拋出了一個異常洒放,控制權(quán)就會跳轉(zhuǎn)到它的 catch 從句蛉鹿。
擴(kuò)充類型的功能
JavaScript 允許給語言的基本類型擴(kuò)充功能。通過給 Object.prototype
添加方法往湿,可以讓該方法對所有對象都可用妖异。這樣的方式對函數(shù)、數(shù)組领追、字符串他膳、數(shù)字、正則表達(dá)式和布爾值同樣適用绒窑。
Function.prototype.method = function(name, func) {
this.prototype[name] = func
return this
}
Number.method('integer', function(){
return Math[this < 0 ? 'ceil' : 'floor'](this)
})
document.writeln((-10 / 3).integer())
作用域
JavaScript作用域?yàn)楹瘮?shù)作用域棕孙,定義在函數(shù)中的參數(shù)和變量在函數(shù)外部是不可見的,而在一個函數(shù)內(nèi)部任何位置定義的變量些膨,在該函數(shù)內(nèi)部任何地方都可見蟀俊。
var foo = function() {
var a = 3
var b = 5
var bar = function() {
var b = 7
var c = 11
// 此時,a 為 3订雾,b 為 7肢预, c 為 11
a += b + c
// 此時,a 為 3洼哎,b 為 7烫映, c 為 11
}
// 此時沼本,a 為 3,b 為 5锭沟, c 沒有定義
bar()
// 此時抽兆,a 為 21,b 為 5
}
閉包
下看兩個閉包的例子:
var uniqueInteger = (function() { // 返回高級函數(shù)局部變量的例子
var counter = 0
return function() {
return counter++
}
}())
//返回一個包含兩個方法的對象族淮,這些方法繼續(xù)享有訪問 value 變量的特權(quán)
var myObject = (function(){
var value = 0
return {
increment: function(inc) {
value += typeof inc === 'number' ? inc : 1
},
getValue: function(){
return value
}
}
})
JS_權(quán)指: 函數(shù)對象可以通過作用域相互關(guān)聯(lián)起來辫红,函數(shù)體內(nèi)的變量都可以保存在函數(shù)作用域內(nèi),這種特性在計(jì)算機(jī)科學(xué)文獻(xiàn)中稱為“閉包”瞧筛。
JS: Good Parts-函數(shù)可以訪問被它創(chuàng)建時所處的上下文環(huán)境厉熟,這被稱為閉包导盅。
在同一個作用域鏈中定義兩個閉包较幌,這兩個閉包共享同樣的私有變量或變量。
function constfunc(v) {
return function() {
return v
}
}
var funcs = []
for (var i = 0; i < 10; i++) {
funcs[i] = constfunc(i)
}
funcs[5]() // => 5
下面的例子中白翻,創(chuàng)建了10個閉包乍炉,并將它們存儲到一個數(shù)組中。這些閉包都是在同一個函數(shù)調(diào)用中定義的滤馍,因此它們可以共享變量i岛琼。當(dāng) constfunc() 返回時,變量 i 的值是10巢株,所有的閉包都共享這一個值槐瑞,因此,數(shù)組中的函數(shù)的返回值都是同一個值阁苞。嵌套的函數(shù)不會將作用域內(nèi)的私有成員復(fù)制一份困檩,也不會對所綁定的變量生成靜態(tài)快照(static snapshot)
所以,避免在循環(huán)中創(chuàng)建函數(shù)那槽,它可能只會帶來無謂的計(jì)算悼沿,還會引起混淆,正如下面這個糟糕的例子骚灸。
function constfuncs() {
var funcs = []
for (var i = 0; i < 10; i++) {
funcs[i] = function() {
return i
}
}
return funcs
}
funcs[5]() // => 10
回調(diào)
函數(shù)是的對不連續(xù)實(shí)踐的處理變得更容易糟趾。例如,堅(jiān)定有這么一個序列甚牲,由用戶交互行為處罰义郑,向服務(wù)器發(fā)送請求,最終顯示服務(wù)器的響應(yīng)丈钙。最自然的寫法可能會是這樣的:
request = prepare_the_request()
response = send_request_synchronously(request)
display(response)
這種方式的問題在于非驮,網(wǎng)絡(luò)上的同步請求會導(dǎo)致客戶端進(jìn)入假死狀態(tài)。如果網(wǎng)絡(luò)傳輸或服務(wù)器很慢,響應(yīng)會慢到讓人不可接受。
更好的方式是發(fā)起異步請求呵哨,提供一個當(dāng)服務(wù)器的響應(yīng)到達(dá)時隨即觸發(fā)的回調(diào)函數(shù)俯萌。異步函數(shù)立即返回侧啼,這樣客戶端就不會被阻塞掘剪。
request = prepare_the_request()
send_request_asynchronously(request, function(response) {
display(response)
})
模塊
模塊模式的一般形式是:一個定義了私有變量和函數(shù)的函數(shù)敬鬓,利用閉包創(chuàng)建可以訪問私有變量和函數(shù)的特權(quán)函數(shù)间唉,最后返回這個特權(quán)函數(shù)栋盹,或者把它們保存到一個可訪問到的地方施逾。
使用模塊模式就可以摒棄全局變量的使用,它促進(jìn)了信息隱藏和其他優(yōu)秀的設(shè)計(jì)實(shí)踐例获。
級聯(lián)
如果我們讓方法返回 this 而不是 undefined 汉额,就可以啟用級聯(lián)。
柯里化
var add = function(a, b) {
return a + b
}
var add1 = add.curry(1)
document.writeln(add1(6)) // 7
add1 是把 1 傳遞給 add 函數(shù)的 curry 方法后創(chuàng)建的一個函數(shù)榨汤。add1 函數(shù)把傳遞給它的參數(shù)的值加 1蠕搜。
簡單說,函數(shù)柯里化就是對高階函數(shù)的降階處理收壕。
舉個例子妓灌,就是把原本:
function(arg1,arg2)變成function(arg1)(arg2)
function(arg1,arg2,arg3)變成function(arg1)(arg2(arg3)
function(arg1,arg2,arg3,arg4)變成function(arg1)(arg2)(arg3)(arg4)……
記憶
var fibonacci = function() {
var memo = [0, 1]
var fib = function(n) {
var result = memo[n]
if (typeof result !== 'number') {
result = fib(n - 1) + fib(n - 2)
memo[n] = result
}
return result
}
return fib
}()
document.writeln(fibonacci(9))
第五章 繼承
在基于類的語言中,對象是類的實(shí)例蜜宪,并且類可以從另一個類繼承虫埂。JavaScript 是一門基于原型的語言,這意味著對象直接從其他對象繼承圃验。
偽類
JavaScript 的原型存在諸多矛盾掉伏,它不直接讓對象從其他對象繼承,反而插入了一個多余的間接層:通過構(gòu)造器函數(shù)產(chǎn)生對象澳窑。
當(dāng)一個函數(shù)對象被創(chuàng)建時斧散,F(xiàn)ucntion 構(gòu)造器產(chǎn)生的函數(shù)對象會運(yùn)行類似這樣的一些代碼:
this.prototype = {constructor: this}
新函數(shù)對象被賦予一個prototype屬性,它的值時一個包含 constructor屬性且屬性值為該新函數(shù)的對象照捡。這個 prototype 對象時存放繼承特性的地方颅湘。因?yàn)?JavaScript 語言沒有提供一種方法去確定哪個函數(shù)是大蒜用來做構(gòu)造器的,所以每個函數(shù)都會得到一個 prototype 對象栗精。constructor 屬性沒什么用闯参,重要的是 prototype 對象。
var Mammal = function(name) {
this.name = name
}
Mammal.prototype.get_name = function() {
return this.name
}
Mammal.prototype.says = function() {
return this.saying || ''
}
var myMallal = new Mammal('Herb the Mammal')
var name = myMallal.get_name() // 'Herb the Mammal'
// 我們可以構(gòu)造另一個偽類來繼承 Mammal悲立,
// 這是通過定義它的 constructor 函數(shù)并替換它的 prototype
// 為一個 Mammal 的實(shí)例來實(shí)現(xiàn)的鹿寨。
var Cat = function(name) {
this.name = name
this.saying = 'meow'
}
// 替換 Cat.prototype 為一個新的 Mammal 實(shí)例
Cat.prototype = new Mammal()
// 擴(kuò)充新原型對象,增加 purr 和 get_name 方法
Cat.prototype.purr = function(n) {
var i, s = ''
for (var i = 0; i < n; i++) {
if (s) {
s += '-'
}
s += 'r'
}
return s
}
Cat.prototype.get_name = function() {
return this.says() + ' ' + this.name + ' ' + this.says()
}
var myCat = new Cat('Henrietta')
var says = myCat.says() // 'meow'
var purr = myCat.purr(5) // 'r-r-r-r-r'
var name = myCat.get_name() // 'meow Henrietta meow'
上述做法的缺陷:
- 沒有私有環(huán)境薪夕,所有的屬性都是公開的
- 無法訪問 super (父類)的方法
使用構(gòu)造器函數(shù)存在一個嚴(yán)重的危害脚草,如果你在調(diào)用構(gòu)造器函數(shù)時忘記了在前面加上 new 前綴,那么 this 將不會被綁定到一個新對象上原献。悲劇的是馏慨, this 將被綁定到全局對象上埂淮,所以你不但沒有擴(kuò)充新對象,反而破壞了全局變量環(huán)境写隶。
為了降低這個問題帶來的風(fēng)險(xiǎn)倔撞,所有的構(gòu)造器函數(shù)都約定命名稱首字母大寫的形式,并且不以首字母大寫的形式拼寫任何其他的東西慕趴。這樣我們至少可以通過目視檢查去發(fā)現(xiàn)是否缺少了 new 前綴痪蝇。一個更好的備選方案就是根本不使用 new。
“偽類” 形式可以給不熟悉 JavaScript 的程序員提供便利冕房,但它也隱藏了該語言的真實(shí)的本質(zhì)躏啰。借鑒類的表示法可能誤導(dǎo)程序員去編寫過于深入與復(fù)雜的層次結(jié)構(gòu)。許多復(fù)雜的類層次結(jié)構(gòu)產(chǎn)生的原因就是靜態(tài)類型檢查的約束耙册。JavaScript 完全擺脫了那些約束给僵。在基于類的語言中,類繼承是代碼重用的唯一方式觅玻。而 JavaScript 有著更多且更好的選擇想际。
對象說明符
構(gòu)造器要接受一大串參數(shù)培漏。這可能令人煩惱溪厘,因?yàn)橐涀?shù)的順序非常困難,所以在編寫構(gòu)造器時讓它接受一個簡單的對象說明符牌柄,就會更加友好:
var myObject = maker({
first: f,
middle: m,
last: l,
state: s,
city: c,
})
原型
在一個純粹的原型類型中畸悬,我們會摒棄類,轉(zhuǎn)而專注于對象珊佣√;拢基于原型的繼承相比基于類的繼承在概念上更為簡單:一個新對象可以繼承一個舊對戲那個的屬性。
// 原型
var myMammal = {
name: 'Herb the Mammal',
get_name: function() {
return this.name
},
says: function() {
return this.saying || ''
}
}
var myCat = Object.create(myMammal)
myCat.name = 'Henrietta'
myCat.saying = 'meow'
myCat.purr = function(n) {
var i, s = ''
for (i = 0; i < n; i += 1) {
if (s) {
s += '-'
}
s += 'r'
}
return s
}
myCat.get_name = function() {
return this.says + ' ' + this.name + ' ' + this.says
}
這是一種“差異化繼承(differential inheritance)”咒锻,通過定制一個新的對象冷冗,我們指明它與所基于的基本對象的區(qū)別。
函數(shù)化
部件
第六章 數(shù)組
數(shù)組是一段線性分配的內(nèi)存惑艇,它通過整數(shù)計(jì)算偏移并訪問其中的元素蒿辙。數(shù)組是一種性能出色的數(shù)據(jù)結(jié)構(gòu)。不幸的是滨巴,JavaScript 沒有像此類數(shù)組一樣的數(shù)據(jù)結(jié)構(gòu)思灌。
作為替代,JavaScript 提供了一種擁有一些類數(shù)組(array-like)特性的對象恭取。它把數(shù)組的下標(biāo)轉(zhuǎn)變稱字符串泰偿,用其作為屬性。它明顯地比一個真正的數(shù)組慢蜈垮,但它是用起來更加方便耗跛。它的屬性的檢索和更新的方式與對象一摸一樣裕照,只不過多一個可以用整數(shù)作為屬性名的特性。
數(shù)組字面量
一個數(shù)組字面量是在一對方括號中包圍零個或多個用逗號分隔的值的表達(dá)式调塌。數(shù)組的第一個值將獲得屬性‘0’牍氛, 第二個值將獲得屬性名‘1’,依次類推:
// 數(shù)組
var empty = []
var numbers = [
'zero', 'one', 'two', 'three', 'four',
'five', 'six', 'seven', 'eight', 'nine'
]
empty[1] // undefined
numbers[1] // one
empty.length // 0
numbers.length // 10
var numbers_object = {
'0': 'zero',
'1': 'one',
'2': 'two',
'3': 'three',
'4': 'four',
'5': 'five',
'6': 'six',
'7': 'seven',
'8': 'eight',
'9': 'nine',
}
numbers 和 numbers_object 產(chǎn)生的結(jié)果相似烟阐。但是它們也有一些顯著的不同搬俊。numbers 繼承自 Array.prototype, 而 numbers_object 繼承自 Object.prototype, 所以 numbers 繼承了 大量有用的方法蜒茄。同時唉擂,numbers 也有一個詭異的 length 屬性,而 numbers_object 則沒有檀葛。
在大多數(shù)語言中玩祟,一個數(shù)組的所有元素都要求是相同的類型。 JavaScript 允許數(shù)組包含人意混合類習(xí)慣的值:
var misc = [
'string', 98.6, true, false, null, undefined, ['nested', 'array'], {object: true}, NaN,
Infinity
]
misc.lenght // 10
長度
每個數(shù)組都有一個 length 屬性屿聋,和大多數(shù)其他語言不同空扎,JavaScript 數(shù)組的 length 是沒有上界的。如果你用大于或等于當(dāng)前 length 的數(shù)字作為下標(biāo)來存儲一個元素润讥,那么 length 值會被增大以容納新元素转锈,不會發(fā)生數(shù)組越界錯誤。
length 屬性的值是這個數(shù)組的最大整數(shù)屬性名加上1楚殿。它不一定等于數(shù)組里的屬性的個數(shù):
var myArray = []
myArray.length // 0
myArray[1000000] = true
myArray.lenght // 1000001
// myArray 只包含一個屬性
[] 后置下表運(yùn)算符把它所含的表達(dá)式轉(zhuǎn)換成一個字符串撮慨,如果該表達(dá)式有 toString 方法,就使用該方法的值脆粥。這個字符串將被用作屬性名砌溺。
你可以直接設(shè)置 length 的值。設(shè)置更大的 length 不會給數(shù)組分配更多的空間变隔。而把 length 設(shè)小將導(dǎo)致所有下標(biāo)大于等于新 length 的屬性被刪除:
numbers.length = 3 // numbers 是 ['zero', 'one', 'two']
// 通過把下標(biāo)指定為一個數(shù)組的當(dāng)前 length规伐,可以附加一個新元素到該數(shù)組的尾部
numbers[numbers.length] = 'shi' // numbers 是 ['zero', 'one', 'two', 'shi', 'go']
// 有時用 push 方法可以更方便地完成同樣的事情:
numbers.push('go') // numbers 是 ['zero', 'one', 'two', 'shi', 'go']
刪除
由于JavaScript 的數(shù)組其實(shí)就是對象,所以 delete 運(yùn)算符可以用來從數(shù)組中移除元素:
delete numbers[2]
// numbers 是 ['zero', 'one', undefined, 'shi', 'go']
不幸的是匣缘,那樣會在數(shù)組中留下一個空洞猖闪。這是因?yàn)榕旁诒粍h除之后的元素保留著他們最初的屬性。
可以用 splice 方法
numbers.splice(2, 1)
// numbers 是 ['zero', 'one', 'shi', 'go']
值為 'shi' 的屬性的鍵值從 '3' 變到 '2' 孵户。因?yàn)楸粍h除屬性后面的每個屬性必須被移除萧朝,并且以一個新的鍵值重新插入,這對于大型數(shù)組來說可能會效率不高夏哭。
枚舉
因?yàn)?JavaScript 的數(shù)組其實(shí)就是對象检柬,所以 for in 語句可以用來遍歷一個數(shù)組的所有屬性。遺憾的是,for in 無法保證屬性的順序何址。采用常規(guī)的 for 語句可以避免這些問題:
var i
for (i = 0; i < myArray.length; i += 1) {
document.writeln(myArray[i])
}
容易混淆的發(fā)放
JavaScript 本身對于數(shù)組和對象的區(qū)別是混亂的里逆。typeof 運(yùn)算符報(bào)告數(shù)組的類型是 'object',這是沒有意義的用爪。
對他們的使用規(guī)則很簡單: 當(dāng)屬性名是小而連續(xù)的整數(shù)時原押,你應(yīng)該使用數(shù)組。否則偎血,使用對象诸衔。
指定初始值
JavaScript 的數(shù)組通常不會預(yù)置值。如果你用 [] 得到一個新數(shù)組颇玷,它將是空的笨农。如果你訪問一個不存在的元素,得到的值則是 undefined帖渠。
第七章 正則表達(dá)式
JavaScript 的許多特性都借鑒自其他語言谒亦。語法借鑒自 Java,函數(shù)借鑒自 Scheme空郊,原型繼承借鑒自 Self份招。而 JavaScript 的正則表達(dá)式特性則借鑒自 Perl。