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

时间:2011-11-14 22:27:53

标签: javascript closures side-effects

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

6 个答案:

答案 0 :(得分:33)

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

function plusWithSideEffects(x, y) { alert("This is a side effect"); return x + y; } 

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

副作用带来的挑战是它们使函数更难以推理和重用。 (推理和重用尽可能接近“纯函数”的函数要容易得多,因为它们倾向于“做好一件事。”)

答案 1 :(得分:5)

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

即,假设f(1,2) == 12。如果您始终可以将f(1,2)替换为12并且程序的行为方式相同,那么f对这些参数没有任何副作用。另一方面,如果在一个地方f(1,2) == 12和另一个f(1,2) == 13,则f会产生副作用。同样,如果程序在用{12替换f(1,2)后停止发送电子邮件,则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;
}

答案 2 :(得分:4)

副作用:

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

副作用的典型例子:

var i = 1;
var j = i++;

副作用发生在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

在此示例中,定义counter()时,计数器的范围链如下所示:<> global object<>。然后,当调用counter()时,范围链如下所示:<> global object<> counter object<>。之后,定义并调用计数器内部没有名称的函数(称为匿名函数)。一旦被调用,匿名函数的作用域链如下所示:<>全局对象<>计数器对象<>匿名函数对象<>

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

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

function counter () { // define counter
                   var count = 0;
                   function f() { return count + 1;}; // define f
                   return f; // return f
                   };
var count = counter(); // invoke counter

在此示例中,我将返回名为f的函数,并将其分配给变量count。现在,变量count包含对整个范围链的引用,并且不会被丢弃。换句话说,变量计数存储范围链,如下所示:<>全局对象<>计数器对象<>匿名函数对象<>。这是闭包的强大功能,您可以对范围链进行引用,并将其称为:count()

答案 3 :(得分:0)

<强>为例

function outer() {
    var outerVar;

    var func = function() {
        var innerVar
        ...
        x = innerVar + outerVar
    }
    return func
}

当outer()死掉时,函数func()继续livе,这使用实用

答案 4 :(得分:0)

我是JavaScript的新手,不会试图谈论闭包。然而,我对JavaScript的新见感让我非常清楚使用副作用,这在我的常用编程语言(Erlang)中是不可能的。

副作用似乎是改变JavaScript状态的常用方法。以w3cschools.com网站上的这个例子为例:

<script>
function myFunction() {
    document.getElementById("demo").innerHTML = "Paragraph changed.";
}
</script>

这里没有输入参数或返回值,而是文档的内容被更改,因为它们在函数范围内是全局的。例如,如果您要在Erlang中编写此文档,则该文档将作为参数传入,并返回新的文档状态。阅读调用程序的人会看到传入的文档和返回的更改文档。

看到被称为不返回显式新状态的函数应警告程序员可能使用副作用。

答案 5 :(得分:0)

主要副作用是从函数内部与外界的交互。副作用的一些示例是:- API调用或HTTP请求,数据突变,打印到屏幕或控制台, DOM查询/操作。 例子:

var a = 12
function addTwo(){
   a = a + 2; // side-effect

}
addTwo()

关闭

根据MDN,

闭包使您可以从内部函数访问外部函数的作用域。在JavaScript中,每次创建函数时都会在函数创建时创建闭包。

示例:

function outer(){
  var a = 12; // Declared in outer function 
  function addTwo(){ // closure function
    a = a + 2; // acessing outer function property
    console.log(a)
  }
  addTwo();
 }
 outer()