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

Posted

技术标签:

【中文标题】JavaScript 中的闭包/回调函数都有哪些用例?【英文标题】:What are the use cases for closures/callback functions in JavaScript?JavaScript 中的闭包/回调函数有哪些用例? 【发布时间】:2011-02-07 00:32:35 【问题描述】:

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

这是一个真实的陈述,一个人可以在有或没有回调的情况下完成相同的功能。

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

我不是在寻找“闭包使代码更安全”的笼统陈述,而是在回调是正确想法时的实际示例或经验法则列表。

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

【问题讨论】:

【参考方案1】:

This writeup from Mozilla may answer why use closures and when

另外,see this set of examples (especially "What can be done with Closures?" section that has the following exmples):

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

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

【讨论】:

【参考方案2】:

首先:

回调:作为参数传递给另一个函数的函数,通常在事件发生时被调用。 关闭:保留范围。 IE。当你在另一个函数中声明一个函数时,outer 函数的作用域可以在 inner 函数中访问。

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

这是一个回调:

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"

闭包的一个常见用法是提供信息隐藏,这有助于为语言带来某种封装。看看the module pattern 看看这个在行动中。

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

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

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

那是行不通的。单击元素时,变量i99。为了使其正常工作,我们可以使用闭包来捕获 i 的值:

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


for (var i = 0; i < 100; ++i) 
    myElements[i].onclick = getHandler(i);

【讨论】:

反例很经典:)。【参考方案3】:

假设您想要一个函数,当您创建新的 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() 为新的&lt;div&gt; 获取新的“id”值。该函数是通过调用一个函数创建的,该函数提供隐藏在闭包中的两个值:前缀字符串(作为参数传入)和计数器(在闭包范围内声明的var)。

一旦您习惯了它,该设施就会非常灵活和有用,以至于您在回到没有它的环境时会感到痛苦。

哦,如果您尝试一下,这里有一个提示可以帮助您远离 ***:这是一个一直弹出的问题:

for (var i = 0; i < 10; ++i) 
  var id = "foo" + i;
  var element = document.getElementById(id);
  element.onclick = function() 
    alert("hello from element " + i);
  ;

这里有什么问题?好吧,该函数引用的“i”变量是该循环运行的范围内的“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);

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

总结一下:一旦你习惯了,利用闭包的技术就会出现所有的时间。这不是进入无错误编程新仙境的免费门票。不要误会我的意思。然而,它是一个非常有用且灵活的范例。

【讨论】:

以上是关于JavaScript 中的闭包/回调函数都有哪些用例?的主要内容,如果未能解决你的问题,请参考以下文章

精读JavaScript模式,函数的回调闭包与重写模式

JavaScript闭包和回调详解

JavaScript 里的闭包是啥?应用场景都有哪些

JavaScript 回调函数属于闭包?

js闭包函数

nodejs异步回调函数中this问题,求助