JavaScript中的闭包/回调函数有哪些用例?

Chr*_*man 21 javascript closures callback

我是听Crockford的JavaScript的关闭谈话和确信信息隐藏的利益,但我没有的时候使用回调函数坚定的认识.

这主要是一个真实的陈述,一个人可以使用或不使用回调来完成相同的功能.

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

我不是在寻找一揽子声明'闭包制作更安全的代码',而是在回调是正确的想法时,列出一些实际示例或经验法则.

Crockford的演讲:http: //www.yuiblog.com/blog/2010/04/08/video-crockonjs-5/

Jam*_*mes 33

首先:

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

回调也可以是闭包,但并非总是如此.

这是一个回调:

someProcess(myCallback);

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

function someProcess(callback) {
    // does stuff...
    // ...
    callback();
}
Run Code Online (Sandbox Code Playgroud)

关闭:

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"
Run Code Online (Sandbox Code Playgroud)

闭包的一个常见用法是提供信息隐藏,这有助于为语言带来某种封装.查看模块模式以查看此操作.

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

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

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

那不行.单击元素时,变量i99.为了使这个工作正常,我们冷使用一个闭包来捕获以下值i:

function getHandler(n) {
    return function() {
        alert( 'You clicked on: ' + n );
    };
}

for (var i = 0; i < 100; ++i) {
    myElements[i].onclick = getHandler(i);
}
Run Code Online (Sandbox Code Playgroud)


Poi*_*nty 14

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

var getId = (function() {
  var counter = 0;
  return function() {
    return "prefix" + counter++;
  };
})();
Run Code Online (Sandbox Code Playgroud)

现在,变量"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'),
  // ...
};
Run Code Online (Sandbox Code Playgroud)

现在我可以打电话getId.div()给新的"id"值<div>.该函数是通过调用一个函数创建的,该函数提供了两个隐藏在闭包中的值:前缀字符串(作为参数传入)和计数器(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);
  };
}
Run Code Online (Sandbox Code Playgroud)

这有什么问题?那么,该函数引用的"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);
}
Run Code Online (Sandbox Code Playgroud)

我们将外部"i"的副本复制到它自己的闭包范围中,所以现在每个事件处理程序都有自己的!

总结:利用封闭的技术出现的所有该死的时间,一旦你习惯了它.这不是一个无错误编程的新仙境的免费门票; 别误会我的意思.然而,它是一个非常有用和灵活的范例.


DVK*_*DVK 5

Mozilla的这篇文章可能会回答为什么使用闭包以及何时使用闭包

另外,请参阅这组示例(特别是"可以使用闭包做什么?"部分,其中包含以下示例):

  • 示例1:带有函数引用的setTimeout
  • 示例2:将函数与对象实例方法相关联
  • 示例3:封装相关功能

我觉得这可以追溯到Crockford,但闭包的经典用法是模拟私有实例或静态变量(JavaScipt缺乏)