精品伊人久久大香线蕉,开心久久婷婷综合中文字幕,杏田冲梨,人妻无码aⅴ不卡中文字幕

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
JavaScript簡易教程

這是我所知道最完整最簡潔的JavaScript基礎教程。

本文將帶你盡快走進JavaScript的世界——前提是你有一些編程經驗的話。本文試圖描述這門語言的最小子集。我給這個子集起名叫做“JavaScript簡易教程”,并推薦那些準備深入閱讀細節和高級技巧之前的新手閱讀。合抱之木生于毫末,九層之臺起于壘土,欲速則不達。本文的最后會提出如何進一步學習。

警告:下面是我所描述的規則集和最佳實踐。我喜歡整潔清晰(例如,你可以隨時通過下面的目錄快速導航)。橫看成嶺側成峰,遠近高低各不同,雖然規則是無懈可擊的,但不可避免——每個人的理解會各不相同。

目錄

  1. 本文約定
  2. 語言的性質
  3. 語法
  4. 變量和賦值
  5. 布爾
  6. 數字
  7. 字符串
  8. 語句
  9. 函數
  10. 異常處理
  11. 嚴格模式
  12. 變量作用域和閉包
  13. 對象和繼承
  14. 數組
  15. 正則表達式
  16. 數學
  17. 標準庫的其他功能
  18. 下一步學什么?

本文約定(Conventions used in this blog post)

命令行交互(Command line interaction)

每當我介紹一個新概念,我都會嘗試通過JavaScript命令行進行演示。像下面這樣:

> 3   4  7

大于號后面的文本是用戶輸入內容。其他的都是JavaScript引擎的輸出內容。此外,也可以使用console.log()來向控制臺打印數據(這種方法可以在大部分JavaScript引擎中工作,包括Node.js).

查找文檔(Finding documentation)

有時你會看到一些函數或方法有超鏈接,你應該清楚他們的工作原理。如果沒有,可以在Mozilla Developer Network (MDN)上查看細節,你也可以使用Google在MDN上查找文檔。例如,下面是通過Google搜索數組的push()方法的例子:

mdn array push

語言的性質(The nature of the language)

本節對JavaScript的性質做簡要介紹,以幫你理解一些疑問。

JavaScript 和 ECMAScript(JavaScript versus ECMAScript)

編程語言稱為JavaScript,語言標準被稱為ECMAScript。他們有不同名字的原因是因為“Java”已經被注冊為商標(屬于Oracle)。目前,只有Mozilla被正式允許使用“JavaScript”名稱,因為很久以前他們得到一份許可。因此,開放的語言標準擁有不同的名字。當前的JavaScript版本是ECMAScript 5,ECMAScript 6當前是開發版。

影響(Influences)

JavaScript之父,Brendan Eich 別無選擇必須迅速創建一門語言。(否則,會更糟糕,Netscape將使用其他技術)。他借鑒了幾門其他語言:

  • JavaScript借鑒了Java的語法和如何區分原始值和對象。
  • JavaScript的函數設計受Scheme和AWK的啟發——他們(的函數)都是第一類(first-class)對象,并且在語言中廣泛使用。閉包使他們(函數)變成強大的工具。
  • Self影響了JavaScript獨一無二的面向對象編程(OOP)風格。它的核心思想(在這里我們沒有提到)非常優雅,基于此創建的語言非常少。但后面會提到一個簡單的模式照顧大部分用例。JavaScript面向對象編程的殺手級特性是你可以直接創建對象。不需要先創建類或其他類似的東西。
  • Perl和Python影響了JavaScript字符串,數組和正則表達式的操作。

JavaScript直到ECMAScript 3才加入異常處理,這解釋了為什么這門語言經常自動轉換類型和經常靜默失?。鹤畛鯖]有拋出異常的功能。

一方面,JavaScript有很多怪癖,并且缺失很多功能(塊級變量作用域(block-sciped variables),模塊(modules)支持子類型(subtyping)等)。另一方面,它有幾個非常強大的特性,允許你彌補上面的問題。在其他語言中,你要學習語言特性。在JavaScript中,你需要經常學習模式代替。

深入閱讀(Further reading)

語法(Syntax)

這節介紹一些JavaScript的基本語法規則。

語句和表達式(Statements versus expressions)

了解JavaScript的語法,先來了解兩個主要的語法類型:語句和表達式。

  • 語句通?!白瞿承┦虑椤?。程序是一組語句序列。舉個例子,下面聲明(創建)一個變量 foo: 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)是一個表達式。他們都實現函數調用。

流程控制語句和語句塊(Control flow statements and blocks)

流程控制語句,其語句體可以是單條語句。舉兩個例子:

if (obj !== null) obj.foo();while (x > 0) x--;

然而,任何語句總能被語句塊代替,花括號包含零或多條語句。因此,你也可以這樣寫:

if (obj !== null) {    obj.foo();}while (x > 0) {    x--;}

在本文中,我們只使用后一種方式。

分號(Semicolons)

JavaScript中的分號是可選的。但省略(分號)可能會帶來意想不到的結果,所以我建議你不要那樣做。

正如上面所看到的,分號作為語句的結尾,但語句塊不需要。僅有一種情況下你能看到語句塊后面有分號——函數表達式后面的函數體塊。表達式作為語句的結尾,后面是分號:

var x = 3 * 7;var f = function () { };

注釋(Comments)

JavaScript的注釋有兩種形式:單行注釋和多行注釋。單行注釋以//開頭,以換行符結尾:

x  ; // 單行(single-line)注釋

多行注釋用/**/包裹

/*  這是多行注釋 多行哦 */

深入閱讀

變量和賦值(Variables and assignment)

JavaScript中的變量在使用前必須先聲明,否則會報錯引用錯誤(Reference Error):

var foo;  // 聲明變量“foo”

賦值(Assignment)

你可以在聲明變量的同時為其賦值:

var foo = 6;

你也可以給已經存在的變量重新賦值:

foo = 4;  // 更改變量的值

復合賦值操作符(Compount assignment operators)

有很多復合賦值操作符,例如 =。下面的兩個賦值操作等價:

x  = 1;x = x   1;

標識符和變量名(Identifiers and variable names)

標識符就是事物的名字,在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

深入閱讀

值(Values)

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

原始類型值和對象(Primitive values versus objects)

JavaScript定義了不同值之間的區別:

  • 原始值包括:boolean,number,string,null和undefined,
  • 所有其他的值都是對象。實際上對象被定義為——所有不為原始值的值。

兩者之間的主要區別在于他們是如何被比較的:每一個對象有一個獨一無二的標志,并且僅和自己相等:

> var obj1 = {};  // 一個空對象> var obj2 = {};  // 另一個空對象> obj1 === obj2  false> obj1 === obj1  true

相反,所有原始值只要編碼值相同就被認為是相同的:

> var prim1 = 123;> var prim2 = 123;> prim1 === prim2  true

接下來的兩節會介紹原始值和對象的更多細節。

原始類型值(Primitive values)

下面全是原始類型值(簡稱:原始值):

原始值的特征:

  • 值做比較時:“內容”做比較。

      > 3 === 3    true  > 'abc' === 'abc'    true
  • 無法更改:值的屬性無法更改,無法添加和移除屬性。

      > var str = 'abc';  > str.foo = 3; // try to create property `foo` ? no effect  > str.foo  // unknown property    undefined 

    (獲取未知屬性總返回undefined)

  • 原始值的集合是固定的(fixed set of values):你不能自定義原始值。

對象(Objects)

所有非原始值(non-primitive)的值都是對象。最常見的幾種對象類型是:

  • 簡單對象(類型是Object)能通過對象字面量創建:

    { firstName: ‘Jane’, lastName: ‘Doe’ }

    上面的對象有兩個屬性:firstName屬性的值是“Jane”,lastName屬性的值是“Doe”。

  • 數組(類型是 Array)能通過數組字面量創建:

    [ ‘apple’, ‘banana’, ‘cherry’ ]

    上面的數組有三個元素,可以通過數字索引訪問。例如“apple”的索引是0.

  • 正則表達式對象(類型是 RegExp)能通過正則表達式字面量創建。

    /^a b $/

對象的特征:

  • 比較的是引用:比較的是標識符,每個值有自己的標識符。

      > {} === {}  // 兩個不同的空對象    false	  > var obj1 = {};  > var obj2 = obj1;  > obj1 === obj2    true
  • 默認可以更改。

       > var obj = {};   > obj.foo = 123;   > obj.foo     123

-** 用戶可擴展(user-extensible):**你可以通過構造函數定義新的對象類型。

所有的數據結構(如數組)都是對象,但并不是所有的對象都是數據結構。例如:正則表達式是對象,但不是數據結構。

undefined 和 null(undefined and null)

多少有些不必要,JavaScript有兩個“無值(non-values)”:undefined 和 null。

  • undefined的意思是“沒有值(no value)”。未初始化的變量是undefined:

      > var foo;  > foo  undefined

    讀取不存在的屬性時,將返回undefined:

      > var obj = {}; // 空對象  > obj.foo    undefined

    缺省的參數也是undefined:

      > function f(x) { return x }  > f()    undefined
  • null的意思是“沒有對象(no object)”。它被用來表示對象的無值(參數,鏈上的對象等)。

通常情況下你應該把undefined和null看成是等價的,如果他們代表相同意義的無值的話。檢查他們的一種方式是通過嚴格比較:

if (x === undefined || x === null) {    ...}

另一種在實際中使用的方法是認為undefined 和 null 都是false

if (!x) {    ...}

警告:false,0,NaN 和 “” 都被當作false。

包裝類型(Wrapper types)

對象類型的實例Foo(包括內建類型,例如Array和其他自定義類型)從對象Foo.prototype上獲取方法。你可以通過讀取這個方法的方式(不是調用)驗證這點:

> [].push === Array.prototype.push  true

相反,原始類型是沒有類型的,所以每個原始類型有一個關聯類型,稱之為包裝類型:

  • 布爾值的包裝類型是 Boolean。布爾值從Boolean.prototype上獲取方法:

      > true.toString === Boolean.prototype.toString	true

    注意包裝類型名字的首字母是大寫的B。如果在JavaScript中布爾值的類型可以訪問,那么它可能會被轉換為布爾對象。

  • 數字值的包裝類型是Number。
  • 字符串值的包裝類型是String。

包裝類型也有實例(他們的實例是對象),但不常用。相反,包裝類型有其他用處:如果你將他們作為函數調用,他們可以將值轉換為原始類型。

> Number('123')  123> String(true)  'true'

通過typeof 和 instanceof 將值分類(Categorizing values via typeof and instanceof)

有兩個操作符可以用來將值分類:typeof 主要用于原始值,instanceof 主要用于對象。

typeof 使用方法如下:

typeof ?value?

typeof返回描述 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'

有兩個結果和我們上面說的的原始值與對象是矛盾的:

  • 函數的類型是“function”而不是“object”。鑒于函數(類型為“function”)是對象(類型是對象)的子類型,這不是一個錯誤。
  • null的類型是“object”。這是一個bug,但從沒被修復,因為修復后會破壞現有的代碼。

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

深入閱讀

布爾(Booleans)

布爾類型原始值包括true和false。下面的操作符產生布爾值:

  • 二元邏輯運算符:&&(與),   (或)
  • 前綴邏輯運算符:!(非)
  • 等值運算符:=== !== == !=
  • 比較運算符(字符串或數字):> >= < <=

真值和假值(Truthy and falsy)

每當JavaScript希望一個布爾值時(例如:if語句的條件),可以使用任何值。它將被理解(轉換)為true或false。下面的值被理解為false:

  • undefined, null
  • 布爾: false
  • 數字: -0, NaN
  • 字符串: ‘’

所有其他值被認為true。被理解為false的值稱為假值(falsy),被理解為true的值稱為真值(truthy)??梢允褂肂oolean作為函數,測試值被理解為什么。

> Boolean(undefined)  false> Boolean(0)  false> Boolean(3)  true

二元邏輯運算符(Binary logical operators)

JavaScript中的二元邏輯運算符是短路運算——如果第一個操作數可以確定結果,第二個操作數將不被驗證(運算)。例如,在下面的代碼中,函數foo()永遠不會被調用。

false && foo()true  || foo()

此外,二元邏輯運算符會返回操作數中的一個——可能是一個布爾值,也可能不是。一張真值表用來決定返回哪個值:

  • 與:如果第一個操作數是假值,返回第一個。否則返回第二個操作數。

      > NaN && 'abc'    NaN  > 123 && 'abc'    'abc'
  • 或:如果第一個操作數是真值,返回第一個。否則,返回第二個操作數。

      > 'abc' || 123    'abc'  > '' || 123    123

等值運算符(Equality operators)

在JavaScript中檢測相等,你可以使用嚴格相等(===)和嚴格不等(!==)。或者你也可以使用非嚴格相等(==)和非嚴格不等(!=)。經驗規則:總是用嚴格運算符,假裝非嚴格運算符不存在。嚴格相等更安全。

深入閱讀

數字(Numbers)

JavaScript中的所有數字都是浮點型(雖然大部分的JavaScript引擎內部也使用整數)。至于為什么這樣設計,查看這里(每一個JavaScript開發者應該了解的浮點知識)。

> 1 === 1.0  true

特殊數字:

  • NaN (“不是一個數字 not a number”): 錯誤值。

      > Number('xyz')  // 'xyz' 不能被轉換為數字    NaN
  • Infinity:也是最大錯誤值(溢出).

      > 3 / 0    Infinity  > Math.pow(2, 1024)  // 數字太大了    Infinity

    Infinity 有時很有用,因為它比任何其他數字都大。同樣,-Infinity 比其他任何數字都小。

  • JavaScript有兩個零, 0 和 -0。它(js引擎)通常不讓你看到,并簡單將兩個零都顯示為0:

      >  0    0  > -0    0

    因此最好假裝只有一個零(正如我們看到假值時所做的那樣:-0 和 0 都是假值)。

運算符(Operators)

JavaScript中有下列算數運算符

  • 加: number1 number2
  • 減: number1 - number2
  • 乘: number1 * number2
  • 除: number1 / number2
  • 模: number1 % number2
  • 自增: variable, variable
  • 自減: –variable, variable–
  • 負值: -value
  • 正值(轉換為數字): value

全局對象Math通過函數提供更多算數運算操作。

JavaScript中也有位運算符(例如:位與 &)。

深入閱讀

在2ality有一系列博文介紹這些內容,例如:

字符串(Strings)

字符串可以直接通過字符串字面量創建。這些字面量被單引號或雙引號包裹。反斜線(\)轉義字符并且產生一些控制字符。例如:

'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

提醒:字符串是不可變的,如果你想改變現有字符串,你需要創建一個新的字符串。

字符串運算符(String operators)

字符串可以通過加號操作符( )拼接,如果其中一個操作數為字符串,會將另一個操作數也轉換為字符串。

> 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.'

字符串方法(String methods)

字符串有許多有用的

深入閱讀

語句(Statements)

條件(Conditionals)

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:  // 所有其他情況        // ...}

循環(Loops)

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(條件);

在所有的循環中:

  • break中斷循環
  • continue開始一個新的循環迭代

函數(Functions)

定義函數的一種方法是通過函數聲明:

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 declarations are hoisted)

函數聲明會被提升,他們全被移動到當前作用域開始之處。這允許你在函數聲明之前調用它們:

function foo() {    bar();  // 沒問題,bar被提升    function bar() {        ...    }}

注意:雖然變量聲明也會被提升,但賦值的過程不會被提升:

function foo() {    bar();  // 有問題,bar是undefined    var bar = function () {        // ...    };}

特殊變量arguments(The special variable arguments)

在JavaScript中你可以調用任意函數并傳遞任意數量的參數——語言絕不會抱怨(參數檢測)。都可以正常工作,然而,使所有參數可訪問需要通過特殊變量 arguments。arguments 看起來像數組,但它沒有數組的方法(稱為類數組 array-like)。

> function f() { return arguments }> var args = f('a', 'b', 'c');> args.length3> args[0]  // 獲取索引為0的元素'a'

太多或太少參數(Too many or too few arguments)

讓我們通過下面的函數探索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[]

可選參數(Optional parameters)

下面是一個常見模式,給參數設置默認值:

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 ] 

強制數量(Enforcing an arity)

如果你想強制參數的數量,你可以檢測arguments.length:

function pair(x, y) {    if (arguments.length !== 2) {        throw new Error('Need exactly 2 arguments');    }    ...}

將arguments 轉換為數組(Converting arguments to an array)

arguments 不是一個數組,它僅僅是類數組(array-like):它有一個length屬性,并且你可以通過方括號索引方式訪問它的元素。然而,你不能移除元素,或在它上面調用任何數組方法。因此,有時你需要將其轉換為數組。這就是下面函數的作用。

function toArray(arrayLikeObject) {    return [].slice.call(arrayLikeObject);}

深入閱讀

異常處理(Exception handling)

異常處理最常見的方式像下面這樣:

function throwException() {    throw new Error('Problem!');}try {    throwException();} catch (e) {    console.log(e);  // 錯誤:信息    console.log(e.stack);  // 非標準,但大部分瀏覽器支持}

try分支包裹易出錯的代碼,如果try分支內部拋出異常,catch分支將會執行。

深入閱讀

嚴格模式(Strict mode)

你也可以在每個函數上選擇性開啟嚴格模式,只需將上面的代碼放在函數的開頭:

function functionInStrictMode() {    'use strict';}

下面的兩小節看下嚴格模式的三大好處。

明確錯誤(Explicit errors)

讓我們看一個例子,嚴格模式給我們明確的錯誤,否則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(this in non-method functions)

在嚴格模式下,不作為方法的函數中的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

不再自動創建全局變量(No auto-created global variables)

在非嚴格模式下,如果你給不存在的變量賦值,JavaScript會自動創建一個全局變量:

> function f() { foo = 5 }> f()  // 不會報錯> foo5

在嚴格模式下,這會產生一個錯誤:

> function f_strict() { 'use strict'; foo2 = 4; }> f_strict()ReferenceError: foo2 is not defined

深入閱讀

變量作用域和閉包(Variable scoping and closures)

在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;

由于提升(見下文),最好在函數頂部聲明變量。

變量和函數作用域(Variables are function-scoped)

變量的作用域總是整個函數(沒有塊級作用域)。例如:

function foo() {    var x = -3;    if (x < 0) {  // (*)        var tmp = -x;        ...    }    console.log(tmp);  // 3}

我們可以看到tmp變量不僅在(*)所在行的語句塊存在,它在整個函數內都存在。

變量提升(Variables are hoisted)

變量聲明會被提升:聲明會被移到函數的頂部,但賦值過程不會。舉個例子,在下面的函數中(*)行位置聲明了一個變量。

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    }}

閉包(Closures)

每個函數保持和函數體內部變量的連接,甚至離開創建它的作用域之后。例如:

function createIncrementor(start) {    return function () {  // (*)        return start  ;    }}

在(*)行開始的函數在它創建時保留上下文,并在內部保存一個start活動值:

> var inc = createIncrementor(5);> inc()5> inc()6> inc()7

閉包是一個函數加上和其作用域鏈的鏈接。因此,createIncrementor() 返回的是一個閉包。

IIFE:模擬塊級作用域(IIFE: Simulating block scoping)

有時你想模擬一個塊,例如你想將變量從全局作用域隔離。完成這個工作的模式叫做 IIFE(立即執行函數表達式(Immediately Invoked Function Expression)):

(function () {  // 塊開始    var tmp = ...;  // 非全局變量}());  // 塊結束

上面你會看到函數表達式被立即執行。外面的括號用來阻止它被解析成函數聲明;只有函數表達式能被立即調用。函數體產生一個新的作用域并使 tmp 變為局部變量。

閉包實現變量共享(Inadvertent sharing via closures)

下面是個經典問題,如果你不知道,會讓你費盡思量。因此,先瀏覽下,對問題有個大概的了解。

閉包保持和外部變量的連接,有時可能和你想像的行為不一致:

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}

深入閱讀

對象和繼承(Objects and inheritance)

和所有的值類型一樣,對象有屬性。事實上,你可以將對象當作一組屬性的集合,每個屬性都是一對(鍵和值)。鍵是字符串,值可以是任意JavaScript值。到目前為止,我們僅僅見過鍵是標識符的屬性,因為點操作符處理的鍵必須為標識符。在這節,你講見到另一種訪問屬性的方法,能將任意字符串作為鍵。

單個對象(Single objects)

在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 janetrue> 'foo' in janefalse

若讀取一個不存在的屬性,將會得到undefined值。因此上面的兩個檢查也可以像下面這樣:

> jane.newProperty !== undefinedtrue> jane.foo !== undefinedfalse

delete操作符用來刪除一個屬性:

> delete jane.newPropertytrue> 'newProperty' in janefalse

任意鍵屬性(Arbitrary property keys)

屬性的鍵可以是任意字符串。到目前為止,我們看到的對象字面量中的和點操作符后的屬性關鍵字。按這種方法你只能使用標識符。如果你想用其他任意字符串作為鍵名,你必須在對象字面量里加上引號,并使用方括號獲取和設置屬性。

> 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'

引用方法(Extracting methods)

如果你引用一個方法,它將失去和對象的連接。就其本身而言,函數不是方法,其中的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'

方法內部的函數(Functions inside a method)

每個函數都有一個特殊變量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。

構造函數:對象工廠(Constructors: factories for objects)

目前為止,你可能認為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.x3> p.dist()5.830951894845301

p是Point的一個實例:

> p instanceof Pointtrue> typeof p'object'

深入閱讀

數組(Arrays)

數組是數組元素的序列,能通過整數索引方法數組元素,數組索引從0開始。

數組字面量(Array literals)

數組字面量創建數組很方便:

> var arr = [ 'a', 'b', 'c' ];

上面的數組有三個元素:分別是字符串“a”,“b”, “c”。你可以通過整數索引訪問它們:

> arr[0]'a'> arr[0] = 'x';> arr[ 'x', 'b', 'c' ]

length屬性總表示一個數組有多少項元素。

> arr.length3

除此之外它也可以用來從數組上移除尾部元素:

> arr.length = 2;> arr[ 'x', 'b' ]

in操作符也可以在數組上工作。

> 1 in arr // arr在索引為1處是否有元素?true> 5 in arr // arr在索引為5處是否有元素?false

值得注意的是數組是對象,因此可以有對象屬性:

> arr.foo = 123;> arr.foo123

數組方法(Array methods)

數組有許多方法。舉些例子:

> 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')  // 查找給定項在數組中的索引,若不存在返回-11> arr.indexOf('y') -1> arr.join('-')  // 將元素拼接為一個字符串'x-b-c'> arr.join('')'xbc'> arr.join()'x,b,c'

遍歷數組(Iterating over arrays)

有幾種方法可以遍歷數組元素。其中兩個最重要的是 forEach 和 map。

forEach遍歷整個數組,并將當前元素和它的索引傳遞給一個函數:

[ 'a', 'b', 'c' ].forEach(    function (elem, index) {  // (*)        console.log(index   '. '   elem);    });

上面代碼的輸出

0. a1. b2. c

注意(*)行的函數參數是可省略的。例如:它可以只有一個參數 elem。

map創建一個新數組,通過給每個存在數組元素應用一個函數:

> [1,2,3].map(function (x) { return x*x })[ 1, 4, 9 ]

深入閱讀

正則表達式(Regular expressions)

JavaScript內建支持正則表達式。他們被雙斜線分隔:

/^abc$//[A-Za-z0-9] /

方法 test():測試是否匹配(Method test(): is there a match?)

> /^a b $/.test('aaab')true> /^a b $/.test('aaa')false

方法 exec():匹配和捕獲組(Method exec(): match and capture groups)

> /a(b )a/.exec('_abbba_aba_')[ 'abbba', 'bbb' ]

返回的數組第一項(索引為0)是完整匹配,捕獲的第一個分組在第二項(索引為1),等。有一種方法可以反復調用獲取所有匹配。

方法 replace():搜索并替換(Method replace(): search and replace)

> '<a> <bbb>'.replace(/<(.*?)>/g, '[$1]')'[a] [bbb]'

replace的第一個參數必須是正則表達式,并且開啟全局搜索(/g 標記),否則僅第一個匹配項會被替換。有一種方法使用一個函數來計算替換項。

深入閱讀


本站僅提供存儲服務,所有內容均由用戶發布,如發現有害或侵權內容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
各大互聯網公司2014前端筆試面試題–JavaScript篇 – 碼農網
你可能不知道的19個JavaScript編碼技巧
每個前端開發者必會的 20 個 JavaScript 面試題 – 碼農網
<貼板> Javascript 常見陷阱及特殊用法
JavaScript 新手的踩坑日記
JavaScript分享ES6相關知識
更多類似文章 >>
生活服務
分享 收藏 導長圖 關注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點擊這里聯系客服!

聯系客服

主站蜘蛛池模板: 民乐县| 东光县| 唐海县| 镇雄县| 繁昌县| 古浪县| 牙克石市| 偃师市| 玉山县| 山阳县| 泰顺县| 祁阳县| 云阳县| 泸西县| 独山县| 渭南市| 延安市| 清远市| 岑溪市| 海丰县| 九龙坡区| 广南县| 田林县| 北流市| 瑞昌市| 安阳市| 宁德市| 龙泉市| 扬州市| 葫芦岛市| 宁海县| 高尔夫| 仁布县| 中江县| 盱眙县| 容城县| 沙田区| 遂宁市| 敖汉旗| 大姚县| 巢湖市|