我正在学习闭包是如何工作的,并开始使用一些代码。根据我的理解,闭包是一种内存,可以在该状态下保存函数的环境,即使父函数结束,闭包也被创建并且仍然存活(当返回或绑定到事件时)。
所以我尝试了下面的代码,它按预期工作。两个按钮,将相应的文本框从“检查”切换为“已保存”。
//var v1;
//var v2;
//var v3;
function func1(src, action, arg) {
document.getElementById(arg).value = "check";
document.getElementById(src.id).onclick = function() {
func2(src, action, arg);
};
//v1 = src;
//v2 = action;
//v3 = arg;
}
function func2(v1, v2, v3) {
document.getElementById(v3).value = "saved";
document.getElementById(v1.id).onclick = function() {
func1(v1, v2, v3);
};
}
<input type=text id="t1">
<input type=button id="btn1" value="Go 1" onclick="func1(this, 'edit', 't1')">
<input type=text id="t2">
<input type=button id="btn2" value="Go 2" onclick="func1(this, 'edit', 't2')">
但现在出现了混乱。当我使用全局变量来构造func2()中的闭包时,开关会出错。请参阅以下代码:
var v1;
var v2;
var v3;
function func1(src, action, arg) {
document.getElementById(arg).value = "check";
document.getElementById(src.id).onclick = function() {
func2();
};
v1 = src;
v2 = action;
v3 = arg;
}
function func2() {
document.getElementById(v3).value = "saved";
document.getElementById(v1.id).onclick = function() {
func1(v1, v2, v3);
};
}
<input type=text id="t1">
<input type=button id="btn1" value="Go 1" onclick="func1(this,'edit','t1')">
<input type=text id="t2">
<input type=button id="btn2" value="Go 2" onclick="func1(this,'edit','t2')">
点击Go1 - &gt; Textbox1 = check;点击Go2 - &gt; Textbox2 = check;但现在点击Go1 - &gt; Textbox2(而不是1)=已保存。
所以闭包中的变量v1,v2,v3似乎仍然使用闭包之外的全局值。有人可以解释为什么以及如何做,所以它适用于全局变量?
感谢T.J.Crowder我更新了我的代码,在闭包中使用私有变量。但不幸的是,它仍然无效。与第二个代码块相同的行为。
var v1;
var v2;
var v3;
function func1(src, action, arg) {
document.getElementById(arg).value = "check";
document.getElementById(src.id).onclick = function() {
func2();
};
v1 = src;
v2 = action;
v3 = arg;
}
function func2() {
var private1 = v1;
var private2 = v2;
var private3 = v3;
document.getElementById(private3).value = "saved";
document.getElementById(private1.id).onclick = function() {
func1(private1, private2, private3);
};
}
<input type=text id="t1">
<input type=button id="btn1" value="Go 1" onclick="func1(this,'edit','t1')">
<input type=text id="t2">
<input type=button id="btn2" value="Go 2" onclick="func1(this,'edit','t2')">
答案 0 :(得分:1)
所以闭包中的变量v1,v2,v3似乎仍然使用闭包之外的全局值
是的,因为没有任何东西影响他们,所以他们最终会如何解决。
在您的第一个代码块中,onclick
处理程序正在使用src
调用中提供的action
,arg
和func1
参数v1
调用中提供的v2
,v3
和func2
参数。
在第二个示例中,由于您删除了func2
的参数,因此这些标识符在其创建的onclick
函数范围内的唯一原因是全局变量;如果它不是全局变量,那么你将获得ReferenceError
因为这些标识符无法解析。所以它是使用的全局变量。
如果您在第二个示例中将参数传递给func2
,这些参数具有相同的名称(不是一个好主意,只是为了解释),那么onclick
关闭它使用的标识符create会解决这些参数而不是全局变量。
解释这条线的作用可能很有用:
document.getElementById(v1.id).onclick = function () { func1(v1,v2,v3); };
该行创建一个函数,它是创建它的上下文的闭包,对func2
的调用(该上下文指的是创建它的上下文,依此类推全球背景)。 onclick
闭包在创建时没有副本范围内的变量,它有一个持久引用。因此,当函数运行时,它将被使用的变量的值。在你的第一个例子中,这些值永远不会改变,因为它们是在创建闭包的调用期间传递给func2
的参数的值,并且没有任何改变它们。但是,在第二个示例中,这些值是不同的,因为您使用的是全局变量,而不是传递给func2
的参数。
因为闭包具有对创建它的上下文的引用,所以每个调用到func2
的上下文由该调用中创建的闭包保留。因此,如果保留由多个调用创建的闭包,则为func2
的调用保留多个上下文,因此保留了多个参数副本(每个副本都由为该上下文创建的闭包使用)。一个更简单的例子可能会有所帮助:
// A global variable
var global = "g";
// A function that creates and returns a closure
function foo(arg) {
return function() {
snippet.log("global = " + global +) ", arg = " + arg);
};
}
// Create a closure over arg = 42
var f1 = foo(42);
f1(); // global = g, arg = 42
// Change global
global = "g+";
f1(); // global = g+, arg = 42
// Create a second closure over a second arg, 67
var f2 = foo(67);
f2(); // global = g+, arg = 67
// Change global again
global = "g++";
f1(); // global = g++, arg = 42
f2(); // global = g++, arg = 67
&#13;
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
&#13;
请注意每个闭包如何拥有自己的arg
副本,但它们都共享global
。
此代码段演示了闭包的内容是对arg
的引用,而不是对其值的副本的引用:
function foo(arg) {
return {
showArg: function() {
console.log("arg = " + arg);
},
incrementArg: function() {
++arg;
}
};
}
var o1 = foo(42);
o1.showArg(); // arg = 42
o1.incrementArg();
o1.showArg(); // arg = 43
&#13;
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
&#13;
您可以在我的贫穷小博客上找到这篇文章有用:Closures are not complicated
这是一个片段,演示了在调用func2
的闭包中捕获当时的全局变量,但我们最终只是复制了src
,action
中已有的内容,和arg
,所以没有意义。
var v1;
var v2;
var v3;
function func1(src, action, arg) {
var p1 = src, p2 = action, p3 = arg; // Just duplicates what we already have
document.getElementById(arg).value = "check";
document.getElementById(src.id).onclick = function() {
func2(p1, p2, p3); // We could just use src, action, and arg here like your first example
};
v1 = src;
v2 = action;
v3 = arg;
}
function func2(v1, v2, v3) {
document.getElementById(v3).value = "saved";
document.getElementById(v1.id).onclick = function() {
func1(v1, v2, v3);
};
}
&#13;
<input type=text id="t1">
<input type=button id="btn1" value="Go 1" onclick="func1(this,'edit','t1')">
<input type=text id="t2">
<input type=button id="btn2" value="Go 2" onclick="func1(this,'edit','t2')">
&#13;