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