JavaScript 中闭包的实际用途是什么?
我正在尝试尽最大努力来理解 JavaScript 闭包。
我通过返回内部函数得到这一点,它将可以访问其直接父级中定义的任何变量。
这对我有什么用处?也许我还没有完全理解它。大多数我在网上看到的示例不提供任何现实世界的代码,只是模糊的示例。
有人可以向我展示闭包在现实世界中的用法吗?
例如,这是一个吗?
var warnUser = function (msg) {
var calledCount = 0;
return function() {
calledCount++;
alert(msg + '\nYou have been warned ' + calledCount + ' times.');
};
};
var warnForTamper = warnUser('You can not tamper with our HTML.');
warnForTamper();
warnForTamper();
I'm trying my hardest to wrap my head around JavaScript closures.
I get that by returning an inner function, it will have access to any variable defined in its immediate parent.
Where would this be useful to me? Perhaps I haven't quite got my head around it yet. Most of the examples I have seen online don't provide any real world code, just vague examples.
Can someone show me a real world use of a closure?
Is this one, for example?
var warnUser = function (msg) {
var calledCount = 0;
return function() {
calledCount++;
alert(msg + '\nYou have been warned ' + calledCount + ' times.');
};
};
var warnForTamper = warnUser('You can not tamper with our HTML.');
warnForTamper();
warnForTamper();
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(24)
假设您想要计算用户单击网页上的按钮的次数。
为此,您在按钮的
onclick
事件上触发函数来更新变量的计数现在可能有很多方法,例如:
您可以使用 < strong>全局变量,以及增加计数器的函数:
但是,陷阱是页面上的任何脚本都可以更改计数器,而无需调用
updateClickCount()
。现在,您可能正在考虑在函数内声明变量:
但是,嘿!每次调用
updateClickCount()
函数时,计数器都会再次设置为 1。考虑嵌套函数吗?
嵌套函数可以访问它们“上方”的作用域。
在此示例中,内部函数
updateClickCount()
可以访问父函数countWrapper()
中的计数器变量:如果您可以从外部访问
updateClickCount()
函数,并且还需要找到一种方法来执行counter = 0
,那么这可以解决计数器困境只一次而不是每次。关闭救援! (自调用函数):
自调用函数仅运行一次。它将
计数器
设置为零 (0),并返回一个函数表达式。这样
updateClickCount
就成为一个函数。 “美妙”的部分是它可以访问父作用域中的计数器。这称为JavaScript 闭包。它使得函数可以拥有“私有”变量。
计数器
受匿名函数范围的保护,并且只能使用updateClickCount()
函数进行更改!关于闭包的更生动的例子
参考:JavaScript 闭包
Suppose, you want to count the number of times user clicked a button on a webpage.
For this, you are triggering a function on
onclick
event of button to update the count of the variableNow there could be many approaches like:
You could use a global variable, and a function to increase the counter:
But, the pitfall is that any script on the page can change the counter, without calling
updateClickCount()
.Now, you might be thinking of declaring the variable inside the function:
But, hey! Every time
updateClickCount()
function is called, the counter is set to 1 again.Thinking about nested functions?
Nested functions have access to the scope "above" them.
In this example, the inner function
updateClickCount()
has access to the counter variable in the parent functioncountWrapper()
:This could have solved the counter dilemma, if you could reach the
updateClickCount()
function from the outside and you also need to find a way to executecounter = 0
only once not everytime.Closure to the rescue! (self-invoking function):
The self-invoking function only runs once. It sets the
counter
to zero (0), and returns a function expression.This way
updateClickCount
becomes a function. The "wonderful" part is that it can access the counter in the parent scope.This is called a JavaScript closure. It makes it possible for a function to have "private" variables.
The
counter
is protected by the scope of the anonymous function, and can only be changed using theupdateClickCount()
function!A more lively example on closures
Reference: JavaScript Closures
我使用闭包来执行以下操作:
正如您所看到的,
a
现在是一个对象,具有方法publicfunction
(a.publicfunction()< /code> )调用
privatefunction
,它仅存在于闭包内部。您可以不直接调用privatefunction
(即a.privatefunction()
),只需publicfunction()
。这是一个最小的示例,但也许您可以看到它的用途?我们用它来强制执行公共/私有方法。
I've used closures to do things like:
As you can see there,
a
is now an object, with a methodpublicfunction
(a.publicfunction()
) which callsprivatefunction
, which only exists inside the closure. You can not callprivatefunction
directly (i.e.a.privatefunction()
), justpublicfunction()
.It's a minimal example, but maybe you can see uses to it? We used this to enforce public/private methods.
你给出的例子是一个很好的例子。闭包是一种抽象机制,可以让您非常干净地分离关注点。您的示例是将检测(对调用进行计数)与语义(错误报告 API)分离的情况。其他用途包括:
将参数化行为传递到算法中(经典的高阶编程):
模拟面向对象编程:
实现奇异的流控制,例如 jQuery 的事件处理和 AJAX API。
The example you give is an excellent one. Closures are an abstraction mechanism that allow you to separate concerns very cleanly. Your example is a case of separating instrumentation (counting calls) from semantics (an error-reporting API). Other uses include:
Passing parameterised behaviour into an algorithm (classic higher-order programming):
Simulating object oriented programming:
Implementing exotic flow control, such as jQuery's Event handling and AJAX APIs.
JavaScript 闭包可用于在应用程序中实现throttle 和debounce 功能。
限制
限制限制了随着时间的推移可以调用函数的最大次数。如“最多每 100 毫秒执行一次此函数”。
代码:
去
抖 去抖对函数进行了限制,直到经过一定时间而没有调用该函数为止。正如“仅当 100 毫秒没有被调用时才执行此函数”。
代码:
正如您所看到的,闭包有助于实现两个漂亮的功能,每个 Web 应用程序都应该拥有这两个功能来提供流畅的 UI 体验功能。
JavaScript closures can be used to implement throttle and debounce functionality in your application.
Throttling
Throttling puts a limit on as a maximum number of times a function can be called over time. As in "execute this function at most once every 100 milliseconds."
Code:
Debouncing
Debouncing puts a limit on a function not be called again until a certain amount of time has passed without it being called. As in "execute this function only if 100 milliseconds have passed without it being called."
Code:
As you can see closures helped in implementing two beautiful features which every web application should have to provide smooth UI experience functionality.
是的,这是一个有用的闭包的好例子。对 warnUser 的调用在其作用域中创建了 CalledCount 变量,并返回存储在 warnForTamper 变量中的匿名函数。由于仍然有一个闭包使用 calledCount 变量,因此在函数退出时它不会被删除,因此每次调用
warnForTamper()
都会增加作用域变量并警告该值。我在 Stack Overflow 上看到的最常见问题是,有人想要“延迟”使用在每次循环时增加的变量,但由于该变量是有作用域的,因此对该变量的每次引用都将在循环结束后进行,从而导致变量的结束状态:
这将导致每个警报显示相同的
i
值,即循环结束时增加到的值。解决方案是创建一个新的闭包,即变量的单独作用域。这可以使用立即执行的匿名函数来完成,该函数接收变量并将其状态存储为参数:Yes, that is a good example of a useful closure. The call to warnUser creates the
calledCount
variable in its scope and returns an anonymous function which is stored in thewarnForTamper
variable. Because there is still a closure making use of the calledCount variable, it isn't deleted upon the function's exit, so each call to thewarnForTamper()
will increase the scoped variable and alert the value.The most common issue I see on Stack Overflow is where someone wants to "delay" use of a variable that is increased upon each loop, but because the variable is scoped then each reference to the variable would be after the loop has ended, resulting in the end state of the variable:
This would result in every alert showing the same value of
i
, the value it was increased to when the loop ended. The solution is to create a new closure, a separate scope for the variable. This can be done using an instantly executed anonymous function, which receives the variable and stores its state as an argument:特别是在 JavaScript(或任何 ECMAScript)语言中,闭包在隐藏功能的实现同时仍然显示接口方面非常有用。
例如,假设您正在编写一类日期实用程序方法,并且希望允许用户通过索引查找工作日名称,但您不希望他们能够修改您在后台使用的名称数组。
请注意,days 数组可以简单地存储为 dateUtil 对象的属性,但是脚本的用户可以看到它,并且如果他们愿意,他们甚至可以更改它想要的,甚至不需要你的源代码。但是,由于它包含在返回日期查找函数的匿名函数中,因此只能由查找函数访问,因此它现在是防篡改的。
In the JavaScript (or any ECMAScript) language, in particular, closures are useful in hiding the implementation of functionality while still revealing the interface.
For example, imagine you are writing a class of date utility methods and you want to allow users to look up weekday names by index, but you don't want them to be able to modify the array of names you use under the hood.
Note that the
days
array could simply be stored as a property of thedateUtil
object, but then it would be visible to users of the script and they could even change it if they wanted, without even needing your source code. However, since it's enclosed by the anonymous function which returns the date lookup function it is only accessible by the lookup function so it is now tamperproof.有一个关于实用闭包的部分Mozilla 开发者网络。
There is a section on Practical Closures at the Mozilla Developer Network.
如果您熟悉在面向对象的意义上实例化类的概念(即创建该类的对象),那么您就已经接近理解闭包了。
这样想:当您实例化两个 Person 对象时,您知道类成员变量“Name”在实例之间不共享;每个对象都有自己的“副本”。类似地,当您创建闭包时,自由变量(上面示例中的“usedCount”)将绑定到函数的“实例”。
我认为你的概念飞跃受到以下事实的轻微阻碍: warnUser 函数返回的每个函数/闭包(旁白:这是一个高阶函数)闭包将“usedCount”绑定到相同的初始值(0 ),而通常在创建闭包时,将不同的初始值设定项传递给高阶函数会更有用,就像将不同的值传递给类的构造函数一样。
因此,假设当“calledCount”达到某个值时,您想要结束用户的会话;您可能需要不同的值,具体取决于请求是来自本地网络还是来自糟糕的互联网(是的,这是一个人为的示例)。为了实现这一点,您可以将 CalledCount 的不同初始值传递给 warnUser(即 -3 或 0?)。
文献的部分问题在于用于描述它们的术语(“词法范围”、“自由变量”)。不要让它欺骗你,闭包比看起来更简单......表面上看;-)
If you're comfortable with the concept of instantiating a class in the object-oriented sense (i.e. to create an object of that class) then you're close to understanding closures.
Think of it this way: when you instantiate two Person objects you know that the class member variable "Name" is not shared between instances; each object has its own 'copy'. Similarly, when you create a closure, the free variable ('calledCount' in your example above) is bound to the 'instance' of the function.
I think your conceptual leap is slightly hampered by the fact that every function/closure returned by the warnUser function (aside: that's a higher-order function) closure binds 'calledCount' with the same initial value (0), whereas often when creating closures it is more useful to pass different initializers into the higher-order function, much like passing different values to the constructor of a class.
So, suppose when 'calledCount' reaches a certain value you want to end the user's session; you might want different values for that depending on whether the request comes in from the local network or the big bad internet (yes, it's a contrived example). To achieve this, you could pass different initial values for calledCount into warnUser (i.e. -3, or 0?).
Part of the problem with the literature is the nomenclature used to describe them ("lexical scope", "free variables"). Don't let it fool you, closures are more simple than would appear... prima facie ;-)
在此,我有一句想说几遍的问候。如果我创建一个闭包,我可以简单地调用该函数来记录问候语。如果我不创建闭包,我必须每次都传递我的名字。
没有闭包(https://jsfiddle.net/lukeschlangen/pw61qrow/3/):
使用闭包(https://jsfiddle.net/lukeschlangen/Lb5cfve9/3/):
Here, I have a greeting that I want to say several times. If I create a closure, I can simply call that function to record the greeting. If I don't create the closure, I have to pass my name in every single time.
Without a closure (https://jsfiddle.net/lukeschlangen/pw61qrow/3/):
With a closure (https://jsfiddle.net/lukeschlangen/Lb5cfve9/3/):
闭包的另一个常见用途是将方法中的
this
绑定到特定对象,从而允许在其他地方(例如事件处理程序)调用它。每当 mousemove 事件触发时,就会调用 watcher.follow(evt) 。
闭包也是高阶函数的重要组成部分,允许通过参数化不同部分将多个相似函数重写为单个高阶函数的非常常见的模式。作为一个抽象的例子,
变得
其中 A 和 B 不是语法单元,而是源代码字符串(不是字符串文字)。
有关具体示例,请参阅“使用函数简化我的 javascript”。
Another common use for closures is to bind
this
in a method to a specific object, allowing it to be called elsewhere (such as as an event handler).Whenever a mousemove event fires,
watcher.follow(evt)
is called.Closures are also an essential part of higher-order functions, allowing the very common pattern of rewriting multiple similar functions as a single higher order function by parameterizing the dissimilar portions. As an abstract example,
becomes
where A and B aren't syntactical units but source code strings (not string literals).
See "Streamlining my javascript with a function" for a concrete example.
这里我有一个关于闭包概念的简单例子,我们可以在我们的电子商务网站或许多其他网站中使用它。
我在示例中添加了 JSFiddle 链接。它包含一份包含三件商品和一个购物车柜台的小产品清单。
JSFiddle
Here I have one simple example of the closure concept which we can use for in our E-commerce site or many others as well.
I am adding my JSFiddle link with the example. It contains a small product list of three items and one cart counter.
JSFiddle
闭包的使用:
闭包是 JavaScript 最强大的功能之一。 JavaScript 允许函数嵌套,并授予内部函数对外部函数内部定义的所有变量和函数(以及外部函数有权访问的所有其他变量和函数)的完全访问权限。但是,外部函数无法访问内部函数内部定义的变量和函数。
这为内部函数的变量提供了某种安全性。此外,由于内部函数可以访问外部函数的作用域,因此如果内部函数能够在外部函数的生命周期之外生存,则外部函数中定义的变量和函数的生命周期将比外部函数本身更长。当内部函数以某种方式可用于外部函数之外的任何作用域时,就会创建闭包。
示例:
在上面的代码中,外部函数的 name 变量可以被内部函数访问,并且除了通过内部函数之外没有其他方法可以访问内部变量。内部函数的内部变量充当内部函数的安全存储。它们保存“持久”但安全的数据供内部函数使用。这些函数甚至不必分配给变量或有名称。
请阅读此处了解详细信息。
Use of Closures:
Closures are one of the most powerful features of JavaScript. JavaScript allows for the nesting of functions and grants the inner function full access to all the variables and functions defined inside the outer function (and all other variables and functions that the outer function has access to). However, the outer function does not have access to the variables and functions defined inside the inner function.
This provides a sort of security for the variables of the inner function. Also, since the inner function has access to the scope of the outer function, the variables and functions defined in the outer function will live longer than the outer function itself, if the inner function manages to survive beyond the life of the outer function. A closure is created when the inner function is somehow made available to any scope outside the outer function.
Example:
In the code above, the name variable of the outer function is accessible to the inner functions, and there is no other way to access the inner variables except through the inner functions. The inner variables of the inner function act as safe stores for the inner functions. They hold "persistent", yet secure, data for the inner functions to work with. The functions do not even have to be assigned to a variable, or have a name.
read here for detail.
解释 JavaScript 中闭包的实际用途
当我们在另一个函数中创建一个函数时,我们正在创建一个闭包。闭包非常强大,因为它们能够读取和操作其外部函数的数据。每当调用函数时,都会为该调用创建一个新的作用域。函数内部声明的局部变量属于该作用域,并且只能从该函数访问它们。当函数执行完毕后,作用域通常会被销毁。
此类函数的一个简单示例如下:
在上面的示例中,函数 buildName() 声明了一个局部变量greeting 并返回它。每个函数调用都会创建一个带有新局部变量的新作用域。函数执行完毕后,我们无法再次引用该作用域,因此它会被垃圾收集。
但是当我们有到该范围的链接时怎么样呢?
让我们看看下一个函数:
此示例中的函数 sayName() 是一个闭包。 sayName() 函数有自己的本地作用域(带有变量welcome),并且还可以访问外部(封闭)函数的作用域。在本例中,是来自 buildName() 的变量greeting。
在这种情况下,执行完 buildName 后,范围不会被破坏。 sayMyName() 函数仍然可以访问它,因此它不会被垃圾收集。然而,除了闭包之外,没有其他方法可以从外部作用域访问数据。闭包充当全局上下文和外部作用域之间的网关。
Explaining the practical use for a closure in JavaScript
When we create a function inside another function, we are creating a closure. Closures are powerful because they are capable of reading and manipulating the data of its outer functions. Whenever a function is invoked, a new scope is created for that call. The local variable declared inside the function belong to that scope and they can only be accessed from that function. When the function has finished the execution, the scope is usually destroyed.
A simple example of such function is this:
In above example, the function buildName() declares a local variable greeting and returns it. Every function call creates a new scope with a new local variable. After the function is done executing, we have no way to refer to that scope again, so it’s garbage collected.
But how about when we have a link to that scope?
Let’s look at the next function:
The function sayName() from this example is a closure. The sayName() function has its own local scope (with variable welcome) and has also access to the outer (enclosing) function’s scope. In this case, the variable greeting from buildName().
After the execution of buildName is done, the scope is not destroyed in this case. The sayMyName() function still has access to it, so it won’t be garbage collected. However, there is no other way of accessing data from the outer scope except the closure. The closure serves as the gateway between the global context and the outer scope.
JavaScript 模块模式使用闭包。它的良好模式允许您拥有类似的“公共”和“私有”变量。
The JavaScript module pattern uses closures. Its nice pattern allows you to have something alike "public" and "private" variables.
我喜欢 Mozilla 的函数工厂示例。
I like Mozilla's function factory example.
这篇文章极大地帮助我更好地理解了闭包的工作原理。
从那以后,我自己做了一些实验,并提出了这个相当简单的代码,它可以帮助其他人了解如何以实际的方式使用闭包,以及如何在不同级别使用闭包来维护类似于 static 和/或全局变量,而不会有被覆盖或与全局变量混淆的风险。
这会跟踪按钮点击次数,无论是在本地级别还是全局级别,对每次按钮点击进行计数,从而得出一个数字。请注意,我没有使用任何全局变量来执行此操作,这就是练习的重点 - 拥有一个可以应用于任何对全局有贡献的按钮的处理程序。
请各位专家,如果我在这里犯了任何不好的做法,请告诉我!我自己还在学习这些东西。
This thread has helped me immensely in gaining a better understanding of how closures work.
I've since done some experimentation of my own and came up with this fairly simple code which may help some other people see how closures can be used in a practical way and how to use the closure at different levels to maintain variables similar to static and/or global variables without risk of them getting overwritten or confused with global variables.
This keeps track of button clicks, both at a local level for each individual button and a global level, counting every button click, contributing towards a single figure. Note I haven't used any global variables to do this, which is kind of the point of the exercise - having a handler that can be applied to any button that also contributes to something globally.
Please experts, do let me know if I've committed any bad practices here! I'm still learning this stuff myself.
闭包有多种用例。在这里,我将解释闭包概念的最重要用法。
创建index.html:
2)在index.js中:
注意:您可能想知道这段代码有什么特别之处。当您检查时,您会注意到无法使用 window 对象更改 count 的值。这意味着您已经声明了私有变量 count,这样可以防止您的状态被客户端破坏。
There are various use cases of closures.Here, I am going to explain most important usage of Closure concept.
Create index.html:
2)In index.js:
Note: You might be wondering what's special in this code. When you inspect, you will notice that you can't change the value of count using the window object. This means you have declared private variable count so this prevents your states from being spoiled by the client.
我不久前写了一篇关于如何使用闭包来简化事件处理代码的文章。它将 ASP.NET 事件处理与客户端 jQuery 进行了比较。
http://www.hackification.com/2009 /02/20/closures-simplify-event-handling-code/
I wrote an article a while back about how closures can be used to simplify event-handling code. It compares ASP.NET event handling to client-side jQuery.
http://www.hackification.com/2009/02/20/closures-simplify-event-handling-code/
小提琴
Fiddle
闭包是创建生成器(一个序列)的有用方法按需增加:
差异总结如下:
参考资料
Closures are a useful way to create generators, a sequence incremented on-demand:
The differences are summarized as follows:
References
我正在尝试学习闭包,我认为我创建的示例是一个实际用例。您可以运行一个代码片段并在控制台中查看结果。
我们有两个不同的用户,他们拥有不同的数据。他们每个人都可以看到实际状态并更新它。
I'm trying to learn closures and I think the example that I have created is a practical use case. You can run a snippet and see the result in the console.
We have two separate users who have separate data. Each of them can see the actual state and update it.
参考:闭包的实际用法
在实践中,闭包可以创建优雅的设计,允许自定义各种计算、延迟调用、回调、创建封装范围等。
一个例子是数组的排序方法,它接受排序条件函数作为参数:
将函数映射为数组的映射方法,通过函数参数的条件映射新数组:
通常,使用定义几乎无限搜索条件的函数参数来实现搜索函数很方便:
此外,我们可能会注意到应用函数,例如,应用函数的 forEach 方法到元素数组:
函数应用于参数(应用于参数列表 - 在 apply 中,以及定位参数 - 在调用中):
延迟调用:
回调函数:
创建封装范围以隐藏辅助对象:
Reference: Practical usage of closures
In practice, closures may create elegant designs, allowing customization of various calculations, deferred calls, callbacks, creating encapsulated scope, etc.
An example is the sort method of arrays which accepts the sort condition function as an argument:
Mapping functionals as the map method of arrays which maps a new array by the condition of the functional argument:
Often it is convenient to implement search functions with using functional arguments defining almost unlimited conditions for search:
Also, we may note applying functionals as, for example, a forEach method which applies a function to an array of elements:
A function is applied to arguments (to a list of arguments — in apply, and to positioned arguments — in call):
Deferred calls:
Callback functions:
Creation of an encapsulated scope for the purpose of hiding auxiliary objects:
在给定的示例中,封闭变量“计数器”的值受到保护,并且只能使用给定的函数(递增、递减)进行更改。因为它处于封闭状态,
In the given sample, the value of the enclosed variable 'counter' is protected and can be altered only using the given functions (increment, decrement). Because it is in a closure,
每个人都解释了闭包的实际用例:定义和几个示例。
我想贡献一个闭包用例列表:
Everyone has explained the practical use cases of closure: the definition and a couple of examples.
I want to contribute a list of use cases of Closures: