国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

《JavaScript 闖關(guān)記》之作用域和閉包

Jacendfeng / 1721人閱讀

摘要:作用域和閉包是最重要的概念之一,想要進(jìn)一步學(xué)習(xí),就必須理解作用域和閉包的工作原理。全局和局部作用域的關(guān)系在函數(shù)體內(nèi),局部變量的優(yōu)先級(jí)高于同名的全局變量。作用域鏈的用途,是保證對(duì)執(zhí)行環(huán)境有權(quán)訪問的所有變量和函數(shù)的有序訪問。

作用域和閉包是 JavaScript 最重要的概念之一,想要進(jìn)一步學(xué)習(xí) JavaScript,就必須理解 JavaScript 作用域和閉包的工作原理。

作用域

任何程序設(shè)計(jì)語(yǔ)言都有作用域的概念,簡(jiǎn)單的說,作用域就是變量與函數(shù)的可訪問范圍,即作用域控制著變量與函數(shù)的可見性和生命周期。在 JavaScript 中,變量的作用域有全局作用域和局部作用域兩種。

全局作用域(Global Scope)

在代碼中任何地方都能訪問到的對(duì)象擁有全局作用域,一般來說以下三種情形擁有全局作用域:

1.最外層函數(shù)和在最外層函數(shù)外面定義的變量擁有全局作用域,例如:

var global = "global";     // 顯式聲明一個(gè)全局變量
function checkscope() {
    var local = "local";   // 顯式聲明一個(gè)局部變量
    return global;         // 返回全局變量的值
}
console.log(global);       // "global"
console.log(checkscope()); // "global"
console.log(local);        // error: local is not defined.

上面代碼中,global 是全局變量,不管是在 checkscope() 函數(shù)內(nèi)部還是外部,都能訪問到全局變量 global

2.所有末定義直接賦值的變量自動(dòng)聲明為擁有全局作用域,例如:

function checkscope() {
    var local = "local"; // 顯式聲明一個(gè)局部變量
    global = "global";   // 隱式聲明一個(gè)全局變量(不好的寫法)
}
console.log(global);     // "global"
console.log(local);      // error: local is not defined.

上面代碼中,變量 global 未用 var 關(guān)鍵字定義就直接賦值,所以隱式的創(chuàng)建了全局變量 global,但這種寫法容易造成誤解,應(yīng)盡量避免這種寫法。

3.所有 window 對(duì)象的屬性擁有全局作用域

一般情況下,window 對(duì)象的內(nèi)置屬性都擁有全局作用域,例如 window.namewindow.locationwindow.top 等等。

局部作用域(Local Scope)

和全局作用域相反,局部作用域一般只在固定的代碼片段內(nèi)可訪問到。最常見的是在函數(shù)體內(nèi)定義的變量,只能在函數(shù)體內(nèi)使用。例如:

function checkscope() {
    var local = "local";   // 顯式聲明一個(gè)局部變量
    return local;         // 返回全局變量的值
}
console.log(checkscope()); // "local"
console.log(local);        // error: local is not defined.

上面代碼中,在函數(shù)體內(nèi)定義了變量 local,在函數(shù)體內(nèi)是可以訪問了,在函數(shù)外訪問就報(bào)錯(cuò)了。

全局和局部作用域的關(guān)系

在函數(shù)體內(nèi),局部變量的優(yōu)先級(jí)高于同名的全局變量。如果在函數(shù)內(nèi)聲明的一個(gè)局部變量或者函數(shù)參數(shù)中帶有的變量和全局變量重名,那么全局變量就被局部變量所遮蓋。

var scope = "global";      // 聲明一個(gè)全局變量
function checkscope() {
    var scope = "local";   // 聲明一個(gè)同名的局部變量
    return scope;          // 返回局部變量的值,而不是全局變量的值
}
console.log(checkscope()); // "local"

盡管在全局作用域編寫代碼時(shí)可以不寫 var 語(yǔ)句,但聲明局部變量時(shí)則必須使用 var 語(yǔ)句。思考一下如果不這樣做會(huì)怎樣:

scope = "global";           // 聲明一個(gè)全局變量,甚至不用 var 來聲明
function checkscope2() {
    scope = "local";        // 糟糕!我們剛修改了全局變量
    myscope = "local";      // 這里顯式地聲明了一個(gè)新的全局變量
    return [scope, myscope];// 返回兩個(gè)值
}
console.log(checkscope2()); // ["local", "local"],產(chǎn)生了副作用
console.log(scope);         // "local",全局變量修改了
console.log(myscope);       // "local",全局命名空間搞亂了

函數(shù)定義是可以嵌套的。由于每個(gè)函數(shù)都有它自己的作用域,因此會(huì)出現(xiàn)幾個(gè)局部作用域嵌套的情況,例如:

var scope = "global scope";         // 全局變量
function checkscope() {
    var scope = "local scope";      //局部變量 
    function nested() {
        var scope = "nested scope"; // 嵌套作用域內(nèi)的局部變量
        return scope;               // 返回當(dāng)前作用域內(nèi)的值
    }
    return nested();
}
console.log(checkscope());          // "nested scope"
函數(shù)作用域和聲明提前

在一些類似 C 語(yǔ)言的編程語(yǔ)言中,花括號(hào)內(nèi)的每一段代碼都具有各自的作用域,而且變量在聲明它們的代碼段之外是不可見的,我們稱為塊級(jí)作用域(block scope),而 JavaScript 中沒有塊級(jí)作用域。JavaScript 取而代之地使用了函數(shù)作用域(function scope),變量在聲明它們的函數(shù)體以及這個(gè)函數(shù)體嵌套的任意函數(shù)體內(nèi)都是有定義的。

在如下所示的代碼中,在不同位置定義了變量 ijk,它們都在同一個(gè)作用域內(nèi),這三個(gè)變量在函數(shù)體內(nèi)均是有定義的。

function test(o) {
    var i = 0; // i在整個(gè)函數(shù)體內(nèi)均是有定義的
    if (typeof o == "object") {
        var j = 0; // j在函數(shù)體內(nèi)是有定義的,不僅僅是在這個(gè)代碼段內(nèi)
        for (var k = 0; k < 10; k++) { // k在函數(shù)體內(nèi)是有定義的,不僅僅是在循環(huán)內(nèi)
            console.log(k); // 輸出數(shù)字0~9
        }
        console.log(k); // k已經(jīng)定義了,輸出10
    }
    console.log(j); // j已經(jīng)定義了,但可能沒有初始化
}

JavaScript 的函數(shù)作用域是指在函數(shù)內(nèi)聲明的所有變量在函數(shù)體內(nèi)始終是可見的。有意思的是,這意味著變量在聲明之前甚至已經(jīng)可用。JavaScript 的這個(gè)特性被非正式地稱為聲明提前(hoisting),即 JavaScript 函數(shù)里聲明的所有變量(但不涉及賦值)都被「提前」至函數(shù)體的頂部,看一下如下代碼:

var scope = "global";
function f() {
    console.log(scope);  // 輸出"undefined",而不是"global"
    var scope = "local"; // 變量在這里賦初始值,但變量本身在函數(shù)體內(nèi)任何地方均是有定義的
    console.log(scope);  // 輸出"local"
}

你可能會(huì)誤以為函數(shù)中的第一行會(huì)輸出 "global",因?yàn)榇a還沒有執(zhí)行到 var 語(yǔ)句聲明局部變量的地方。其實(shí)不然,由于函數(shù)作用域的特性,局部變量在整個(gè)函數(shù)體始終是有定義的,也就是說,在函數(shù)體內(nèi)局部變量遮蓋了同名全局變量。盡管如此,只有在程序執(zhí)行到 var 語(yǔ)句的時(shí)候,局部變量才會(huì)被真正賦值。因此,上述過程等價(jià)于:將函數(shù)內(nèi)的變量聲明“提前”至函數(shù)體頂部,同時(shí)變量初始化留在原來的位置:

function f() {
    var scope;          // 在函數(shù)頂部聲明了局部變量
    console.log(scope); // 變量存在,但其值是"undefined"
    scope = "local";    // 這里將其初始化并賦值
    console.log(scope); // 這里它具有了我們所期望的值
}

在具有塊級(jí)作用域的編程語(yǔ)言中,在狹小的作用域里讓變量聲明和使用變量的代碼盡可能靠近彼此,通常來講,這是一個(gè)非常不錯(cuò)的編程習(xí)慣。由于 JavaScript 沒有塊級(jí)作用域,因此一些程序員特意將變量聲明放在函數(shù)體頂部,而不是將聲明靠近放在使用變量之處。這種做法使得他們的源代碼非常清晰地反映了真實(shí)的變量作用域。

作用域鏈

當(dāng)代碼在一個(gè)環(huán)境中執(zhí)行時(shí),會(huì)創(chuàng)建變量對(duì)象的一個(gè)作用域鏈(scope chain)。作用域鏈的用途,是保證對(duì)執(zhí)行環(huán)境有權(quán)訪問的所有變量和函數(shù)的有序訪問。作用域鏈的前端,始終都是當(dāng)前執(zhí)行的代碼所在環(huán)境的變量對(duì)象。如果這個(gè)環(huán)境是函數(shù),則將其活動(dòng)對(duì)象(activation object)作為變量對(duì)象。活動(dòng)對(duì)象在最開始時(shí)只包含一個(gè)變量,即 arguments 對(duì)象(這個(gè)對(duì)象在全局環(huán)境中是不存在的)。作用域鏈中的下一個(gè)變量對(duì)象來自包含(外部)環(huán)境,而再下一個(gè)變量對(duì)象則來自下一個(gè)包含環(huán)境。這樣,一直延續(xù)到全局執(zhí)行環(huán)境;全局執(zhí)行環(huán)境的變量對(duì)象始終都是作用域鏈中的最后一個(gè)對(duì)象。

標(biāo)識(shí)符解析是沿著作用域鏈一級(jí)一級(jí)地搜索標(biāo)識(shí)符的過程。搜索過程始終從作用域鏈的前端開始,然后逐級(jí)地向后回溯,直至找到標(biāo)識(shí)符為止(如果找不到標(biāo)識(shí)符,通常會(huì)導(dǎo)致錯(cuò)誤發(fā)生)。

請(qǐng)看下面的示例代碼:

var color = "blue";

function changeColor(){
    if (color === "blue"){
        color = "red";
    } else {
        color = "blue";
    }
}

console.log(changeColor());

在這個(gè)簡(jiǎn)單的例子中,函數(shù) changeColor() 的作用域鏈包含兩個(gè)對(duì)象:它自己的變量對(duì)象(其中定義著 arguments 對(duì)象)和全局環(huán)境的變量對(duì)象。可以在函數(shù)內(nèi)部訪問變量 color,就是因?yàn)榭梢栽谶@個(gè)作用域鏈中找到它。

此外,在局部作用域中定義的變量可以在局部環(huán)境中與全局變量互換使用,如下面這個(gè)例子所示:

var color = "blue";

function changeColor(){
    var anotherColor = "red";

    function swapColors(){
        var tempColor = anotherColor;
        anotherColor = color;
        color = tempColor;

        // 這里可以訪問color、anotherColor和tempColor
    }

    // 這里可以訪問color和anotherColor,但不能訪問tempColor
    swapColors();
}

// 這里只能訪問color
changeColor();

以上代碼共涉及3個(gè)執(zhí)行環(huán)境:全局環(huán)境、changeColor() 的局部環(huán)境和 swapColors() 的局部環(huán)境。全局環(huán)境中有一個(gè)變量 color 和一個(gè)函數(shù) changeColor()changeColor() 的局部環(huán)境中有一個(gè)名為 anotherColor 的變量和一個(gè)名為 swapColors() 的函數(shù),但它也可以訪問全局環(huán)境中的變量 colorswapColors() 的局部環(huán)境中有一個(gè)變量 tempColor,該變量只能在這個(gè)環(huán)境中訪問到。無論全局環(huán)境還是 changeColor() 的局部環(huán)境都無權(quán)訪問 tempColor。然而,在 swapColors() 內(nèi)部則可以訪問其他兩個(gè)環(huán)境中的所有變量,因?yàn)槟莾蓚€(gè)環(huán)境是它的父執(zhí)行環(huán)境。下圖形象地展示了前面這個(gè)例子的作用域鏈。

上圖中的矩形表示特定的執(zhí)行環(huán)境。其中,內(nèi)部環(huán)境可以通過作用域鏈訪問所有的外部環(huán)境,但外部環(huán)境不能訪問內(nèi)部環(huán)境中的任何變量和函數(shù)。這些環(huán)境之間的聯(lián)系是線性、有次序的。每個(gè)環(huán)境都可以向上搜索作用域鏈,以查詢變量和函數(shù)名;但任何環(huán)境都不能通過向下搜索作用域鏈而進(jìn)入另一個(gè)執(zhí)行環(huán)境。對(duì)于這個(gè)例子中的 swapColors() 而言,其作用域鏈中包含3個(gè)對(duì)象:swapColors() 的變量對(duì)象、changeColor() 的變量對(duì)象和全局變量對(duì)象。swapColors() 的局部環(huán)境開始時(shí)會(huì)先在自己的變量對(duì)象中搜索變量和函數(shù)名,如果搜索不到則再搜索上一級(jí)作用域鏈。changeColor() 的作用域鏈中只包含兩個(gè)對(duì)象:它自己的變量對(duì)象和全局變量對(duì)象。這也就是說,它不能訪問 swapColors() 的環(huán)境。函數(shù)參數(shù)也被當(dāng)作變量來對(duì)待,因此其訪問規(guī)則與執(zhí)行環(huán)境中的其他變量相同。

閉包

MDN 對(duì)閉包的定義:

閉包是指那些能夠訪問獨(dú)立(自由)變量的函數(shù)(變量在本地使用,但定義在一個(gè)封閉的作用域中)。換句話說,這些函數(shù)可以「記憶」它被創(chuàng)建時(shí)候的環(huán)境。

《JavaScript 權(quán)威指南(第6版)》對(duì)閉包的定義:

函數(shù)對(duì)象可以通過作用域鏈相互關(guān)聯(lián)起來,函數(shù)體內(nèi)部的變量都可以保存在函數(shù)作用域內(nèi),這種特性在計(jì)算機(jī)科學(xué)文獻(xiàn)中稱為閉包。

《JavaScript 高級(jí)程序設(shè)計(jì)(第3版)》對(duì)閉包的定義:

閉包是指有權(quán)訪問另一個(gè)函數(shù)作用域中的變量的函數(shù)。

上面這些定義都比較晦澀難懂,阮一峰的解釋稍微好理解一些:

由于在 Javascript 語(yǔ)言中,只有函數(shù)內(nèi)部的子函數(shù)才能讀取局部變量,因此可以把閉包簡(jiǎn)單理解成定義在一個(gè)函數(shù)內(nèi)部的函數(shù)。

閉包的用途

閉包可以用在許多地方。它的最大用處有兩個(gè),一個(gè)是可以讀取函數(shù)內(nèi)部的變量(作用域鏈),另一個(gè)就是讓這些變量的值始終保持在內(nèi)存中。怎么來理解這句話呢?請(qǐng)看下面的代碼。

function fun() {   
    var n = 1;

    add = function() {
        n += 1
    }

    function fun2(){
        console.log(n);
    }

    return fun2;
}

var result = fun();  
result(); // 1
add();
result(); // 2

在這段代碼中,result 實(shí)際上就是函數(shù) fun2。它一共運(yùn)行了兩次,第一次的值是 1,第二次的值是 2。這證明了,函數(shù) fun 中的局部變量 n 一直保存在內(nèi)存中,并沒有在 fun 調(diào)用后被自動(dòng)清除。

為什么會(huì)這樣呢?原因就在于 funfun2 的父函數(shù),而 fun2 被賦給了一個(gè)全局變量,這導(dǎo)致 fun2 始終在內(nèi)存中,而 fun2 的存在依賴于 fun,因此 fun 也始終在內(nèi)存中,不會(huì)在調(diào)用結(jié)束后,被垃圾回收機(jī)制(garbage collection)回收。

這段代碼中另一個(gè)值得注意的地方,就是 add = function() { n += 1 } 這一行。首先,變量 add 前面沒有使用 var 關(guān)鍵字,因此 add 是一個(gè)全局變量,而不是局部變量。其次,add 的值是一個(gè)匿名函數(shù)(anonymous function),而這個(gè)匿名函數(shù)本身也是一個(gè)閉包,和 fun2 處于同一作用域,所以 add 相當(dāng)于是一個(gè) setter,可以在函數(shù)外部對(duì)函數(shù)內(nèi)部的局部變量進(jìn)行操作。

計(jì)數(shù)器的困境

我們?cè)賮砜匆粋€(gè)經(jīng)典例子「計(jì)數(shù)器的困境」,假設(shè)你想統(tǒng)計(jì)一些數(shù)值,且該計(jì)數(shù)器在所有函數(shù)中都是可用的。你可以定義一個(gè)全局變量 counter 當(dāng)做計(jì)數(shù)器,再定義一個(gè) add() 函數(shù)來設(shè)置計(jì)數(shù)器遞增。代碼如下:

var counter = 0;
function add() {
    return counter += 1;
}

console.log(add());
console.log(add());
console.log(add());
// 計(jì)數(shù)器現(xiàn)在為 3

計(jì)數(shù)器數(shù)值在執(zhí)行 add() 函數(shù)時(shí)發(fā)生變化。但問題來了,頁(yè)面上的任何腳本都能改變計(jì)數(shù)器 counter,即便沒有調(diào)用 add() 函數(shù)。如果我們將計(jì)數(shù)器 counter 定義在 add() 函數(shù)內(nèi)部,就不會(huì)被外部腳本隨意修改到計(jì)數(shù)器的值了。代碼如下:

function add() {
    var counter = 0;
    return counter += 1;
}

console.log(add());
console.log(add());
console.log(add());
// 本意是想輸出 3, 但事與愿違,輸出的都是 1 

因?yàn)槊看握{(diào)用 add() 函數(shù),計(jì)數(shù)器都會(huì)被重置為 0,輸出的都是 1,這并不是我們想要的結(jié)果。閉包正好可以解決這個(gè)問題,我們?cè)?add() 函數(shù)內(nèi)部,再定義一個(gè) plus() 內(nèi)嵌函數(shù)(閉包),內(nèi)嵌函數(shù) plus() 可以訪問父函數(shù)的 counter 變量。代碼如下:

function add() {
    var counter = 0;
    var plus = function() {counter += 1;}
    plus();
    return counter; 
}

接下來,只要我們能在外部訪問 plus() 函數(shù),并且確保 counter = 0 只執(zhí)行一次,就能解決計(jì)數(shù)器的困境。代碼如下:

var add = function() {
    var counter = 0;
    var plus = function() {return counter += 1;}
    return plus;
}

var puls2 = add();
console.log(puls2());
console.log(puls2());
console.log(puls2());
// 計(jì)數(shù)器為 3

計(jì)數(shù)器 counteradd() 函數(shù)的作用域保護(hù),只能通過 puls2 方法修改。

使用閉包的注意點(diǎn)

由于閉包會(huì)使得函數(shù)中的變量都被保存在內(nèi)存中,內(nèi)存消耗很大,所以不能濫用閉包,否則會(huì)造成網(wǎng)頁(yè)的性能問題,在 IE 中可能導(dǎo)致內(nèi)存泄露。解決方法是,在退出函數(shù)之前,將不使用的局部變量全部刪除或設(shè)置為 null,斷開變量和內(nèi)存的聯(lián)系。

閉包會(huì)在父函數(shù)外部,改變父函數(shù)內(nèi)部變量的值。所以,如果你把父函數(shù)當(dāng)作對(duì)象(object)使用,把閉包當(dāng)作它的公用方法(public method),把內(nèi)部變量當(dāng)作它的私有屬性(private value),這時(shí)一定要小心,不要隨便改變父函數(shù)內(nèi)部變量的值。

JavaScript 閉包是一種強(qiáng)大的語(yǔ)言特性。通過使用這個(gè)語(yǔ)言特性來隱藏變量,可以避免覆蓋其他地方使用的同名變量,理解閉包有助于編寫出更有效也更簡(jiǎn)潔的代碼。

this 關(guān)鍵字

談到作用域和閉包就不得不說 this 關(guān)鍵字,雖然它們之間關(guān)聯(lián)不大,但是它們一起使用卻容易讓人產(chǎn)生疑惑。下面列出了使用 this 的大部分場(chǎng)景,帶大家一探究竟。

this 是 JavaScript 的關(guān)鍵字,指函數(shù)執(zhí)行時(shí)的上下文,跟函數(shù)定義時(shí)的上下文無關(guān)。隨著函數(shù)使用場(chǎng)合的不同,this 的值會(huì)發(fā)生變化。但是有一個(gè)總的原則,那就是 this 指代的是調(diào)用函數(shù)的那個(gè)對(duì)象。

全局上下文

在全局上下文中,也就是在任何函數(shù)體外部,this 指代全局對(duì)象。

// 在瀏覽器中,this 指代全局對(duì)象 window
console.log(this === window);  // true
函數(shù)上下文

在函數(shù)上下文中,也就是在任何函數(shù)體內(nèi)部,this 指代調(diào)用函數(shù)的那個(gè)對(duì)象。

函數(shù)調(diào)用中的 this
function f1(){
    return this;
}

console.log(f1() === window); // true

如上代碼所示,直接定義一個(gè)函數(shù) f1(),相當(dāng)于為 window 對(duì)象定義了一個(gè)屬性。直接執(zhí)行函數(shù) f1(),相當(dāng)于執(zhí)行 window.f1()。所以函數(shù) f1() 中的 this 指代調(diào)用函數(shù)的那個(gè)對(duì)象,也就是 window 對(duì)象。

function f2(){
    "use strict"; // 這里是嚴(yán)格模式
    return this;
}

console.log(f2() === undefined); // true

如上代碼所示,在「嚴(yán)格模式」下,禁止 this 關(guān)鍵字指向全局對(duì)象(在瀏覽器環(huán)境中也就是 window 對(duì)象),this 的值將維持 undefined 狀態(tài)。

對(duì)象方法中的 this
var o = {
    name: "stone",
    f: function() {
        return this.name;
    }
};

console.log(o.f()); // "stone"

如上代碼所示,對(duì)象 o 中包含一個(gè)屬性 name 和一個(gè)方法 f()。當(dāng)我們執(zhí)行 o.f() 時(shí),方法 f() 中的 this 指代調(diào)用函數(shù)的那個(gè)對(duì)象,也就是對(duì)象 o,所以 this.name 也就是 o.name

注意,在何處定義函數(shù)完全不會(huì)影響到 this 的行為,我們也可以首先定義函數(shù),然后再將其附屬到 o.f。這樣做 this 的行為也一致。如下代碼所示:

var fun = function() {
    return this.name;
};

var o = { name: "stone" };
o.f = fun;

console.log(o.f()); // "stone"

類似的,this 的綁定只受最靠近的成員引用的影響。在下面的這個(gè)例子中,我們把一個(gè)方法 g() 當(dāng)作對(duì)象 o.b 的函數(shù)調(diào)用。在這次執(zhí)行期間,函數(shù)中的 this 將指向 o.b。事實(shí)上,這與對(duì)象本身的成員沒有多大關(guān)系,最靠近的引用才是最重要的。

o.b = {
    name: "sophie"
    g: fun,
};

console.log(o.b.g()); // "sophie"
eval() 方法中的 this

eval() 方法可以將字符串轉(zhuǎn)換為 JavaScript 代碼,使用 eval() 方法時(shí),this 指向哪里呢?答案很簡(jiǎn)單,看誰在調(diào)用 eval() 方法,調(diào)用者的執(zhí)行環(huán)境中的 this 就被 eval() 方法繼承下來了。如下代碼所示:

// 全局上下文
function f1(){
    return eval("this");
}
console.log(f1() === window); // true

// 函數(shù)上下文
var o = {
    name: "stone",
    f: function() {
        return eval("this.name");
    }
};
console.log(o.f()); // "stone"
call()apply() 方法中的 this

call()apply() 是函數(shù)對(duì)象的方法,它的作用是改變函數(shù)的調(diào)用對(duì)象,它的第一個(gè)參數(shù)就表示改變后的調(diào)用這個(gè)函數(shù)的對(duì)象。因此,this 指代的就是這兩個(gè)方法的第一個(gè)參數(shù)。

var x = 0;  
function f() {    
    console.log(this.x);  
}  
var o = {};  
o.x = 1;
o.m = f;  
o.m.apply(); // 0

call()apply() 的參數(shù)為空時(shí),默認(rèn)調(diào)用全局對(duì)象。因此,這時(shí)的運(yùn)行結(jié)果為 0,證明 this 指的是全局對(duì)象。如果把最后一行代碼修改為:

o.m.apply(o); // 1

運(yùn)行結(jié)果就變成了 1,證明了這時(shí) this 指代的是對(duì)象 o

bind() 方法中的 this

ECMAScript 5 引入了 Function.prototype.bind。調(diào)用 f.bind(someObject) 會(huì)創(chuàng)建一個(gè)與 f 具有相同函數(shù)體和作用域的函數(shù),但是在這個(gè)新函數(shù)中,this 將永久地被綁定到了 bind 的第一個(gè)參數(shù),無論這個(gè)函數(shù)是如何被調(diào)用的。如下代碼所示:

function f() {
    return this.a;
}

var g = f.bind({
    a: "stone"
});
console.log(g()); // stone

var o = {
    a: 28,
    f: f,
    g: g
};
console.log(o.f(), o.g()); // 28, stone
DOM 事件處理函數(shù)中的 this

一般來講,當(dāng)函數(shù)使用 addEventListener,被用作事件處理函數(shù)時(shí),它的 this 指向觸發(fā)事件的元素。如下代碼所示:




    
    test


    
    

但在 IE 瀏覽器中,當(dāng)函數(shù)使用 attachEvent ,被用作事件處理函數(shù)時(shí),它的 this 卻指向 window。如下代碼所示:




    
    test


    
    

內(nèi)聯(lián)事件處理函數(shù)中的 this

當(dāng)代碼被內(nèi)聯(lián)處理函數(shù)調(diào)用時(shí),它的 this 指向監(jiān)聽器所在的 DOM 元素。如下代碼所示:

上面的 alert 會(huì)顯示 button,注意只有外層代碼中的 this 是這樣設(shè)置的。如果 this 被包含在匿名函數(shù)中,則又是另外一種情況了。如下代碼所示:

在這種情況下,this 被包含在匿名函數(shù)中,相當(dāng)于處于全局上下文中,所以它指向 window 對(duì)象。

關(guān)卡

仔細(xì)想想,下面代碼塊會(huì)輸出什么結(jié)果呢?

// 挑戰(zhàn)一
function func1() {
    function func2() {
        console.log(this)
    }
    return func2;
}
func1()();  // ???
// 挑戰(zhàn)二
scope = "stone";

function Func() {
    var scope = "sophie";

    function inner() {
        console.log(scope);
    }
    return inner;
}

var ret = Func();
ret();    // ???
// 挑戰(zhàn)三
scope = "stone";

function Func() {
    var scope = "sophie";

    function inner() {
        console.log(scope);
    }
    scope = "tommy";
    return inner;
}

var ret = Func();
ret();    // ???
// 挑戰(zhàn)四
scope = "stone";

function Bar() {
    console.log(scope);
}

function Func() {
    var scope = "sophie";
    return Bar;
}

var ret = Func();
ret();    // ???
// 挑戰(zhàn)五
var name = "The Window";  
var object = {    
    name: "My Object",
    getNameFunc: function() {      
        return function() {        
            return this.name;      
        };    
    }  
};  
console.log(object.getNameFunc()());    // ???
// 挑戰(zhàn)六
var name = "The Window";  
var object = {    
    name: "My Object",
    getNameFunc: function() {      
        var that = this;      
        return function() {        
            return that.name;      
        };    
    }  
};  
console.log(object.getNameFunc()());    // ???
更多

關(guān)注微信公眾號(hào)「劼哥舍」回復(fù)「答案」,獲取關(guān)卡詳解。
關(guān)注 https://github.com/stone0090/javascript-lessons,獲取最新動(dòng)態(tài)。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/91340.html

相關(guān)文章

  • JavaScript 闖關(guān)

    摘要:對(duì)象數(shù)組初始化表達(dá)式,闖關(guān)記之上文檔對(duì)象模型是針對(duì)和文檔的一個(gè)。闖關(guān)記之?dāng)?shù)組數(shù)組是值的有序集合。數(shù)組是動(dòng)態(tài)的,根闖關(guān)記之語(yǔ)法的語(yǔ)法大量借鑒了及其他類語(yǔ)言如和的語(yǔ)法。 《JavaScript 闖關(guān)記》之 DOM(下) Element 類型 除了 Document 類型之外,Element 類型就要算是 Web 編程中最常用的類型了。Element 類型用于表現(xiàn) XML 或 HTML 元素...

    mj 評(píng)論0 收藏0
  • JavaScript 闖關(guān)

    摘要:本課程之所以叫做闖關(guān)記,是因?yàn)椴糠终鹿?jié)精心設(shè)計(jì)了挑戰(zhàn)關(guān)卡,通過提供更多的實(shí)戰(zhàn)機(jī)會(huì),讓大家可以循序漸進(jìn)地有目的地有挑戰(zhàn)地開展學(xué)習(xí)。課程結(jié)構(gòu)及目錄以下目錄只是初步構(gòu)想,課程結(jié)構(gòu)及內(nèi)容會(huì)根據(jù)實(shí)際情況隨時(shí)進(jìn)行調(diào)整。 為何寫作此課程 stone 主要負(fù)責(zé)基于 Web 的企業(yè)內(nèi)部管理系統(tǒng)的開發(fā),雖然能夠熟練地使用 JavaScript,但隨著對(duì) JavaScript 的理解越來越深,才發(fā)現(xiàn)自己尚...

    curried 評(píng)論0 收藏0
  • 闖關(guān)模式】作用域、鏈和閉包

    摘要:前言上篇闖關(guān)模式作用域鏈和閉包上任務(wù)三全局作用域和變量遮蔽全局作用域理解作用域鏈在哪結(jié)束很重要所有的運(yùn)行時(shí)環(huán)境都必須隱式創(chuàng)建一個(gè)全局作用域?qū)ο鬄g覽器中是,中是,這個(gè)對(duì)象就位于作用域鏈的頂端在任務(wù)一中,我們忽略了一個(gè)細(xì)節(jié),即當(dāng)不使用或者等定義 前言 上篇:【闖關(guān)模式】作用域、鏈和閉包 上 任務(wù)三 Global Scope & Shadowing 全局作用域和變量遮蔽 全局作用域(Glob...

    airborne007 評(píng)論0 收藏0
  • JavaScript 闖關(guān)垃圾回收和內(nèi)存管理

    摘要:內(nèi)存回收此時(shí),局部變量就沒有存在的必要了,因此可以釋放它們的內(nèi)存以供將來使用。局部變量會(huì)在它們離開執(zhí)行環(huán)境時(shí)自動(dòng)被解除引用,如下面這個(gè)例子所示手工解除的引用由于局部變量在函數(shù)執(zhí)行完畢后就離開了其執(zhí)行環(huán)境,因此無需我們顯式地去為它解除引用。 JavaScript 具有自動(dòng)垃圾收集機(jī)制(GC:Garbage Collecation),也就是說,執(zhí)行環(huán)境會(huì)負(fù)責(zé)管理代碼執(zhí)行過程中使用的內(nèi)存。而...

    Sleepy 評(píng)論0 收藏0
  • 闖關(guān)模式】作用域、鏈和閉包

    摘要:前言這個(gè)系列是翻譯自中的直接闖關(guān)作用域鏈和閉包作用域,作用域鏈,閉包和垃圾回收機(jī)制都有一個(gè)共同點(diǎn)學(xué)了就忘閉包到底是干啥的啥時(shí)候發(fā)生垃圾回收機(jī)制作用域鏈到底是啥這個(gè)教程讓你發(fā)現(xiàn)這些都是小意思。 前言 這個(gè)系列是翻譯自 nodeschool.io中的 scope-chains-closures 直接闖關(guān): npm install -g scope-chains-closures scope...

    shinezejian 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<