Javascript闭包和简单英语的副作用?(分别)

ale*_*nco 24 javascript closures side-effects

我一直在阅读一些JavaScript书籍,我总是听说关闭和副作用.出于某种原因,我无法理解他们到底是什么.任何人都可以用简单的英语和例子向我解释它们是什么吗?(正如你向具有图形设计师编程水平的人解释的那样).

Lar*_*ien 33

副作用是更容易的概念."纯函数"是将其输入值映射为输出值的函数function plus(x, y) { return x + y; }."副作用"是除返回值以外的任何效果.所以,例如:

function plusWithSideEffects(x, y) { alert("This is a side effect"); return x + y; } 
Run Code Online (Sandbox Code Playgroud)

具有引发警报对话框(并要求用户交互)的副作用.每个代码函数都有一些副作用(它们都消耗内存并且花费时间,如果没有别的话),但是当人们谈论副作用时,他们通常最关心的是IO(如上面的警告对话框)或者写入状态超出了功能的执行期.

副作用带来的挑战是它们使得函数更难以推理和重用.(这是很容易推理和复用功能,这些功能为接近"纯函数"成为可能,因为他们往往以"做一两件事.")

  • 对于“图形设计师的编程水平”,这是一个真正的答案。 (2认同)

out*_*tis 7

具有副作用的函数除了返回值之外还执行其他操作(尽管它们也可以执行此操作).如果您可以使用这些参数的值替换给定参数的所有函数调用,并且程序具有相同的行为,则没有副作用.这要求函数始终为给定的参数返回相同的值.

也就是说,假设f(1,2) == 12.如果你总是可以替换f(1,2),12并且程序的行为方式相同,那么f对这些参数没有副作用.另一方面,如果在一个地方f(1,2) == 12和另一个地方f(1,2) == 13,那么f有副作用.同样,如果程序在替换f(1,2)为12 之后停止发送电子邮件,则会f产生副作用.通常,如果f(x,y) == z(其中z取决于x和y)并且您始终可以替换每个f(x,y)调用z,则f没有副作用.

一些带副作用的简单功能:

// doesn't always return the same value
function counter() {
    // globals are bad
    return ++x;
}
// omitting calls to `say` change logging behavior
function say(x) {
    console.log(x);
    return x;
}
Run Code Online (Sandbox Code Playgroud)


Jes*_*ood 5

副作用:

将副作用视为同时做两件事的事情。例如:

副作用的经典示例:

var i = 1;
var j = i++;
Run Code Online (Sandbox Code Playgroud)

副作用发生在i++。这里发生的是j变为 1 ,然后 i递增并变为 2。换句话说,发生了两件事,副作用是i变为 2。

关闭:

可视化这样的链接链:<><><><><><><>。想象一下,这个链接链的名字叫做作用域链。然后想象所有这些链接像这样将对象连接在一起:<>object<>object<>object<>。现在,请记住以下几点:

(1)所有作用域链都以全局对象 开头

(2)定义函数时,会存储该函数的作用域链

(3)当一个函数被调用时,它会创建一个新对象并将其添加到作用域链中。

现在,请看下面的例子:

function counter () { // define counter
                   var count = 0;
                   return function () { return count + 1;}; // define anonymous function
                   };
var count = counter(); // invoke counter
Run Code Online (Sandbox Code Playgroud)

在这个例子中,当counter()定义时,计数器的作用域链看起来像这样:<>全局对象<>。然后,当counter()被调用时,作用域链看起来像这样:<>全局对象<>计数器对象<>。之后,定义并调用 counter 中没有名称的函数(称为匿名函数)。一旦调用匿名函数的作用域链如下所示:<>全局对象<>计数器对象<>匿名函数对象<>

这是闭包部分。如果您注意到,匿名函数正在使用在count其外部定义的变量。原因是匿名函数可以访问在其作用域链中定义的任何变量。这就是闭包,一个函数以及对其存储的作用域链中任何变量的引用。

然而,在上面的例子中,一旦函数返回,调用时创建的对象就会被丢弃,所以真的没有意义。现在看看以下内容:

function counter () { // define counter
                   var count = 0;
                   function f() { return count + 1;}; // define f
                   return f; // return f
                   };
var count = counter(); // invoke counter
Run Code Online (Sandbox Code Playgroud)

在此示例中,我返回一个名为的函数f并将其分配给变量count。现在该变量count持有对整个作用域链的引用,并且不会被丢弃。换句话说,变量 count 像这样存储作用域链:<>全局对象<>计数器对象<>匿名函数对象<>。这是瓶盖的力量,你可以拿着一个作用域链的引用,并调用它是这样的:count()

  • **此答案的结束部分不正确**。JavaScript 有*静态作用域*,这意味着函数的作用域链定义在函数*定义*,**不是**函数*调用*。该示例之所以有效,是因为每次调用 `counter` 时都会定义匿名函数,这意味着每个返回的函数都将有一个作用域链,其中包含不同的 *activation object* 用于 `counter`,因此有一组不同的局部变量(例如`计数`)。[有关更多信息,请参阅此答案。](http://stackoverflow.com/questions/7721200/using-javascript-closures-in-settimeout/7722057#7722057) (2认同)