这个'run once' Javascript函数是如何工作的?



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

这里的fn1fn2来自两种不同的once调用,因此它们在闭包中将具有不同的done绑定。

fn1() ; prints "test"
fn1() ; does nothing
fn2() ; prints "test"
fn2() ; does nothing

但我的理解是,当每次调用doPayment()时,完成的变量每次都会被初始化为false

let done = falseonce的主体中,所以它在调用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(倒数第二条语句)时,将针对计算结果为falsedone进行检查。表达式((done = true), fn.apply(this, arguments))执行。这会将done设置为true并执行fn.apply

现在,当再次调用doPayment(最后一条语句)时,内部函数

done ? undefined : ((done = true), fn.apply(this, arguments));

被执行。这个内部函数已经有一个闭包,done上一次执行时已经将其设置为true。所以它返回undefined.

运行上述代码时,将运行以下函数:

  1. once(运行后,done为假)
  2. done ? undefined : ((done = true), fn.apply(this, arguments));(运行后,done为真)
  3. done ? undefined : ((done = true), fn.apply(this, arguments));(运行后done保持true

最新更新