2011-12-13 64 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