我从 Functional Programming with JavaScript Using EcmaScript 6 一书中获取了这段代码。
这就是代码的工作方式。多次调用doPayment()
不会执行输入箭头函数,() => { say("Payment Done") }
因为内部变量done
在第一次运行中设置为true
。
但我的理解是,每次调用doPayment()
时,变量done
每次都会用false
初始化,所以内部箭头函数每次都会运行。
它是如何工作的?
function say(v)
{
document.querySelector('#out').innerHTML += v + "</br>";
}
const once = fn => {
let done = false;
return function() {
return done ? undefined : ((done = true), fn.apply(this, arguments));
}
}
var doPayment = once(() => {
say("Payment Done");
});
doPayment();
doPayment();
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<span id="out" style="font-family: roboto"></span>
</body>
</html>
---------更新--------------------
看到像这样一个中等复杂的问题是如何被要求关闭的,因为它是其他问题的重复,这真是令人沮丧。
在所有答案中,@Sylvester的答案被选为正确答案。我也给出了我自己的解释作为答案。
调用once
时,局部变量done
初始化为false
并返回函数。该函数是绑定到doPayment
函数的函数,因此每次调用都会检查并可能改变在创建该函数的once
调用中创建的done
。
如果你要做两个:
const fnPrint = console.log.bind(null, "test");
const fn1 = once(fnPrint);
const fn2 = once(fnPrint);
这里的fn1
和fn2
来自两种不同的once
调用,因此它们在闭包中将具有不同的done
绑定。
fn1() ; prints "test"
fn1() ; does nothing
fn2() ; prints "test"
fn2() ; does nothing
但我的理解是,当每次调用doPayment()时,完成的变量每次都会被初始化为false
。
行let done = false
在once
的主体中,所以它在调用once
时执行。doPayment
的主体只是return done ? undefined : ((done = true), fn.apply(this, arguments));
,所以这是你调用doPayment
时唯一执行的东西。
你的 once 函数由两个函数组成,第一个函数返回第二个函数。
第一个函数将在您最初使用只应运行一次的回调函数调用它时执行。发生这种情况时,您将done
值设置为false
并返回第二个函数。
现在,当您调用原始函数时,您不再直接执行它,而是调用从一次返回的函数(第二个函数)。
在这里,您正在寻找分配给done
的变量。 作用域中没有名称变量,因此解释器将查找父作用域,这是第一个函数,它将在其中找到可以在第二个函数中使用的变量。
return done ? undefined : ((done = true), fn.apply(null, arguments))
上面的语句所做的是,如果done
变量是TRUTHY返回undefined
否则,将 done 变量设置为 true,然后使用给定的上下文和参数调用修饰函数。
有关更多查找闭包和词法范围
这个例子中有一些JavaScript语言功能。它们是 (1) 闭包 (2) 逗号运算符 (3) 高阶函数。
say
的方法仅用于显示目的,因此不必将其视为问题的一部分。once
是一个常量函数,执行时将返回另一个函数。doPayment
是高阶函数。
((done = true), fn.apply(this, arguments));
使用逗号分隔的运算符。首先执行第一个表达式done = true
,然后执行第二个fn.appy
,并返回第二个函数的结果。
当JavaScript执行引擎运行第一个doPayment()
时,它会运行以下内容:
var doPayment = once(() => {
say("Payment Done");
});
它运行once
方法。此时done = false
被执行。然后返回内部函数,并将其分配给变量doPayment
。内部功能是:
done ? undefined : ((done = true), fn.apply(this, arguments));
由于上述内容在once
函数内部,因此它创建了一个带有变量done
的闭包。
现在,当运行doPayment
(倒数第二条语句)时,将针对计算结果为false
的done
进行检查。表达式((done = true), fn.apply(this, arguments))
执行。这会将done
设置为true
并执行fn.apply
。
现在,当再次调用doPayment
(最后一条语句)时,内部函数
done ? undefined : ((done = true), fn.apply(this, arguments));
被执行。这个内部函数已经有一个闭包,done
上一次执行时已经将其设置为true
。所以它返回undefined
.
运行上述代码时,将运行以下函数:
once
(运行后,done
为假)done ? undefined : ((done = true), fn.apply(this, arguments));
(运行后,done
为真)done ? undefined : ((done = true), fn.apply(this, arguments));
(运行后done
保持true