这个'运行一次'的Javascript函数如何工作?

时间:2017-04-09 11:47:22

标签: javascript functional-programming

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

---------更新--------------------

看到像这样一个中等复杂的问题被要求关闭是如此令人沮丧,因为它与其他问题重复。

在所有答案中,@西尔维斯特的答案被选为正确的答案。我也给出了自己的解释作为答案。

4 个答案:

答案 0 :(得分:2)

  

但我的理解是,每次调用doPayment()时,每次都会使用false初始化变量

#Creating sampleobject $o = "Update Result", " Message: The update completed successfully, but the system needs to be rebooted for the changes to be effective.", " Reboot Required: true", " VIBs Installed: VMware_bootbank....................." $upgradecomm = New-Object psobject -Property @{ Host = "Server1" Output = $o } #Verify that sampleobject is correct $upgradecomm | Format-List * #Output Host : Server1 Output : {Update Result, Message: The update completed successfully, but the system needs to be rebooted for the changes to be effe ctive., Reboot Required: true, VIBs Installed: VMware_bootbank.....................} #Check if successfull and rebooted are in the message-string in output if($upgradecomm.Output -match 'Message: .*?successfull.*?rebooted') { "Do something" } #Output Do something 位于let done = false的正文中,因此在调用once时执行。 once的正文仅为doPayment,因此当您致电return done ? undefined : ((done = true), fn.apply(this, arguments));时,这是唯一执行的内容。

答案 1 :(得分:2)

当您致电once时,本地变量done初始化为false,并返回功能。该函数是绑定到doPayment的函数,因此每次调用都会检查并可能改变在创建该函数的done调用中创建的once

如果你要做两个:

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

答案 2 :(得分:1)

你的一次函数由两个函数组成,第一个函数返回第二个函数。

当您最初使用应该只运行一次的回调函数调用它时,将执行第一个函数。发生这种情况时,您将done值设置为false并返回第二个函数。

现在当你调用原始函数时,你不再直接执行它,而是调用从一次返回的函数(第二个函数)。

您正在寻找分配给done的变量。范围中没有名称的变量,因此解释器将查找父范围,这是第一个可以找到它可以在第二个函数中使用的变量的函数。

return done ? undefined : ((done = true), fn.apply(null, arguments)) 

上面的说法是,如果done变量 truthy 返回undefined,则将done变量设置为true,然后使用给定的函数调用trim函数背景和论点。

了解更多闭包词汇范围

答案 3 :(得分:-1)

There is a few JavaScript language features in this example. They are (1) closure (2) comma operator (3) high order functions.

The method say is for just display purpose only, so don't have to considered as part of the problem. once is a constant function when executed will return another function. The doPayment is the high order function.

The ((done = true), fn.apply(this, arguments)); uses a comma separated operator. The first expression done = true is executed first and then the second fn.appy is executed next and return the results from the second function.

When the JavaScript execution engine runs the first doPayment(), it runs the following:

var doPayment = once(() => {
  say("Payment Done");
});

It runs the once method. At that time done = false is executed. Then the inner function is returned and that is assigned to the variable doPayment. The inner function is:

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

Since the above is inside the once function, it has created a closure with the variable done.

Now when the doPayment (second last statement) is run , so a check is done against done which evaluates to false. The expression ((done = true), fn.apply(this, arguments)) gets executed. This will set the done to true and executes fn.apply.

Now when the doPayment is called again (the last statement), the inner function

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

gets executed. This inner function has already a closure which has done set to true already by the previous execution. So it returns undefined.

When the above code is run, the following functions are run:

  1. once (after the run, done is false)
  2. done ? undefined : ((done = true), fn.apply(this, arguments)); (after the run, done is true)
  3. done ? undefined : ((done = true), fn.apply(this, arguments)); (after the run done stays as true