JavaScript語言精粹

第二章 語法

數(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。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末狞甚,一起剝皮案震驚了整個濱河市锁摔,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌入愧,老刑警劉巖鄙漏,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異棺蛛,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)巩步,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門旁赊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人椅野,你說我怎么就攤上這事终畅。” “怎么了竟闪?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵离福,是天一觀的道長。 經(jīng)常有香客問我炼蛤,道長妖爷,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任理朋,我火速辦了婚禮絮识,結(jié)果婚禮上绿聘,老公的妹妹穿的比我還像新娘。我一直安慰自己次舌,他們只是感情好熄攘,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著彼念,像睡著了一般挪圾。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上逐沙,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天洛史,我揣著相機(jī)與錄音,去河邊找鬼酱吝。 笑死也殖,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的务热。 我是一名探鬼主播忆嗜,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼崎岂!你這毒婦竟也來了捆毫?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤冲甘,失蹤者是張志新(化名)和其女友劉穎绩卤,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體江醇,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡濒憋,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了陶夜。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片凛驮。...
    茶點(diǎn)故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖条辟,靈堂內(nèi)的尸體忽然破棺而出黔夭,到底是詐尸還是另有隱情,我是刑警寧澤羽嫡,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布本姥,位于F島的核電站,受9級特大地震影響杭棵,放射性物質(zhì)發(fā)生泄漏婚惫。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望辰妙。 院中可真熱鬧鹰祸,春花似錦、人聲如沸密浑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽尔破。三九已至街图,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間懒构,已是汗流浹背餐济。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留胆剧,地道東北人絮姆。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像秩霍,于是被迫代替她去往敵國和親篙悯。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 首發(fā)于:segmentfault《JavaScript語言精粹 修訂版》 讀書筆記 之前看到這篇文章铃绒,前端網(wǎng)老姚淺...
    若川i閱讀 870評論 0 3
  • 編程 所謂編程鸽照,就是將一組需求分解成一組函數(shù)與數(shù)據(jù)結(jié)構(gòu)的技能。 假與真 下面列出的值被當(dāng)作假(false) fal...
    linice閱讀 254評論 0 4
  • 第1章 精華 JavaScript建立在一些非常優(yōu)秀的想法和少數(shù)非常糟糕的想法之上優(yōu)秀的想法包括:函數(shù)颠悬、弱類型矮燎、...
    _安哥拉閱讀 395評論 0 0
  • 打開了堯家微信群,看到了大家都在聊天赔癌,還發(fā)現(xiàn)有幾個紅包诞外,點(diǎn)開一看,里面居然有你在搶紅包~届榄。 從九點(diǎn)開始浅乔,你陸續(xù)都有...
    坦蕩兮兮閱讀 162評論 0 0
  • 世界可能有時會很順眼,但生活一定充滿冒險(xiǎn)铝条,對此,我深信不疑席噩。 接著上次去成都的事兒說(新朋友需要接著第一篇…)班缰,由...
    寫作小白鼠閱讀 516評論 2 2