這是我所知道最完整最簡(jiǎn)潔的JavaScript基礎(chǔ)教程。 本文將帶你盡快走進(jìn)JavaScript的世界——前提是你有一些編程經(jīng)驗(yàn)的話。本文試圖描述這門語(yǔ)言的最小子集。我給這個(gè)子集起名叫做“JavaScript簡(jiǎn)易教程”,并推薦那些準(zhǔn)備深入閱讀細(xì)節(jié)和高級(jí)技巧之前的新手閱讀。合抱之木生于毫末,九層之臺(tái)起于壘土,欲速則不達(dá)。本文的最后會(huì)提出如何進(jìn)一步學(xué)習(xí)。 警告:下面是我所描述的規(guī)則集和最佳實(shí)踐。我喜歡整潔清晰(例如,你可以隨時(shí)通過(guò)下面的目錄快速導(dǎo)航)。橫看成嶺側(cè)成峰,遠(yuǎn)近高低各不同,雖然規(guī)則是無(wú)懈可擊的,但不可避免——每個(gè)人的理解會(huì)各不相同。 目錄
本文約定(Conventions used in this blog post)命令行交互(Command line interaction)每當(dāng)我介紹一個(gè)新概念,我都會(huì)嘗試通過(guò)JavaScript命令行進(jìn)行演示。像下面這樣:
大于號(hào)后面的文本是用戶輸入內(nèi)容。其他的都是JavaScript引擎的輸出內(nèi)容。此外,也可以使用console.log()來(lái)向控制臺(tái)打印數(shù)據(jù)(這種方法可以在大部分JavaScript引擎中工作,包括Node.js). 查找文檔(Finding documentation)有時(shí)你會(huì)看到一些函數(shù)或方法有超鏈接,你應(yīng)該清楚他們的工作原理。如果沒(méi)有,可以在Mozilla Developer Network (MDN)上查看細(xì)節(jié),你也可以使用Google在MDN上查找文檔。例如,下面是通過(guò)Google搜索數(shù)組的push()方法的例子: 語(yǔ)言的性質(zhì)(The nature of the language)本節(jié)對(duì)JavaScript的性質(zhì)做簡(jiǎn)要介紹,以幫你理解一些疑問(wèn)。 JavaScript 和 ECMAScript(JavaScript versus ECMAScript)編程語(yǔ)言稱為JavaScript,語(yǔ)言標(biāo)準(zhǔn)被稱為ECMAScript。他們有不同名字的原因是因?yàn)椤癑ava”已經(jīng)被注冊(cè)為商標(biāo)(屬于Oracle)。目前,只有Mozilla被正式允許使用“JavaScript”名稱,因?yàn)楹芫靡郧八麄兊玫揭环菰S可。因此,開(kāi)放的語(yǔ)言標(biāo)準(zhǔn)擁有不同的名字。當(dāng)前的JavaScript版本是ECMAScript 5,ECMAScript 6當(dāng)前是開(kāi)發(fā)版。 影響(Influences)JavaScript之父,Brendan Eich 別無(wú)選擇必須迅速創(chuàng)建一門語(yǔ)言。(否則,會(huì)更糟糕,Netscape將使用其他技術(shù))。他借鑒了幾門其他語(yǔ)言:
JavaScript直到ECMAScript 3才加入異常處理,這解釋了為什么這門語(yǔ)言經(jīng)常自動(dòng)轉(zhuǎn)換類型和經(jīng)常靜默失?。鹤畛鯖](méi)有拋出異常的功能。 一方面,JavaScript有很多怪癖,并且缺失很多功能(塊級(jí)變量作用域(block-sciped variables),模塊(modules)支持子類型(subtyping)等)。另一方面,它有幾個(gè)非常強(qiáng)大的特性,允許你彌補(bǔ)上面的問(wèn)題。在其他語(yǔ)言中,你要學(xué)習(xí)語(yǔ)言特性。在JavaScript中,你需要經(jīng)常學(xué)習(xí)模式代替。 深入閱讀(Further reading)
語(yǔ)法(Syntax)這節(jié)介紹一些JavaScript的基本語(yǔ)法規(guī)則。 語(yǔ)句和表達(dá)式(Statements versus expressions)了解JavaScript的語(yǔ)法,先來(lái)了解兩個(gè)主要的語(yǔ)法類型:語(yǔ)句和表達(dá)式。
語(yǔ)句和表達(dá)式之間的區(qū)別最好通過(guò)實(shí)例說(shuō)明,JavaScript(像Java)有兩種不同的方式實(shí)現(xiàn)if-then-else。一種是用語(yǔ)句:
另一種是表達(dá)式:
你可以將后者作為函數(shù)參數(shù)(但前者不行):
最后,每當(dāng)JavaScript期待一個(gè)語(yǔ)句,你也可以用一個(gè)表達(dá)式代替。例如:
流程控制語(yǔ)句和語(yǔ)句塊(Control flow statements and blocks)流程控制語(yǔ)句,其語(yǔ)句體可以是單條語(yǔ)句。舉兩個(gè)例子:
然而,任何語(yǔ)句總能被語(yǔ)句塊代替,花括號(hào)包含零或多條語(yǔ)句。因此,你也可以這樣寫:
在本文中,我們只使用后一種方式。 分號(hào)(Semicolons)JavaScript中的分號(hào)是可選的。但省略(分號(hào))可能會(huì)帶來(lái)意想不到的結(jié)果,所以我建議你不要那樣做。 正如上面所看到的,分號(hào)作為語(yǔ)句的結(jié)尾,但語(yǔ)句塊不需要。僅有一種情況下你能看到語(yǔ)句塊后面有分號(hào)——函數(shù)表達(dá)式后面的函數(shù)體塊。表達(dá)式作為語(yǔ)句的結(jié)尾,后面是分號(hào):
注釋(Comments)JavaScript的注釋有兩種形式:?jiǎn)涡凶⑨尯投嘈凶⑨?。單行注釋?/開(kāi)頭,以換行符結(jié)尾:
多行注釋用/**/包裹
深入閱讀變量和賦值(Variables and assignment)JavaScript中的變量在使用前必須先聲明,否則會(huì)報(bào)錯(cuò)引用錯(cuò)誤(Reference Error):
賦值(Assignment)你可以在聲明變量的同時(shí)為其賦值:
你也可以給已經(jīng)存在的變量重新賦值:
復(fù)合賦值操作符(Compount assignment operators)有很多復(fù)合賦值操作符,例如 =。下面的兩個(gè)賦值操作等價(jià):
標(biāo)識(shí)符和變量名(Identifiers and variable names)標(biāo)識(shí)符就是事物的名字,在JavaScript中他們扮演不同的語(yǔ)法角色。例如,變量的名稱是一個(gè)標(biāo)識(shí)符。 大體上,標(biāo)識(shí)符的第一個(gè)字符可以是任何Unicode字符、美元標(biāo)志符($)或下劃線(_)。后面可以是任意字符和數(shù)字。因此,下面全是合法的標(biāo)識(shí)符:
注意:首字符不能是數(shù)字,如果是數(shù)字的話,該如何區(qū)分是數(shù)字還是變量呢? 一些標(biāo)識(shí)符是“保留關(guān)鍵字”——他們是語(yǔ)法的一部分,不能用作變量名:
從技術(shù)上講,下面三個(gè)標(biāo)識(shí)符不是保留字,但也不應(yīng)該作為變量名:
深入閱讀
值(Values)JavaScript有所有我們期待的編程語(yǔ)言值類型:布爾,數(shù)字,字符串,數(shù)組等。JavaScript中的所有值都有屬性。每個(gè)屬性有一個(gè)鍵(或名字)和一個(gè)值。參考記錄的域(fields of record)。你可以使用點(diǎn)(.)操作符讀取屬性:
舉個(gè)例子:字符串“abc”有屬性lenght(長(zhǎng)度)。
上面的代碼也可以寫成下面這樣:
點(diǎn)操作符也可以用來(lái)給屬性賦值:
你也可以通過(guò)它(.)調(diào)用方法:
上面,我們?cè)谥怠癶ello”上面調(diào)用方法 toUpperCase()。 原始類型值和對(duì)象(Primitive values versus objects)JavaScript定義了不同值之間的區(qū)別:
兩者之間的主要區(qū)別在于他們是如何被比較的:每一個(gè)對(duì)象有一個(gè)獨(dú)一無(wú)二的標(biāo)志,并且僅和自己相等:
相反,所有原始值只要編碼值相同就被認(rèn)為是相同的:
接下來(lái)的兩節(jié)會(huì)介紹原始值和對(duì)象的更多細(xì)節(jié)。 原始類型值(Primitive values)下面全是原始類型值(簡(jiǎn)稱:原始值):
原始值的特征:
對(duì)象(Objects)所有非原始值(non-primitive)的值都是對(duì)象。最常見(jiàn)的幾種對(duì)象類型是:
對(duì)象的特征:
-** 用戶可擴(kuò)展(user-extensible):**你可以通過(guò)構(gòu)造函數(shù)定義新的對(duì)象類型。 所有的數(shù)據(jù)結(jié)構(gòu)(如數(shù)組)都是對(duì)象,但并不是所有的對(duì)象都是數(shù)據(jù)結(jié)構(gòu)。例如:正則表達(dá)式是對(duì)象,但不是數(shù)據(jù)結(jié)構(gòu)。 undefined 和 null(undefined and null)多少有些不必要,JavaScript有兩個(gè)“無(wú)值(non-values)”:undefined 和 null。
通常情況下你應(yīng)該把undefined和null看成是等價(jià)的,如果他們代表相同意義的無(wú)值的話。檢查他們的一種方式是通過(guò)嚴(yán)格比較:
另一種在實(shí)際中使用的方法是認(rèn)為undefined 和 null 都是false:
警告:false,0,NaN 和 “” 都被當(dāng)作false。 包裝類型(Wrapper types)對(duì)象類型的實(shí)例Foo(包括內(nèi)建類型,例如Array和其他自定義類型)從對(duì)象Foo.prototype上獲取方法。你可以通過(guò)讀取這個(gè)方法的方式(不是調(diào)用)驗(yàn)證這點(diǎn):
相反,原始類型是沒(méi)有類型的,所以每個(gè)原始類型有一個(gè)關(guān)聯(lián)類型,稱之為包裝類型:
包裝類型也有實(shí)例(他們的實(shí)例是對(duì)象),但不常用。相反,包裝類型有其他用處:如果你將他們作為函數(shù)調(diào)用,他們可以將值轉(zhuǎn)換為原始類型。
通過(guò)typeof 和 instanceof 將值分類(Categorizing values via typeof and instanceof)有兩個(gè)操作符可以用來(lái)將值分類:typeof 主要用于原始值,instanceof 主要用于對(duì)象。 typeof 使用方法如下:
下面列出了typeof操作的所有結(jié)果:
有兩個(gè)結(jié)果和我們上面說(shuō)的的原始值與對(duì)象是矛盾的:
instanceof使用方法如下:
如果 value 是一個(gè)對(duì)象,并且value 是由構(gòu)造函數(shù)Constr創(chuàng)建的(參考:類)。例如:
深入閱讀
布爾(Booleans)布爾類型原始值包括true和false。下面的操作符產(chǎn)生布爾值:
真值和假值(Truthy and falsy)每當(dāng)JavaScript希望一個(gè)布爾值時(shí)(例如:if語(yǔ)句的條件),可以使用任何值。它將被理解(轉(zhuǎn)換)為true或false。下面的值被理解為false:
所有其他值被認(rèn)為true。被理解為false的值稱為假值(falsy),被理解為true的值稱為真值(truthy)??梢允褂肂oolean作為函數(shù),測(cè)試值被理解為什么。
二元邏輯運(yùn)算符(Binary logical operators)JavaScript中的二元邏輯運(yùn)算符是短路運(yùn)算——如果第一個(gè)操作數(shù)可以確定結(jié)果,第二個(gè)操作數(shù)將不被驗(yàn)證(運(yùn)算)。例如,在下面的代碼中,函數(shù)foo()永遠(yuǎn)不會(huì)被調(diào)用。
此外,二元邏輯運(yùn)算符會(huì)返回操作數(shù)中的一個(gè)——可能是一個(gè)布爾值,也可能不是。一張真值表用來(lái)決定返回哪個(gè)值:
等值運(yùn)算符(Equality operators)在JavaScript中檢測(cè)相等,你可以使用嚴(yán)格相等(===)和嚴(yán)格不等(!==)?;蛘吣阋部梢允褂梅菄?yán)格相等(==)和非嚴(yán)格不等(!=)。經(jīng)驗(yàn)規(guī)則:總是用嚴(yán)格運(yùn)算符,假裝非嚴(yán)格運(yùn)算符不存在。嚴(yán)格相等更安全。 深入閱讀數(shù)字(Numbers)JavaScript中的所有數(shù)字都是浮點(diǎn)型(雖然大部分的JavaScript引擎內(nèi)部也使用整數(shù))。至于為什么這樣設(shè)計(jì),查看這里(每一個(gè)JavaScript開(kāi)發(fā)者應(yīng)該了解的浮點(diǎn)知識(shí))。
特殊數(shù)字:
運(yùn)算符(Operators)JavaScript中有下列算數(shù)運(yùn)算符:
全局對(duì)象Math通過(guò)函數(shù)提供更多算數(shù)運(yùn)算操作。 JavaScript中也有位運(yùn)算符(例如:位與 &)。 深入閱讀在2ality有一系列博文介紹這些內(nèi)容,例如:
字符串(Strings)字符串可以直接通過(guò)字符串字面量創(chuàng)建。這些字面量被單引號(hào)或雙引號(hào)包裹。反斜線(\)轉(zhuǎn)義字符并且產(chǎn)生一些控制字符。例如:
可以通過(guò)方括號(hào)訪問(wèn)單個(gè)字符:
length屬性是字符串的字符數(shù)量。
提醒:字符串是不可變的,如果你想改變現(xiàn)有字符串,你需要?jiǎng)?chuàng)建一個(gè)新的字符串。 字符串運(yùn)算符(String operators)字符串可以通過(guò)加號(hào)操作符( )拼接,如果其中一個(gè)操作數(shù)為字符串,會(huì)將另一個(gè)操作數(shù)也轉(zhuǎn)換為字符串。
連續(xù)執(zhí)行拼接操作可以使用 = 操作符:
字符串方法(String methods)字符串有許多有用的方法。例如:
深入閱讀語(yǔ)句(Statements)條件(Conditionals)if語(yǔ)句通過(guò)布爾條件決定執(zhí)行那個(gè)分支:
下面的switch語(yǔ)句,furit的值決定那個(gè)分支被執(zhí)行。
循環(huán)(Loops)for 循環(huán)的格式如下:
例子:
當(dāng)條件成立時(shí)while循環(huán)繼續(xù)循環(huán)它的循環(huán)體。
當(dāng)條件成立時(shí),do-while循環(huán)繼續(xù)循環(huán)。由于條件位于循環(huán)體之后,所以循環(huán)體總是被至少至少執(zhí)行一次。
在所有的循環(huán)中:
函數(shù)(Functions)定義函數(shù)的一種方法是通過(guò)函數(shù)聲明:
上面的代碼定義一個(gè)名稱叫做add的函數(shù),有兩個(gè)參數(shù)param1和param2,并且返回參數(shù)的和。下面是如何調(diào)用這個(gè)函數(shù):
另一種定義add()函數(shù)的方法是通過(guò)函數(shù)表達(dá)式:
函數(shù)表達(dá)式產(chǎn)生一個(gè)值,因此可以直接將函數(shù)作為參數(shù)傳遞給其他函數(shù):
函數(shù)聲明提升(Function declarations are hoisted)函數(shù)聲明會(huì)被提升,他們?nèi)灰苿?dòng)到當(dāng)前作用域開(kāi)始之處。這允許你在函數(shù)聲明之前調(diào)用它們:
注意:雖然變量聲明也會(huì)被提升,但賦值的過(guò)程不會(huì)被提升:
特殊變量arguments(The special variable arguments)在JavaScript中你可以調(diào)用任意函數(shù)并傳遞任意數(shù)量的參數(shù)——語(yǔ)言絕不會(huì)抱怨(參數(shù)檢測(cè))。都可以正常工作,然而,使所有參數(shù)可訪問(wèn)需要通過(guò)特殊變量 arguments。arguments 看起來(lái)像數(shù)組,但它沒(méi)有數(shù)組的方法(稱為類數(shù)組 array-like)。
太多或太少參數(shù)(Too many or too few arguments)讓我們通過(guò)下面的函數(shù)探索JavaScript中傳遞太多或太少參數(shù)時(shí)如何處理(函數(shù) toArray在后面提到)
多出的參數(shù)將被忽略(可以通過(guò)arguments訪問(wèn)):
缺少的參數(shù)將會(huì)是undefined:
可選參數(shù)(Optional parameters)下面是一個(gè)常見(jiàn)模式,給參數(shù)設(shè)置默認(rèn)值:
強(qiáng)制數(shù)量(Enforcing an arity)如果你想強(qiáng)制參數(shù)的數(shù)量,你可以檢測(cè)arguments.length:
將arguments 轉(zhuǎn)換為數(shù)組(Converting arguments to an array)arguments 不是一個(gè)數(shù)組,它僅僅是類數(shù)組(array-like):它有一個(gè)length屬性,并且你可以通過(guò)方括號(hào)索引方式訪問(wèn)它的元素。然而,你不能移除元素,或在它上面調(diào)用任何數(shù)組方法。因此,有時(shí)你需要將其轉(zhuǎn)換為數(shù)組。這就是下面函數(shù)的作用。
深入閱讀異常處理(Exception handling)異常處理最常見(jiàn)的方式像下面這樣:
try分支包裹易出錯(cuò)的代碼,如果try分支內(nèi)部拋出異常,catch分支將會(huì)執(zhí)行。 深入閱讀嚴(yán)格模式(Strict mode)嚴(yán)格模式開(kāi)啟檢測(cè)和一些其他措施,使JavaScript變成更整潔的語(yǔ)言。推薦使用嚴(yán)格模式。為了開(kāi)啟嚴(yán)格模式,只需在JavaScript文件或script標(biāo)簽第一行添加如下語(yǔ)句:
你也可以在每個(gè)函數(shù)上選擇性開(kāi)啟嚴(yán)格模式,只需將上面的代碼放在函數(shù)的開(kāi)頭:
下面的兩小節(jié)看下嚴(yán)格模式的三大好處。 明確錯(cuò)誤(Explicit errors)讓我們看一個(gè)例子,嚴(yán)格模式給我們明確的錯(cuò)誤,否則JavaScript總是靜默失?。合旅娴暮瘮?shù) f() 執(zhí)行一些非法操作,它試圖更改所有字符串都有的只讀屬性——length:
當(dāng)你調(diào)用上面的函數(shù),它靜默失敗,賦值操作被簡(jiǎn)單忽略。讓我們將 f() 在嚴(yán)格模式下運(yùn)行:
現(xiàn)在瀏覽器報(bào)給我們一些錯(cuò)誤:
不是方法的函數(shù)中的this(this in non-method functions)在嚴(yán)格模式下,不作為方法的函數(shù)中的this值是undefined:
在非嚴(yán)格模式下,this的值是被稱作全局對(duì)象(global object)(在瀏覽器里是window):
不再自動(dòng)創(chuàng)建全局變量(No auto-created global variables)在非嚴(yán)格模式下,如果你給不存在的變量賦值,JavaScript會(huì)自動(dòng)創(chuàng)建一個(gè)全局變量:
在嚴(yán)格模式下,這會(huì)產(chǎn)生一個(gè)錯(cuò)誤:
深入閱讀變量作用域和閉包(Variable scoping and closures)在JavaScript中,你必須使用變量之前,通過(guò)var聲明變量:
你可以用一條var語(yǔ)句聲明和初始化多個(gè)變量:
但我建議每個(gè)變量使用一條語(yǔ)句。因此,我將上面的語(yǔ)句重寫為:
由于提升(見(jiàn)下文),最好在函數(shù)頂部聲明變量。 變量和函數(shù)作用域(Variables are function-scoped)變量的作用域總是整個(gè)函數(shù)(沒(méi)有塊級(jí)作用域)。例如:
我們可以看到tmp變量不僅在(*)所在行的語(yǔ)句塊存在,它在整個(gè)函數(shù)內(nèi)都存在。 變量提升(Variables are hoisted)變量聲明會(huì)被提升:聲明會(huì)被移到函數(shù)的頂部,但賦值過(guò)程不會(huì)。舉個(gè)例子,在下面的函數(shù)中(*)行位置聲明了一個(gè)變量。
在內(nèi)部,上面的函數(shù)被執(zhí)行像下面這樣:
閉包(Closures)每個(gè)函數(shù)保持和函數(shù)體內(nèi)部變量的連接,甚至離開(kāi)創(chuàng)建它的作用域之后。例如:
在(*)行開(kāi)始的函數(shù)在它創(chuàng)建時(shí)保留上下文,并在內(nèi)部保存一個(gè)start活動(dòng)值:
閉包是一個(gè)函數(shù)加上和其作用域鏈的鏈接。因此,createIncrementor() 返回的是一個(gè)閉包。 IIFE:模擬塊級(jí)作用域(IIFE: Simulating block scoping)有時(shí)你想模擬一個(gè)塊,例如你想將變量從全局作用域隔離。完成這個(gè)工作的模式叫做 IIFE(立即執(zhí)行函數(shù)表達(dá)式(Immediately Invoked Function Expression)):
上面你會(huì)看到函數(shù)表達(dá)式被立即執(zhí)行。外面的括號(hào)用來(lái)阻止它被解析成函數(shù)聲明;只有函數(shù)表達(dá)式能被立即調(diào)用。函數(shù)體產(chǎn)生一個(gè)新的作用域并使 tmp 變?yōu)榫植孔兞俊?/p> 閉包實(shí)現(xiàn)變量共享(Inadvertent sharing via closures)下面是個(gè)經(jīng)典問(wèn)題,如果你不知道,會(huì)讓你費(fèi)盡思量。因此,先瀏覽下,對(duì)問(wèn)題有個(gè)大概的了解。 閉包保持和外部變量的連接,有時(shí)可能和你想像的行為不一致:
(*)行的返回值總是當(dāng)前的i值,而不是當(dāng)函數(shù)被創(chuàng)建時(shí)的i值。當(dāng)循環(huán)結(jié)束后,i的值是5,這是為什么數(shù)組中的所有函數(shù)的返回值總是一樣的。如果你想捕獲當(dāng)前變量的快照,你可以使用 IIFE:
深入閱讀
對(duì)象和繼承(Objects and inheritance)和所有的值類型一樣,對(duì)象有屬性。事實(shí)上,你可以將對(duì)象當(dāng)作一組屬性的集合,每個(gè)屬性都是一對(duì)(鍵和值)。鍵是字符串,值可以是任意JavaScript值。到目前為止,我們僅僅見(jiàn)過(guò)鍵是標(biāo)識(shí)符的屬性,因?yàn)辄c(diǎn)操作符處理的鍵必須為標(biāo)識(shí)符。在這節(jié),你講見(jiàn)到另一種訪問(wèn)屬性的方法,能將任意字符串作為鍵。 單個(gè)對(duì)象(Single objects)在JavaScript中,你可以直接創(chuàng)建對(duì)象,通過(guò)對(duì)象字面量:
上面的對(duì)象有兩個(gè)屬性:name 和 describe。你能讀(“get”)和 寫(“set”)屬性:
屬性是函數(shù)如 describe 可以被當(dāng)作方法調(diào)用。當(dāng)調(diào)用他們時(shí)可以在它們內(nèi)部通過(guò)this引用對(duì)象。
in 操作符用來(lái)檢測(cè)一個(gè)屬性是否存在:
若讀取一個(gè)不存在的屬性,將會(huì)得到undefined值。因此上面的兩個(gè)檢查也可以像下面這樣:
delete操作符用來(lái)刪除一個(gè)屬性:
任意鍵屬性(Arbitrary property keys)屬性的鍵可以是任意字符串。到目前為止,我們看到的對(duì)象字面量中的和點(diǎn)操作符后的屬性關(guān)鍵字。按這種方法你只能使用標(biāo)識(shí)符。如果你想用其他任意字符串作為鍵名,你必須在對(duì)象字面量里加上引號(hào),并使用方括號(hào)獲取和設(shè)置屬性。
方括號(hào)允許你動(dòng)態(tài)計(jì)算屬性關(guān)鍵字:
引用方法(Extracting methods)如果你引用一個(gè)方法,它將失去和對(duì)象的連接。就其本身而言,函數(shù)不是方法,其中的this值為undefined(嚴(yán)格模式下)。
解決辦法是使用函數(shù)內(nèi)置的bind()方法。它創(chuàng)建一個(gè)新函數(shù),其this值固定為給定的值。
方法內(nèi)部的函數(shù)(Functions inside a method)每個(gè)函數(shù)都有一個(gè)特殊變量this。如果你在方法內(nèi)部嵌入函數(shù)是很不方便的,因?yàn)槟悴荒軓暮瘮?shù)中訪問(wèn)方法的this。下面是一個(gè)例子,我們調(diào)用forEach循環(huán)一個(gè)數(shù)組:
調(diào)用 logHiToFriends 會(huì)產(chǎn)生錯(cuò)誤:
有兩種方法修復(fù)這問(wèn)題。 #1:將this存儲(chǔ)在不同的變量。
#2:forEach的第二個(gè)參數(shù)允許提供this值。
在JavaScript中函數(shù)表達(dá)式經(jīng)常被用作函數(shù)參數(shù)。時(shí)刻小心函數(shù)表達(dá)式中的this。 構(gòu)造函數(shù):對(duì)象工廠(Constructors: factories for objects)目前為止,你可能認(rèn)為JavaScript的對(duì)象僅是鍵值的映射,通過(guò)JavaScript對(duì)象字面量可以得出這個(gè)觀點(diǎn),看起來(lái)很像其他語(yǔ)言中的地圖/字典(map/dictionary)。然而,JavaScript對(duì)象也支持真正意義上的面向?qū)ο筇匦裕豪^承(inheritance)。本節(jié)不會(huì)完全講解JavaScript中繼承的工作原理,但會(huì)給你以此為開(kāi)始的簡(jiǎn)單模式。如果你想得到更多知識(shí),請(qǐng)查閱這篇文章“JavaScript inheritance by example”。 除了作為“真正”的函數(shù)和方法,函數(shù)還在JavaScript中扮演第三種角色:如果通過(guò)new操作符調(diào)用,他們會(huì)變?yōu)闃?gòu)造函數(shù),對(duì)象的工廠。構(gòu)造函數(shù)是對(duì)其他語(yǔ)言中的類的粗略模擬。約定俗成,構(gòu)造函數(shù)的第一個(gè)字母大寫。例如:
我們看到構(gòu)造函數(shù)分為兩部分:首先,Point函數(shù)設(shè)置實(shí)例數(shù)據(jù)。其次,Point.prototype屬性包含對(duì)象的方法。前者的數(shù)據(jù)是每個(gè)實(shí)例私有的,后面的數(shù)據(jù)是所有實(shí)例共享的。 我們通過(guò)new操作符調(diào)用Point:
p是Point的一個(gè)實(shí)例:
深入閱讀
數(shù)組(Arrays)數(shù)組是數(shù)組元素的序列,能通過(guò)整數(shù)索引方法數(shù)組元素,數(shù)組索引從0開(kāi)始。 數(shù)組字面量(Array literals)數(shù)組字面量創(chuàng)建數(shù)組很方便:
上面的數(shù)組有三個(gè)元素:分別是字符串“a”,“b”, “c”。你可以通過(guò)整數(shù)索引訪問(wèn)它們:
length屬性總表示一個(gè)數(shù)組有多少項(xiàng)元素。
除此之外它也可以用來(lái)從數(shù)組上移除尾部元素:
in操作符也可以在數(shù)組上工作。
值得注意的是數(shù)組是對(duì)象,因此可以有對(duì)象屬性:
數(shù)組方法(Array methods)數(shù)組有許多方法。舉些例子:
遍歷數(shù)組(Iterating over arrays)有幾種方法可以遍歷數(shù)組元素。其中兩個(gè)最重要的是 forEach 和 map。 forEach遍歷整個(gè)數(shù)組,并將當(dāng)前元素和它的索引傳遞給一個(gè)函數(shù):
上面代碼的輸出
注意(*)行的函數(shù)參數(shù)是可省略的。例如:它可以只有一個(gè)參數(shù) elem。 map創(chuàng)建一個(gè)新數(shù)組,通過(guò)給每個(gè)存在數(shù)組元素應(yīng)用一個(gè)函數(shù):
深入閱讀正則表達(dá)式(Regular expressions)JavaScript內(nèi)建支持正則表達(dá)式。他們被雙斜線分隔:
方法 test():測(cè)試是否匹配(Method test(): is there a match?)
方法 exec():匹配和捕獲組(Method exec(): match and capture groups)
返回的數(shù)組第一項(xiàng)(索引為0)是完整匹配,捕獲的第一個(gè)分組在第二項(xiàng)(索引為1),等。有一種方法可以反復(fù)調(diào)用獲取所有匹配。 方法 replace():搜索并替換(Method replace(): search and replace)
replace的第一個(gè)參數(shù)必須是正則表達(dá)式,并且開(kāi)啟全局搜索(/g 標(biāo)記),否則僅第一個(gè)匹配項(xiàng)會(huì)被替換。有一種方法使用一個(gè)函數(shù)來(lái)計(jì)算替換項(xiàng)。 深入閱讀
|
|