2011-12-13 87 views
11

我正在查看Mozilla開發人員網站關於javascript關閉,他們有這個代碼示例。在JavaScript中返回函數,瞭解範圍和關閉

function makeAdder(x){ 
    return function (y) { 
     console.log(y + " this is y") 
     console.log(x + " this is x") 
     return x + y; 
     } 
} 
var add10 = makeAdder(10); 
console.log(add10(2)); // 12 

現在我明白X屬性被設置,但我沒有得到的是y的範圍如何受到影響。我知道它的返回函數,但我的大腦嘗試想象如何在沒有引用時設置y。有人可以解釋嗎?

回答

13

makeAdder返回一個函數,您可以通過該函數傳遞參數y。它是在調用時設置的,而不是在創建新函數時(在調用makeAdder時)設置的x

在這個例子的情況下,輸出相當於寫過:

function add10(y) { 
    return 10 + y; 
} 

console.log(add10(2)); // 12 

沒有新東西是怎麼回事。示例代碼主要試圖說明正在爲x創建封閉。

所以makeAdder,在這裏,被恰當地命名爲:當你通過10到它,它給你一個功能將增加10到一切你傳遞到該新功能

var add10 = makeAdder(10); 
var add20 = makeAdder(20); 

console.log(add10(1) + add20(1)); // 32 

當然,爲了添加目的,只需要一個接受兩個參數並添加它們的函數就可以了。但這不是一個補充的教訓,它是一個關閉的教訓。

真實的場景可能是這樣的:

var buttons = document.getElementsByClassName('myButton'); 
for(var i = 0; i < buttons.length; i++) { 
    buttons[i].onclick = function() { 
     alert('You clicked button ' + i); 
    }; 
} 

在上面的代碼,i將不得不通過整個迭代集的任何按鈕被點擊之前。因此,全部按鈕都會提示buttons.length是什麼。相反,你可以做到以下幾點:

var makeAlert = function(x) { 
    return function() { 
     alert('You clicked button ' + x); 
    }; 
}; 

for(var i = 0; i < buttons.length; i++) { 
    buttons[i].onclick = makeAlert(i); 
} 

這裏的區別是,i按鈕被點擊時不被使用(這將是整個迭代後),但它是用於在迭代,當i將 爲每個按鈕有不同的值。

而不是創建一個變量,makeAlert,你會經常看到這種類型的代碼被編寫爲一個匿名函數,立即調用。下面的代碼基本上等同於上面的代碼:

for(var i = 0; i < buttons.length; i++) { 
    buttons[i].onclick = (function(x) { 
     return function() { 
      alert('You clicked button ' + x); 
     }; 
    })(i); 
} 
+0

我明白了,我的大腦仍然在努力得到它,我只是想看看y是如何通過或設置的,因爲add10(3)在我的腦海裏沒有提及它,說你的設置X不是爲什麼?這似乎很混亂:( – mustafa

+1

不,在調用'addAdder(10)'時'x'設爲'10',在調用'add10(2)'中設置爲'2'。 –

+0

但是不應該makeAdder(10,2)做同樣的事情,或者我們在做的事情本質上是makeAdAd(10)。(2)? 非常感謝你們:) – mustafa

1

函數makeAdder在被調用時會返回函數。 makeAdder返回的這個函數接受一個參數;這被稱爲y

變量y僅在調用由makeAdder返回的函數時才存在。它在每次被調用時被創建,並且在函數返回時被銷燬。

另一方面,變量x是在調用makeAdder時創建的,並且由於函數makeAdder返回而產生的閉包持續存在。當不再有對返回的函數的引用存在時它將被銷燬。

1

所以add10 = makeAdder(10);真的返回該功能:

function(y) { 
    console.log(y + " this is y") 
    console.log("10" + " this is x") 
    return 10 + y; 
} 

然後add10(2)被調用該函數,有2處處取代Y:

console.log("2" + " this is y") 
    console.log("10" + " this is x") 
    return 10 + 2; 
+0

好吧,但你怎麼知道你的目標?對不起,聽起來像一個新手,只是試圖看看你會如何知道你正在訪問正確的參數,我認爲我的問題是少於返回功能,而不是瓦爾等。 – mustafa

+0

嗯,首先想到用10替換X無處不在,並分配該功能add10。當你第一次調用makeAdded時,你的目標是第一個函數本身,第二次是針對'第二個'函數,一個是y(第一個嵌套,但現在分配給一個變種,然後調用beeing)。您可以在Firebug中使用console.log(add10)在完成作業後查看您自己。 – alessioalex

+0

所以它有點像寫作makeAdder(10)。(2)? 非常感謝你們:) – mustafa

2

函數可以看作包含可執行代碼和屬性的特殊對象。每個函數都有一個特殊的[scope]屬性,它表示定義它時的環境。如果一個函數是從另一個函數返回的,那麼這個對舊環境的引用就會被一個「閉包」中的新函數關閉。

所以當你打電話var add10 = makeAdder(10)什麼情況是,返回的函數的x有這勢必給它的範圍值10,呼叫console.log(add10(2))打印12

考慮閱讀this文章,瞭解什麼是閉包。關閉的更詳細的解釋可以發現here

+0

感謝您的鏈接夥伴 – mustafa

5

什麼你問的是,做東西給你一個函數:

function giveMeAFunctionThatBeeps(){ 
    return function() { 
     alert('Beep!'); 
     } 
} 

var beeper = giveMeAFunctionThatBeeps(); 

beeper(); // beeps! 

實際giveMeAFunctionThatbeeps只是一個工廠,爲您提供了一個功能,你想要做什麼。

在他們所提供的例子,你在做同樣的事情,蜂鳴器,但你也傳遞一個值:

function giveMeAFunctionThatBeepsANumber(x){ 
    return function() { 
     alert('Beep ' + x); 
     } 
} 

這將返回一個蜂鳴器(這是一個工廠記不清了),但蜂鳴器提醒x的值。

然而,這個值設置當您第一次創建蜂鳴器:

var beeper = giveMeAFunctionThatBeeps(5); 

beeper(); // beeps 5! 

蜂鳴器響鬧卡住值5,現在,我們不能做任何事情。

下一個例子是,如果你想創建一個蜂鳴任意數量的蜂鳴器:

function giveMeAFunctionThatBeepsANumber(){ 
    return function (x) { 
     alert('Beep ' + x); 
     } 
} 

var beeper = giveMeAFunctionThatBeeps(); 

beeper(6); // beeps 6! 
beeper(7); // beeps 7! 

正如現在我們要求廠方給我們,我們可以插入一個數字的功能。

然後最後,最初的例子,是上述兩種的組合:

function giveMeAFunctionThatBeepsANumber(x){ 
    return function (y) { 
     alert('Beep ' + (x + y)); 
     } 
} 

var beeper = giveMeAFunctionThatBeeps(2); 

當我們創建了蜂鳴器,我們正在傳遞2.記住如上,我們不能事後改變這個!它會一直蜂鳴2 ...

...但因爲它是一個工廠(具有值2預配置)返回一個函數,它接受一個參數,我們可以自定義它,當我們運行:

beeper(6); // beeps 8! because x was set when we created it, and y is what we pass in. 
+0

這是非常好的非常感謝你的時間 – mustafa

+0

不用擔心,我遇到了關閉的麻煩,我發現只讀噸和噸的不同解釋相同的事情幫助:) – NibblyPig