JavaScript中闭包/回调函数的用例是什么?

JavaScript中闭包/回调函数的用例是什么?,javascript,callback,closures,Javascript,Callback,Closures,我在听Crockford关于JavaScript闭包的演讲,我确信信息隐藏的好处,但我对何时使用回调函数没有明确的理解 一个人可以通过回调或不通过回调来完成相同的功能,这基本上是一个正确的说法 作为编写代码的人,在决定何时使用回调/闭包时,我应该记住哪些启发或提示 我不是在寻找“闭包使代码更安全”这一笼统的语句,而是一个实用示例列表或经验法则,以说明回调何时是正确的想法 Crockford的介绍: 还有: 示例1:使用函数引用设置超时 示例2:将函数与对象实例方法关联 示例3:封装相关功能

我在听Crockford关于JavaScript闭包的演讲,我确信信息隐藏的好处,但我对何时使用回调函数没有明确的理解

一个人可以通过回调或不通过回调来完成相同的功能,这基本上是一个正确的说法

作为编写代码的人,在决定何时使用回调/闭包时,我应该记住哪些启发或提示

我不是在寻找“闭包使代码更安全”这一笼统的语句,而是一个实用示例列表或经验法则,以说明回调何时是正确的想法

Crockford的介绍:

还有:

  • 示例1:使用函数引用设置超时
  • 示例2:将函数与对象实例方法关联
  • 示例3:封装相关功能
我觉得这可以追溯到Crockford,但闭包的经典用法是模拟私有实例或静态变量(JavaScipt缺少这些变量)

首先:

  • 回调:作为参数传递给另一个函数的函数,通常在事件发生时被调用
  • 闭包:保留的范围。也就是说,当你在另一个函数中声明一个函数时,外部函数的作用域可以在内部函数中访问
回调也可以是闭包,但并不总是闭包

这是一个回调:

someProcess(myCallback);

function myCallback() {
    alert('Done...');
}

function someProcess(callback) {
    // does stuff...
    // ...
    callback();
}
结束:

function foo(msg) {

    function bar() {
        // I can access foo's scope
        // (i.e. bar can access everything that foo can access)
        alert(msg);
    }

    return bar;

}

foo('hello')(); // alerts "hello"
闭包的一个常见用法是提供信息隐藏,这有助于为语言带来某种封装。看一看,看看这是如何起作用的

另一个常见用法是将事件处理程序绑定到元素。例如

var myElements = [ /* DOM Collection */ ];

for (var i = 0; i < 100; ++i) {
    myElements[i].onclick = function() {
        alert( 'You clicked on: ' + i );
    };
}

假设您需要一个函数,可以使用该函数返回在创建新DOM元素时使用的唯一“id”值。现在,在类似Java的东西中,您可以创建一个具有内部私有计数器的类,然后有一个方法将计数器附加到某个前缀字符串。在Javascript中:

var getId = (function() {
  var counter = 0;
  return function() {
    return "prefix" + counter++;
  };
})();
现在,变量“getId”被绑定到由另一个函数创建的函数,并以这样的方式创建,即在调用之间使用一个持久变量。类似地,如果我想拥有一系列“getId”函数(比如,我可能添加的每种类型的DOM元素都有一个),我可以这样做:

var getIdFunc = function(prefix) {
  var counter = 0;
  return function() {
    return prefix + counter++;
  };
};
var getId = {
  'div': getIdFunc('div'),
  'span': getIdFunc('span'),
  'dl': getIdFunc('dl'),
  // ...
};
现在我可以调用
getId.div()
为新的
获取一个新的“id”值。该函数是通过调用一个函数创建的,该函数提供隐藏在闭包中的两个值:前缀字符串(作为参数传入)和计数器(闭包范围中声明的
var

一旦你习惯了它,这个设施是如此的灵活和有用,以至于你在回到一个没有它的环境时会感到痛苦

哦,这里有一个小贴士可以帮助你避免StackOverflow,如果你尝试这个方法的话:这是一个经常出现的问题:

for (var i = 0; i < 10; ++i) {
  var id = "foo" + i;
  var element = document.getElementById(id);
  element.onclick = function() {
    alert("hello from element " + i);
  };
}
for(变量i=0;i<10;++i){
var id=“foo”+i;
var元素=document.getElementById(id);
element.onclick=function(){
警报(“来自元素的你好”+i);
};
}
这里有什么问题?这个函数引用的“i”变量是循环运行范围中的“i”。您会注意到,该变量在循环中递增(duhh,对吗?)。好吧,创建并分配为事件处理程序的每一个小函数都将在闭包范围内共享同一个变量“i”。哎呀!解决方案是这样做:

for (var i = 0; i < 10; ++i) {
  var id = "foo" + i;
  var element = document.getElementById(id);
  element.onclick = (function(iCopy) {
    return function() {
      alert("hello from element " + iCopy);
    };
  })(i);
}
for(变量i=0;i<10;++i){
var id=“foo”+i;
var元素=document.getElementById(id);
element.onclick=(函数(iCopy){
返回函数(){
警报(“来自元素的你好”+iCopy);
};
})(i) );
}
我们将外部“i”复制到它自己的闭包范围中,因此现在每个事件处理程序都有自己的闭包范围

总而言之:一旦你习惯了,利用闭包的技术就会经常出现。这不是一张免费入场券,进入一个无错误编程的新仙境;别误会我。然而,这是一个非常有用和灵活的范例。

反例是经典:)。
for (var i = 0; i < 10; ++i) {
  var id = "foo" + i;
  var element = document.getElementById(id);
  element.onclick = (function(iCopy) {
    return function() {
      alert("hello from element " + iCopy);
    };
  })(i);
}