javascript中的返回函数,理解作用域&闭包

Returning functions in javascript, understanding scope & closures

本文关键字:作用域 闭包 返回 函数 javascript      更新时间:2023-09-26

我在Mozillas的开发者网站上看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时陷入了混乱。有人能解释一下吗?

makeAdder返回一个函数,您可以向该函数传递y参数。它是在调用时设置的,而x是在创建新函数时设置的(在调用makeAdder时)。

对于这个例子,输出相当于这样写:

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

你要求的是一个为你做某事的函数:

  function giveMeAFunctionThatBeeps(){
    return function () {
         alert('Beep!');
        }
}
var beeper = giveMeAFunctionThatBeeps();
beeper(); // beeps!

实际的givemefunctionthatbeeps只是一个工厂,它给你一个函数,做你想做的事。

在他们提供的例子中,你正在做与寻呼机相同的事情,但你也传递了一个值:

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

创建beeper时,传递的是2。记住如上所述,我们不能在之后更改它!它会一直发出哔哔声…

…但是因为它是一个工厂(预配置值为2),返回一个接受参数的函数,所以我们可以在运行它时自定义它:

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

函数可以看作是包含可执行代码和属性的特殊对象。每个函数都有一个特殊的[scope]属性,表示它被定义时所处的环境。如果一个函数从另一个函数返回,那么这个对旧环境的引用将被"闭包"中的新函数关闭。

所以当你调用var add10 = makeAdder(10)时,所发生的是返回函数的x具有绑定到它的作用域的值10,并且调用console.log(add10(2))打印12

考虑阅读这篇文章,了解什么是闭包。关于闭包的更详细的解释可以在这里找到

函数makeAdder在调用时返回一个函数。makeAdder返回的函数接受一个参数;即y .

变量y仅在调用makeAdder返回的函数时存在。它在每次调用时创建,并在函数返回时销毁。

另一方面,变量x是在makeAdder被调用时创建的,并且由于makeAdder函数返回的闭包而持续存在。当不再存在对返回函数的引用时,该函数将被销毁。

所以add10 = makeAdder(10);实际上返回了这个函数:

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

然后add10(2)调用该函数,将y替换为2:

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