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

发布于 2024-08-28 17:25:19 字数 387 浏览 5 评论 0原文

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

这基本上是一个真实的说法,无论有没有回调,一个人都可以完成相同的功能。

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

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

克罗克福德的介绍: http://www.yuiblog.com/blog/2010/ 04/08/video-crockonjs-5/

I was listening to Crockford's talk on JavaScript closures and am convinced of the benefit of information hiding, but I do not have a firm understanding of when to use callback functions.

It is mostly a true statement that a person could accomplish the same functionality with or without callbacks.

As someone who is writing code, what heuristics or cues should I keep in mind when determining when to use callbacks/closures?

I am not looking for the blanket statement 'Closures make more secure code', rather a list of practical examples or rules of thumb for when callbacks are the right idea.

Crockford's Presentation:
http://www.yuiblog.com/blog/2010/04/08/video-crockonjs-5/

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

带刺的爱情 2024-09-04 17:25:19

首先:

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

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

这是一个回调:

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

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

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

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

Firstly:

  • Callback: A function passed as an argument to another function, usually to be called as a result of an event occurring.
  • Closure: A retained scope. I.e. the concept that when you declare a function within another function, the outer function's scope is accessible within the inner function.

Callbacks can also be closures but are not always.

This is a callback:

someProcess(myCallback);

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

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

A closure:

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"

One common usage of closures is to provide information-hiding, which is helpful in bringing some kind of encapsulation to the language. Have a look at the module pattern to see this in action.

Another common usage is when the binding event handlers to elements. E.g.

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

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

That wouldn't work. By the time the element is clicked, the variable i is 99. To make this work properly we could use a closure to capture the value of i:

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

for (var i = 0; i < 100; ++i) {
    myElements[i].onclick = getHandler(i);
}
作妖 2024-09-04 17:25:19

假设您需要一个函数,可以用来返回唯一的“id”值,以便在创建新的 DOM 元素时使用。现在,在 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”值。该函数是通过调用一个函数创建的,该函数提供存储在闭包中的两个值:前缀字符串(作为参数传入)和计数器(在闭包范围)。

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

哦,如果您尝试一下,这里有一个提示可以帮助您远离 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);
  };
}

这里有什么问题?那么,该函数引用的“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”的副本复制到其自己的闭包范围中,因此现在每个事件处理程序都有自己的!

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

Let's say you want a function that you can use to return a unique "id" value to use when you create new DOM elements. Now, in something like Java, you could create a class with an internal private counter, and then have a method that appends the counter to some prefix string. Well, in Javascript:

var getId = (function() {
  var counter = 0;
  return function() {
    return "prefix" + counter++;
  };
})();

Now the variable "getId" is bound to a function that's created by another function, and created in such a way that it has a persistent variable to use between invocations. Similarly, if I wanted to have a family of "getId" functions (say, one for each type of DOM element I might add), I could do this:

var getIdFunc = function(prefix) {
  var counter = 0;
  return function() {
    return prefix + counter++;
  };
};
var getId = {
  'div': getIdFunc('div'),
  'span': getIdFunc('span'),
  'dl': getIdFunc('dl'),
  // ...
};

Now I can call getId.div() to get a new "id" value for a new <div>. The function was created by calling a function that provides two values stashed away in a closure: the prefix string (passed in as an argument) and the counter (a var declared in the closure scope).

Once you get used to it, the facility is so flexible and useful that you'll feel pain at moving back to an environment without it.

Oh, and here's a tip to help keep you off StackOverflow should you try this out: it's an issue that pops up all the time:

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

What's the problem here? Well, that "i" variable that's referenced by that function is the "i" from the scope in which that loop runs. That variable, you'll note, gets incremented through the loop (duhh, right?). Well, every single one of those little functions created and assigned as event handlers will share that same, single variable "i" in the closure scope. Oops! The solution is to do something like this:

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

We make a copy of the outer "i" into a closure scope of its own, so now each event handler has its own!

To summarize: the technique of leveraging closures comes up all the freaking time once you get used to it. It's not a free ticket into a new wonderland of error-free programming; don't get me wrong. It is, however, a very useful and flexible paradigm.

夜访吸血鬼 2024-09-04 17:25:19

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

, 查看这组示例(尤其是“闭包可以做什么?”部分)有以下示例)

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

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

This writeup from Mozilla may answer why use closures and when

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

  • Example 1: setTimeout with Function References
  • Example 2: Associating Functions with Object Instance Methods
  • Example 3: Encapsulating Related Functionality

I have a feeling that this can be traced to Crockford, but the classic use of closures is to emulate private instance or static variables (which JavaScript lacks)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文