這是我所知道的最完整最簡潔的JavaScript基礎教程。javascript
這篇文章帶你儘快走進JavaScript的世界——前提是你有一些編程經驗的話。本文試圖描述這門語言的最小子集。我給這個子集起名叫作「JavaScript簡易教程」,並推薦準備深刻閱讀細節和高級技巧以前的新手閱讀。心急吃不了熱豆腐。文章的最後提出如何進一步學習。html
警告:下面是我所描述的規則集和最佳實踐。我喜歡整潔清晰(例如,你能夠隨時經過下面的目錄快速導航)。規則是無懈可擊的,但不可避免——每一個人的理解不一樣。java
1. | 本文約定 |
2. | 語言的性質 |
3. | 語法 |
4. | 變量和賦值 |
5. | 值 |
6. | 布爾 |
7. | 數字 |
8. | 字符串 |
9. | 語句 |
10. | 函數 |
11. | 異常處理 |
12. | 嚴格模式 |
13. | 變量做用域和閉包 |
14. | 對象和繼承 |
15. | 數組 |
16. | 正則表達式 |
17. | 數學 |
18. | 標準庫的其餘功能 |
19. | 下一步學什麼? |
每當我介紹一個新概念,我都會嘗試經過JavaScript命令行進行演示。像下面這樣:node
> 3 + 4 7
大於號後面的文本是用戶輸入內容。其餘的都是JavaScript引擎的輸出內容。此外,也可使用console.log()來向控制檯打印數據(這方法能夠在大部分JavaScript引擎中工做,包括Node.js).正則表達式
有時你看到一些函數或方法有超連接,你應該清楚他們的工做原理。若是沒有,能夠在Mozilla Developer Network (MDN)上查看細節,你也可使用Google在MDN上查找文檔。例如,下面是經過Google搜索數組的push()方法的例子:express
本節對JavaScript的性質簡要介紹,以幫助你理解一些疑問。json
編程語言稱爲JavaScript,語言標準稱爲ECMAScript。他們有不一樣名字的緣由是由於「Java」已經被註冊爲商標(屬於Oracle)。目前,只有Mozilla被正式容許使用「JavaScript」名稱,由於好久之前他們等到一份許可。所以,開放的語言標準擁有不一樣的名字。當前的JavaScript版本是ECMAScript 5,ECMAScript 6當前是開發版。後端
JavaScript之父,Brendan Eich 別無選擇必須迅速建立一門語言。(或者,更糟糕,Netscape將使用其餘技術)。他借鑑了幾門其餘語言:api
JavaScript直到ECMAScript 3才加入異常處理,這解釋了爲何這門語言常常自動轉換類型和常常靜默失敗:它最初不能拋出異常。
一方面,JavaScript有不少怪癖,而且確實不少功能(塊級變量做用域(block-sciped variables),模塊(modules)支持子類型(subtyping)等)。另外一方面,它有幾個很是強大的特性,容許你彌補上面的問題。在其餘語言中,你要學習語言特性。在JavaScript中,你須要常常學習模式代替。
這節介紹一些JavaScript的基本語法規則。
瞭解JavaScript的語法,這有助於瞭解(簡而言之),它有兩個主要的語法類型:語句和表達式。
var foo;
3 * 7
語句和表達式之間的區別最好經過實例說明,JavaScript(像Java)有兩種不一樣的方式實現if-then-else。一種是用語句:
var x; if (y >= 0) { x = y; } else { x = -y; }
另外一種是表達式:
var x = y >= 0 ? y : -y;
你能夠將後者做爲函數參數(但前者不行):
myFunction(y >= 0 ? y : -y)
最後,每當JavaScript期待一個語句,你也能夠用一個表達式代替。例如:
foo(bar(7, 1));
foo(...);是一個語句(也叫作表達式語句),bar(7, 1)是一個表達式。他們都實現函數調用。
流程控制語句,其語句體能夠是單條語句。舉兩個例子:
if (obj !== null) obj.foo(); while (x > 0) x--;
然而,任何語句總能被語句塊代替,花括號包含零或多條語句。所以,你也能夠這樣寫:
if (obj !== null) { obj.foo(); } while (x > 0) { x--; }
在本文中,咱們只是用後一種方式。
分號在JavaScript中是可選的。但省略他們可能帶來意想不到的結果,因此我建議你不要那樣作。
正如上面所看到的,分號做爲語句的結尾,但語句塊不須要。僅有一種狀況下你將見到塊後面有分號:函數表達式後面的函數體塊。表達式做爲語句的結尾,後面是分號:
var x = 3 * 7; var f = function () { };
JavaScript有兩種註釋方式:單行註釋和多行註釋。單行註釋以//開頭,以換行符結尾:
x++; // 單行(single-line)註釋
多行註釋用/**/包裹
/*
這是多行註釋
多行哦 */
JavaScript中的變量在使用以前必須先聲明:
var foo; // 聲明變量「foo」
你能夠在生命變量的同時給它賦值:
var foo = 6;
你也能夠給已經存在的變量從新賦值:
foo = 4; // 更改變量的值
有不少符合賦值操做符,例如+=。下面的兩個賦值操做等價:
x += 1;
x = x + 1;
標識符就是事物的名字,在JavaScript中他們扮演不一樣的語法角色。例如,變量的名稱是一個標識符。
大致上,標識符的第一個字符能夠是任何Unicode字符,美圓標誌符($)或下劃線(_)。後面的字符能夠是任意字符和數字。所以,下面全是合法的標識符:
arg0
_tmp
$elem
π
一些標識符是「保留關鍵字」——他們是語法的一部分,不能用做變量名:
arguments break case catch class const continue debugger default delete do else enum eval export extends false finally for function if implements import in instanceof interface let new null package private protected public return static super switch this throw true try typeof var void while with yield
從技術上講,下面三個標識符不是保留字,但也不該該做爲變量名:
Infinity NaN undefined
JavaScript有全部咱們期待的編程語言值類型:布爾,數字,字符串,數組等。JavaScript中的全部值都有屬性。每一個屬性有一個鍵(或名字)和一個值。考慮記錄的域(fields of record)。你可使用點(.)操做符讀取屬性:
value.propKey
舉個例子:字符串「abc」有屬性lenght(長度)。
> var str = 'abc'; > str.length 3
上面的代碼也能夠寫成下面這樣:
> 'abc'.length
3
點操做符也能夠用來給屬性賦值:
> var obj = {}; // 空對象 > obj.foo = 123; // 建立屬性「foo」,設置它爲123 123 > obj.foo 123
你也能夠經過它(.)調用方法:
> 'hello'.toUpperCase()
'HELLO'
上面,咱們在值「hello」上面調用方法 toUpperCase()。
JavaScript定義了不一樣值之間的區別:
二者之間的主要區別在於他們是如何被比較的:每個對象有一個獨一無二的標誌,而且僅和本身相等:
> var obj1 = {}; // 一個空對象 > var obj2 = {}; // 另外一個空對象 > obj1 === obj2 false > obj1 === obj1 true
相反,全部原始值只要編碼值相同就被認爲是相同的:
> var prim1 = 123; > var prim2 = 123; > prim1 === prim2 true
接下來的兩節介紹原始值和對象的更多細節。
下面的全是原始類型值(簡稱:原始值):
原始值的特徵:
> 3 === 3 true > 'abc' === 'abc' true
> var str = 'abc'; > str.foo = 3; // try to create property `foo` ⇒ no effect > str.foo // unknown property undefined
全部非原始值(non-primitive)的值都是對象。最多見的幾種對象類型是:
{ firstName: 'Jane', lastName: 'Doe' }
[ 'apple', 'banana', 'cherry' ]
/^a+b+$/
對象的特徵:
> {} === {} // 兩個不一樣的空對象 false > var obj1 = {}; > var obj2 = obj1; > obj1 === obj2 true
> var obj = {}; > obj.foo = 123; > obj.foo 123
數組全部的數據結構(如)都是對象,但並非全部的對象都是數據結構。例如:正則表達式是對象,但不是一個數據結構。
多少有些沒必要要,JavaScript有兩個「無值(non-values)」:undefined 和 null。
> var foo; > foo undefined
> var obj = {}; // 空對象 > obj.foo undefined
> function f(x) { return x } > f() undefined
一般狀況下你應該把undefined和null當作是等價的,若是他們表明相贊成義的無值的話。檢查他們的一種方式是經過嚴格比較:
if (x === undefined || x === null) { ... }
另外一種在實際中使用的方法是認爲undefined 和 null 都是false:
if (!x) { ... }
警告:false,0,NaN 和 「」 都被看成false。
對象類型的實例Foo(包括內建類型,例如Array和其餘自定義類型)從對象Foo.prototype上獲取方法。你能夠經過讀這個方法但不調用它的方式驗證這點:
> [].push === Array.prototype.push true
相反,原始類型是沒有類型的,因此每一個原始類型有一個關聯類型,稱之爲包裝類型:
> true.toString === Boolean.prototype.toString true
注意包裝類型的名字首個字母是大寫的B。若是在JavaScript中布爾值的類型能夠訪問,那麼它可能會被成爲布爾對象。
包裝類型也有實例(他們的實例是對象),但不經常使用。相反,包裝類型有其餘用處:若是你將他們做爲函數調用,他們能夠將值轉換爲原始類型。
> Number('123') 123 > String(true) 'true'
有兩個操做符能夠用來將值分類:typeof 主要用來操做原始值,instanceof 主要用來造做對象。
typeof 使用方法以下:
typeof «value»
它返回描述 value 「類型」的一個字符串。例如:
> typeof true 'boolean' > typeof 'abc' 'string' > typeof {} // 空對象字面量 'object' > typeof [] // 空數組字面量 'object'
下標列出了全部typeof的結果:
操做數 | 結果 |
undefined | 'undefined' |
null | 'object' |
Boolean value | 'boolean' |
Number value | 'number' |
String value | 'string' |
Function | 'function' |
All other values | 'object' |
有兩個東西和咱們所說的原始值和對象是矛盾的:
instanceof使用方法以下:
«value» instanceof «Constr»
若是 value 是一個對象,而且value 是由構造函數Constr建立的(考慮:類)。例如:
> var b = new Bar(); // 經過構造函數Bar建立對象 > b instanceof Bar true > {} instanceof Object true > [] instanceof Array true > [] instanceof Object // 數字是對象的子類型 true
布爾類型原始值包括true和false。下面的操做符產生布爾值:
每當JavaScript但願一個布爾值時(例如:if語句的條件),可使用任何值。它將被理解(轉換)爲true或false。下面的值被理解爲false:
全部其餘值被認爲true。被理解爲false的值成爲假值(falsy),被理解爲true的值成爲真值(truthy)。可使用Boolean做爲函數測試值被理解爲何。
> Boolean(undefined) false > Boolean(0) false > Boolean(3) true
JavaScript中的二元邏輯運算符是短路運算——若是第一個操做數能夠肯定結果,第二個操做數將不被驗證。例如,在下面的代碼中,函數foo()不會被調用。
false && foo() true || foo()
此外,二元邏輯運算符返回操做數中的一個——多是一個布爾值,也可能不是。一張真值表用來決定返回哪一個值:
> NaN && 'abc'
NaN
> 123 && 'abc'
'abc'
> 'abc' || 123 'abc' > '' || 123 123
在JavaScript中檢測相等,你可使用嚴格相等(===)和嚴格不等(!==)。或者你也可使用非嚴格相等(==)和非嚴格不等(!=)。經驗規則:老是用嚴格運算符,僞裝非嚴格運算符不存在。嚴格相等更安全。
JavaScript中的全部數字都是浮點型(雖然大部分的JavaScript引擎內部也使用整數)。至於爲何這樣設計,查看這裏(每個JavaScript開發者應該瞭解的浮點知識)。
> 1 === 1.0
true
特殊數字:
> Number('xyz') // 'xyz' 不能被轉換爲數字 NaN
> 3 / 0 Infinity > Math.pow(2, 1024) // 數字太大了 Infinity
> +0 0 > -0 0
Therefore, it is best to pretend that there is only a single zero (as we have done when we looked at falsy values: both -0 and +0 are falsy).
JavaScript中有下列算數運算符:
全局對象Math經過函數提供更多算數運算操做。
JavaScript中也有位運算符(例如:位與 &)。
在2ality有一系列博文介紹這些內容,例如:
字符串能夠直接經過字符串字面量建立。這些字面量被單引號或雙引號包裹。反斜線(\)轉義字符而且產生一些控制字符。例如:
'abc' "abc" 'Did she say "Hello"?' "Did she say \"Hello\"?" 'That\'s nice!' "That's nice!" 'Line 1\nLine 2' // 換行 'Backlash: \\'
能夠經過方括號訪問單個字符:
> var str = 'abc'; > str[1] 'b'
length屬性是字符串的字符數量。
> 'abc'.length
3
提醒:字符串是不可變的,若是你想改變現有字符串,你須要建立一個行的字符串。
字符串能夠經過加號操做符(+)拼接,若是其中一個操做數爲字符串,會將另外一個操做數也轉換爲字符串。
> var messageCount = 3; > 'You have '+messageCount+' messages' 'You have 3 messages'
連續執行拼接操做可使用 += 操做符:
> var str = ''; > str += 'Multiple '; > str += 'pieces '; > str += 'are concatenated.'; > str 'Multiple pieces are concatenated.'
字符串有許多有用的方法。例如:
> 'abc'.slice(1) // 複製子字符串 'bc' > 'abc'.slice(1, 2) 'b' > '\t xyz '.trim() // 移除空白字符 'xyz' > 'mjölnir'.toUpperCase() 'MJÖLNIR' > 'abc'.indexOf('b') // 查找字符串 1 > 'abc'.indexOf('x') -1
if語句經過布爾條件決定執行那個分支:
if (myvar === 0) { // then } if (myvar === 0) { // then } else { // else } if (myvar === 0) { // then } else if (myvar === 1) { // else-if } else if (myvar === 2) { // else-if } else { // else }
下面的switch語句,furit的值決定那個分支被執行。
switch (fruit) { case 'banana': // ... break; case 'apple': // ... break; default: // 全部其餘狀況 // ... }
for 循環的格式以下:
for(初始化; 當條件成立時循環; 下一步操做)
例子:
for (var i=0; i < arr.length; i++) { console.log(arr[i]); }
當條件成立時while循環繼續循環它的循環體。
// 和上面的for循環相等 var i = 0; while (i < arr.length) { console.log(arr[i]); i++; }
當條件成立時,do-while循環繼續循環。因爲條件位於循環體以後,因此循環體老是被至少至少執行一次。
do { // ... } while(條件);
在全部的循環中:
定義函數的一種方法是經過函數聲明:
function add(param1, param2) { return param1 + param2; }
上面的代碼定義一個名稱叫作add的函數,有兩個參數param1和param2,而且返回參數的和。下面是你如何調用這個函數:
> add(6, 1) 7 > add('a', 'b') 'ab'
另外一種定義add()函數的方法是經過函數表達式:
var add = function (param1, param2) { return param1 + param2; };
函數表達式產生一個值,所以能夠直接將函數做爲參數傳遞給其餘函數:
someOtherFunction(function (p1, p2) { ... });
函數聲明會被提高,他們全被移動到當前做用域開始之處。這容許你在函數聲明以前調用它們:
function foo() { bar(); // 沒問題,bar被提高 function bar() { ... } }
注意:雖然變量聲明也會被提高,但賦值的過程不會被提高:
function foo() { bar(); // 有問題,bar是undefined var bar = function () { // ... }; }
在JavaScript中你能夠調用任意函數並傳遞任意數量的參數——語言毫不會抱怨。那能夠工做,然而,使全部參數可訪問須要經過特殊變量 arguments。arguments 看起來像數組,但它沒有數組的方法。
> function f() { return arguments } > var args = f('a', 'b', 'c'); > args.length 3 > args[0] // 獲取索引爲0的元素 'a'
讓咱們經過下面的函數探索JavaScript中傳遞太多或太少參數時如何處理(函數 toArray在後面提到)
function f(x, y) { console.log(x, y); console.log(toArray(arguments)); }
多出的參數將被忽略(能夠經過arguments訪問):
> f('a', 'b', 'c')
a b
[ 'a', 'b', 'c' ]
缺乏的參數將是undefined:
> f('a') a undefined [ 'a' ] > f() undefined undefined []
下面是一個常見模式,給參數設置默認值:
function pair(x, y) { x = x || 0; // (*) y = y || 0; return [ x, y ]; }
在(*)這行,若是x是真值(除了:null,undefined 等),||操做符返回x。不然,它返回第二個操做數。
> pair() [ 0, 0 ] > pair(3) [ 3, 0 ] > pair(3, 5) [ 3, 5 ]
若是你想強制參數的數量,你能夠檢測arguments.length:
function pair(x, y) { if (arguments.length !== 2) { throw new Error('Need exactly 2 arguments'); } ... }
arguments 不是一個數組,它僅僅是類數組(array-like):它有一個length屬性,而且你能夠經過方括號索引方式訪問它的元素。然而,你不能移除元素,或在它上面調用任何數組方法。所以,有時你須要將其轉換爲數組。這就是下面函數的做用。
function toArray(arrayLikeObject) { return [].slice.call(arrayLikeObject); }
異常處理最多見的方式像下面這樣:
function throwException() { throw new Error('Problem!'); } try { throwException(); } catch (e) { console.log(e); // 錯誤:信息 console.log(e.stack); // 非標準,但大部分瀏覽器支持 }
try分支包裹易出錯的代碼,若是try分支內部拋出異常,catch分支將會執行。
嚴格模式開啓檢測和一些其餘措施,是JavaScript變成更整潔的語言。推薦使用嚴格模式。爲了開啓嚴格模式,只需在JavaScript文件或script標籤第一行添加以下語句:
'use strict';
你也能夠在每一個函數上選擇性開啓嚴格模式,只需將上面的代碼放在函數的開頭:
function functionInStrictMode() { 'use strict'; }
下面的兩小節看下嚴格模式的三大好處。
讓咱們看一個例子,嚴格模式給咱們明確的錯誤,不然JavaScript老是靜默失敗:下面的函數 f() 執行一些非法操做,它試圖更改全部字符串都有的只讀屬性——length:
function f() { 'abc'.length = 5; }
當你調用上面的函數,它靜默失敗,賦值操做被簡單忽略。讓咱們將 f() 在嚴格模式下運行:
function f_strict() { 'use strict'; 'abc'.length = 5; }
如今瀏覽器報給咱們一些錯誤:
> f_strict()
TypeError: Cannot assign to read only property 'length' of abc
在嚴格模式下,不做爲方法的函數中的this值是undefined:
function f_strict() { 'use strict'; return this; } console.log(f_strict() === undefined); // true
在非嚴格模式下,this的值是被稱做全局對象(global object)(在瀏覽器裏是window):
function f() { return this; } console.log(f() === window); // true
在非嚴格模式下,若是你給不存在的變量賦值,JavaScript會自動建立一個全局變量:
> function f() { foo = 5 } > f() // 不會報錯 > foo 5
在嚴格模式下,這會產生一個錯誤:
> function f_strict() { 'use strict'; foo2 = 4; } > f_strict() ReferenceError: foo2 is not defined
在JavaScript中,你必須經過var聲明變量,在你使用它們以前:
> var x; > x = 3; > y = 4; ReferenceError: y is not defined
你能夠用一條var語句聲明和初始化多個變量:
var x = 1, y = 2, z = 3;
但我建議每一個變量使用一條語句。所以,我將上面的語句重寫爲:
var x = 1; var y = 2; var z = 3;
因爲提高(見下文),最好在函數頂部聲明變量。
變量的做用域老是整個函數(沒有塊級做用域)。例如:
function foo() { var x = -3; if (x < 0) { // (*) var tmp = -x; ... } console.log(tmp); // 3 }
咱們能夠看到tmp變量不只在(*)所在行的語句塊,它在整個函數內都存在。
變量聲明會被提高:聲明會被移到函數的頂部,但賦值過程不會。舉個例子,在下面的函數中(*)行位置聲明瞭一個變量。
function foo() { console.log(tmp); // undefined if (false) { var tmp = 3; // (*) } }
在內部,上面的函數被執行像下面這樣:
function foo() { var tmp; // declaration is hoisted console.log(tmp); if (false) { tmp = 3; // assignment stays put } }
每一個函數保持和函數體內部變量的鏈接,甚至離開建立它的做用域以後。例如:
function createIncrementor(start) { return function () { // (*) return start++; } }
在(*)行開始的函數在它建立時保留上下文,並在內部保存一個start活動值:
> var inc = createIncrementor(5); > inc() 5 > inc() 6 > inc() 7
閉包是一個函數加上和其做用域鏈的連接。所以,createIncrementor() 返回的是一個閉包。
有時你想模擬一個塊,例如你想將變量從全局做用域隔離。完成這個工做的模式叫作 IIFE(當即執行函數表達式(Immediately Invoked Function Expression)):
(function () { // 塊開始 var tmp = ...; // 非全局變量 }()); // 塊結束
上面你會看到函數表達式被當即執行。外面的括號用來阻止它被解析成函數聲明;只有函數表達式能被當即調用。函數體產生一個新的做用域並使 tmp 變爲局部變量。
下面是個經典問題,若是你不知道它那它會讓你費盡思量。所以,先瀏覽下,先對問題有個大概的瞭解。
閉包保持和外部變量的鏈接,有時可能和你想像的行爲不一致:
var result = []; for (var i=0; i < 5; i++) { result.push(function () { return i }); // (*) } console.log(result[1]()); // 5 (不是 1) console.log(result[3]()); // 5 (不是 3)
(*)行的返回值老是當前的i值,而不是當函數被建立時的i值。當循環結束後,i的值是5,這是爲何數組中的全部函數的返回值老是同樣的。若是你想捕獲當前變量的快照,你可使用 IIFE:
for (var i=0; i < 5; i++) { (function (i2) { result.push(function () { return i2 }); }(i)); // 複製當前的i }
和全部的值類型同樣,對象有屬性。事實上,你能夠將對象看成一組屬性的集合,每一個屬性是一對(鍵和值)。鍵是字符串,值能夠是任意JavaScript值。到目前爲止,咱們僅僅見過鍵是標識符的屬性,由於點操做符處理的鍵必須爲標識符。在這節,你講見到另外一種方法屬性的方法,能將任意字符串做爲鍵。
在JavaScript中,你能夠直接建立對象,經過對象字面量:
var jane = { name: 'Jane', describe: function () { 'use strict'; return 'Person named '+this.name; } };
上面的對象有兩個屬性:name 和 describe。你能讀(「get」)和 寫(「set」)屬性:
> jane.name // get 'Jane' > jane.name = 'John'; // set > jane.newProperty = 'abc'; // 自動建立
屬性是函數如 describe 能夠被看成方法調用。當調用他們時能夠在它們內部經過this引用對象。
> jane.describe() // 調用方法 'Person named John' > jane.name = 'Jane'; > jane.describe() 'Person named Jane'
in 操做符用來檢測一個屬性是否存在:
> 'newProperty' in jane true > 'foo' in jane false
你若你讀取一個不存在的屬性,你將獲得undefined值。所以上面的兩個檢查也能夠像下面這樣:
> jane.newProperty !== undefined true > jane.foo !== undefined false
delete操做符用來刪除一個屬性:
> delete jane.newProperty true > 'newProperty' in jane false
屬性的鍵能夠是任意字符串。到目前爲止,咱們看到的對象字面量中的和點操做符後的屬性關鍵字。按這種方法你只能使用標識符。若是你想用其餘任意字符串做爲鍵名,你必須在對象字面量里加上引號,並使用方括號獲取和設置屬性。
> var obj = { 'not an identifier': 123 }; > obj['not an identifier'] 123 > obj['not an identifier'] = 456;
方括號容許你動態計算屬性關鍵字:
> var x = 'name'; > jane[x] 'Jane' > jane['na'+'me'] 'Jane'
若是你引用一個方法,它將失去和對象的鏈接。就其自己而言,函數不是方法,其中的this值爲undefined(嚴格模式下)。
> var func = jane.describe; > func() TypeError: Cannot read property 'name' of undefined
解決辦法是使用函數內置的bind()方法。它建立一個新函數,其this值固定爲給定的值。
> var func2 = jane.describe.bind(jane); > func2() 'Person named Jane'
每一個函數都有一個特殊變量this。若是你在方法內部嵌入函數是很不方便的,由於你不能從函數中訪問方法的this。下面是一個例子,咱們調用forEach循環一個數組:
var jane = { name: 'Jane', friends: [ 'Tarzan', 'Cheeta' ], logHiToFriends: function () { 'use strict'; this.friends.forEach(function (friend) { // 這裏的「this」是undefined console.log(this.name+' says hi to '+friend); }); } }
調用 logHiToFriends 會產生錯誤:
> jane.logHiToFriends()
TypeError: Cannot read property 'name' of undefined
有兩種方法修復這問題。#1:將this存儲在不一樣的變量。
logHiToFriends: function () { 'use strict'; var that = this; this.friends.forEach(function (friend) { console.log(that.name+' says hi to '+friend); }); }
#2:forEach的第二個參數容許提供this值。
logHiToFriends: function () { 'use strict'; this.friends.forEach(function (friend) { console.log(this.name+' says hi to '+friend); }, this); }
在JavaScript中函數表達式常常被用做函數參數。時刻當心函數表達式中的this。
目前爲止,你可能認爲JavaScript的對象僅是鍵值的映射,經過JavaScript對象字面量能夠得出這個觀點,看起來很像其餘語言中的地圖/字典(map/dictionary)。然而,JavaScript對象也支持真正意義上的面向對象特性:繼承(inheritance)。本節會徹底講解JavaScript中繼承的工做原理,但會給你以此爲開始的簡單模式。若是你想獲得更多知識,請查閱這篇文章「JavaScript inheritance by example」。
除了做爲「真正」的函數和方法,函數還在JavaScript中扮演第三種角色:若是經過new操做符調用,他們會變爲構造函數,對象的工廠。構造函數是對其餘語言中的類的粗略模擬。約定俗稱,構造函數的第一個字母大寫。例如:
// 設置實例數據 function Point(x, y) { this.x = x; this.y = y; } // 方法 Point.prototype.dist = function () { return Math.sqrt(this.x*this.x + this.y*this.y); };
咱們看到構造函數分爲兩部分:首先,Point函數設置實例數據。其次,Point.prototype屬性包含對象的方法。前者的數據是每一個實例私有的,後面的數據是全部實例共享的。
咱們經過new操做符調用Point:
> var p = new Point(3, 5); > p.x 3 > p.dist() 5.830951894845301
p是Point的一個實例:
> p instanceof Point true > typeof p 'object'
數組是數組元素的序列,能經過整數索引方法數組元素,數組索引從0開始。
數組字面量建立數組很方便:
> var arr = [ 'a', 'b', 'c' ];
上面的數組有三個元素:分別是字符串「a」,「b」, 「c」。你能夠經過整數索引訪問它們:
> arr[0] 'a' > arr[0] = 'x'; > arr [ 'x', 'b', 'c' ]
length屬性總表示一個數組有多少項元素。
> arr.length
3
除此以外它也能夠用來從數組上移除尾部元素:
> arr.length = 2; > arr [ 'x', 'b' ]
in操做符也能夠在數組上工做。
> 1 in arr // arr在索引爲1處是否有元素? true > 5 in arr // arr在索引爲5處是否有元素? false
值得注意的是數組是對象,所以能夠有對象屬性:
> arr.foo = 123; > arr.foo 123
數組有許多方法。舉些例子:
> var arr = [ 'a', 'b', 'c' ]; > arr.slice(1, 2) // 複製元素 [ 'b' ] > arr.slice(1) [ 'b', 'c' ] > arr.push('x') // 在末尾添加一個元素 4 > arr [ 'a', 'b', 'c', 'x' ] > arr.pop() // 移除最後一個元素 'x' > arr [ 'a', 'b', 'c' ] > arr.shift() // 移除第一個元素 'a' > arr [ 'b', 'c' ] > arr.unshift('x') // 在前面添加一個元素 3 > arr [ 'x', 'b', 'c' ] > arr.indexOf('b') // 查找給定項在數組中的索引,若不存在返回-1 1 > arr.indexOf('y') -1 > arr.join('-') // 將元素拼接爲一個字符串 'x-b-c' > arr.join('') 'xbc' > arr.join() 'x,b,c'
有幾種方法能夠遍歷數組元素。其中兩個最重要的是 forEach 和 map。
forEach遍歷整個數組,並將當前元素和它的索引傳遞給一個函數:
[ 'a', 'b', 'c' ].forEach( function (elem, index) { // (*) console.log(index + '. ' + elem); });
上面代碼的輸出
0. a 1. b 2. c
注意(*)行的函數參數是可省略的。例如:它能夠只有一個參數 elem。
map建立一個新數組,經過給每一個存在數組元素應用一個函數:
> [1,2,3].map(function (x) { return x*x }) [ 1, 4, 9 ]
JavaScript內建支持正則表達式。他們被雙斜線分隔:
/^abc$/ /[A-Za-z0-9]+/
> /^a+b+$/.test('aaab') true > /^a+b+$/.test('aaa') false
> /a(b+)a/.exec('_abbba_aba_')
[ 'abbba', 'bbb' ]
返回的數組第一項(索引爲0)是完整匹配,捕獲的第一個分組在第二項(索引爲1),等。有一種方法能夠反覆調用獲取全部匹配。
> '<a> <bbb>'.replace(/<(.*?)>/g, '[$1]')
'[a] [bbb]'
replace的第一個參數必須是正則表達式,而且開啓全局搜索(/g 標記),不然僅第一個匹配項會被替換。有一種方法使用一個函數來計算替換項。
Math是一個有算數功能的對象。例如:
> Math.abs(-2) 2 > Math.pow(3, 2) // 3^2 9 > Math.max(2, -1, 5) 5 > Math.round(1.9) 2 > Math.cos(Math.PI) // 預約義常量π -1
JavaScript標準庫相對簡單,但有不少其餘東西你可使用:
在你學會了這篇文章的基礎教程後,你能夠轉到大部分章節末尾提到的高級教程。此外,我建議你看下面的資源:
我嘗試找到JavaScript的一個最理想子集。我成功了嗎?須要增刪一些東西嗎?若是你是一個JavaScript新手,我特別想聽聽你的意見:當讀這篇文章的時候是很是容易理解嗎?或是你在某些地方卡住?
原文:http://www.2ality.com/2013/06/basic-javascript.html