JavaScript闭包和全局变量

铁匠铺

我只是在学习闭包是如何工作的,并开始使用一些代码。据我了解,闭包是某种内存,它在该状态下保存了该函数的环境,闭包已创建,即使父函数已结束,它仍处于活动状态(返回或绑定到事件时)。

所以我尝试了下面的代码,它可以正常工作。两个按钮将其对应的文本框从“检查”切换为“保存”。

//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-> Textbox1 = check; 点击Go2-> Textbox2 = check; 但是现在单击Go1-> Textbox2(而不是1)=保存。

因此,闭包中的变量v1,v2,v3似乎仍使用闭包外部的全局值。有人可以解释为什么和怎么做才能与全局变量一起工作吗?


感谢TJCrowder,我更新了代码以在闭包中使用私有变量。但不幸的是,它仍然无法正常工作。行为与第二个代码块相同。

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')">

TJ人群

因此,似乎闭包中的变量v1,v2,v3仍使用闭包外的全局值

是的,因为没有任何东西遮盖住它们,所以这才是最终解决它们的方式。

在你的第一个代码块,该onclick处理器使用srcaction以及arg在调用供给参数func1v1v2以及v3在调用提供的参数func2

在第二个示例中,由于删除了的参数func2,所以这些标识符在onclick其创建函数范围内的唯一原因是全局变量。如果不是全局变量,ReferenceError则将得到s,因为这些标识符将不可解析。因此,习惯了全局变量。

如果您func2在第二个示例中将具有相同名称的参数传递给(不是一个好主意,只是为了说明起见),那么onclick它创建闭包所使用的标识符将针对这些参数而不是针对全局变量进行解析。

解释此行的内容可能会很有用:

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
<!-- 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>

请注意,每个闭包如何都有其自己的副本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
<!-- 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>

您可能会在我贫乏的小博客上发现这篇文章有用:关闭并不复杂


以下是一个演示捕捉当时的全局在封闭通话功能的片断func2,但我们最终只是重复我们已经在srcactionarg,所以没有点。

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);
  };
}
<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')">

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章