JavaScript 中闭包的实际用途是什么?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2728278/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 01:40:58  来源:igfitidea点击:

What is a practical use for a closure in JavaScript?

javascriptclosuresterminology

提问by alex

I'm tryingmy hardest to wrap my head around JavaScript closures.

我正在我最大的努力来解决 JavaScript 闭包问题。

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 onlinedon'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();

采纳答案by Francisco Soto

I've used closures to do things like:

我使用闭包来做类似的事情:

a = (function () {
    var privatefunction = function () {
        alert('hello');
    }

    return {
        publicfunction : function () {
            privatefunction();
        }
    }
})();

As you can see there, ais now an object, with a method publicfunction( a.publicfunction()) which calls privatefunction, which only exists inside the closure. You can NOTcall privatefunctiondirectly (i.e. a.privatefunction()), just publicfunction().

正如你在那里看到的,a现在是一个对象,带有一个调用的方法publicfunction( a.publicfunction()) privatefunction,它只存在于闭包中。您不能privatefunction直接调用(即a.privatefunction()),只调用publicfunction().

Its a minimal example but maybe you can see uses to it? We used this to enforce public/private methods.

它是一个最小的例子,但也许你可以看到它的用途?我们用它来强制执行公共/私有方法。

回答by JerryGoyal

Suppose, you want to count the number of times user clicked a buttonon a webpage.
For this, you are triggering a function on onclickevent of button to update the count of the variable

假设您想计算用户单击网页上按钮的次数
为此,您在onclick按钮事件时触发一个函数 来更新变量的计数

<button onclick="updateClickCount()">click me</button>  

Now there could be many approaches like:

现在可能有很多方法,例如:

1) You could use a global variable, and a function to increase the counter:

1)您可以使用全局变量和增加计数器的函数:

var counter = 0;

function updateClickCount() {
    ++counter;
    // do something with counter
}

But, the pitfall is that any script on the page can change the counter, without calling updateClickCount().

但是,缺陷在于页面上的任何脚本都可以更改计数器,而无需调用updateClickCount().



2) Now, You might be thinking of declaring the variable inside the function:

2)现在,您可能正在考虑在函数内声明变量:

function updateClickCount() {
    var counter = 0;
    ++counter;
    // do something with counter
}

But, Hey! Every time updateClickCount()function is called, the counter is set to 1 again.

但是,嘿!每次updateClickCount()调用函数时,计数器都会再次设置为 1。



3) Thinking about Nested functions?

3)考虑嵌套函数

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 function countWrapper()

嵌套函数可以访问它们“上方”的范围。
在这个例子中,内部函数updateClickCount()可以访问父函数中的计数器变量countWrapper()

function countWrapper() {
    var counter = 0;
    function updateClickCount() {
    ++counter;
    // do something with counter
    }
    updateClickCount();    
    return counter; 
}

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 execute counter = 0only once not everytime.

如果您可以updateClickCount()从外部访问该函数,并且您还需要找到一种counter = 0只执行一次而不是每次都执行的方法,那么这可以解决计数器的困境。



4) Closure to the rescue! (self-invoking function):

4)关闭救援!(自调用函数)

 var updateClickCount=(function(){
    var counter=0;

    return function(){
     ++counter;
     // do something with counter
    }
})();

The self-invoking function only runs once. It sets the counterto zero (0), and returns a function expression.

自调用函数只运行一次。它将 设置counter为零 (0),并返回一个函数表达式。

This way updateClickCountbecomes a function. The "wonderful" part is that it can access the counter in the parent scope.

这种方式updateClickCount成为一个功能。“精彩”的部分是它可以访问父作用域中的计数器。

This is called a JavaScript closure. It makes it possible for a function to have "private" variables.

这称为JavaScript 闭包。它使函数可以拥有“私有”变量。

The counteris protected by the scope of the anonymous function, and can only be changed using the add function!

counter是由匿名函数的范围内保护,并且只能使用add函数来改变!

More lively example on Closure:

Closure 上更生动的例子:

<script>
        var updateClickCount=(function(){
     var counter=0;
    
     return function(){
     ++counter;
      document.getElementById("spnCount").innerHTML=counter;
     }
      })();
    </script>

    <html>
  <button onclick="updateClickCount()">click me</button>
   <div> you've clicked 
  <span id="spnCount"> 0 </span> times!
  </div>
    </html>

回答by Marcelo Cantos

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:

你举的例子是一个很好的例子。闭包是一种抽象机制,允许您非常干净地分离关注点。您的示例是将检测(计数调用)与语义(错误报告 API)分离的情况。其他用途包括:

  1. Passing parameterised behaviour into an algorithm (classic higher-order programming):

    function proximity_sort(arr, midpoint) {
        arr.sort(function(a, b) { a -= midpoint; b -= midpoint; return a*a - b*b; });
    }
    
  2. Simulating object oriented programming:

    function counter() {
        var a = 0;
        return {
            inc: function() { ++a; },
            dec: function() { --a; },
            get: function() { return a; },
            reset: function() { a = 0; }
        }
    }
    
  3. Implementing exotic flow control, such as jQuery's Event handling and AJAX APIs.

  1. 将参数化行为传递给算法(经典的高阶编程):

    function proximity_sort(arr, midpoint) {
        arr.sort(function(a, b) { a -= midpoint; b -= midpoint; return a*a - b*b; });
    }
    
  2. 模拟面向对象编程:

    function counter() {
        var a = 0;
        return {
            inc: function() { ++a; },
            dec: function() { --a; },
            get: function() { return a; },
            reset: function() { a = 0; }
        }
    }
    
  3. 实现奇特的流控制,例如 jQuery 的事件处理和 AJAX API。

回答by Mohd Asim Suhail

I know i am super late in answering this question but it might help anyone still searching for the answer in 2018.

我知道我回答这个问题太晚了,但它可能会帮助任何仍在 2018 年寻找答案的人。

Javascript closures can be used to implement throttleand debouncefunctionality in your application.

Javascript 闭包可用于在您的应用程序中实现节流去抖动功能。

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."

节流限制了一个函数在一段时间内可以被调用的最大次数。如“最多每 100 毫秒执行一次此函数”。

Code :

代码 :

const throttle = (func, limit) => {
  let isThrottling
  return function() {
    const args = arguments
    const context = this
    if (!isThrottling) {
      func.apply(context, args)
      isThrottling = true
      setTimeout(() => isThrottling = false, limit)
    }
  }
}

Debouncing:

去抖动

Debouncing put 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."

去抖动限制了一个函数不会被再次调用,直到经过一定的时间而没有被调用。如“仅当 100 毫秒过去了而没有被调用时才执行此函数”。

Code:

代码:

const debounce = (func, delay) => {
  let debouncing
  return function() {
    const context = this
    const args = arguments
    clearTimeout(debouncing)
    debouncing = setTimeout(() => func.apply(context, args), delay)
  }
}

As you can see closures helped in implementing two beautiful features which every web app should have to provide smooth UI experience functionality.

正如你所看到的,闭包有助于实现两个漂亮的特性,每个 Web 应用程序都应该提供这些特性,以提供流畅的 UI 体验功能。

I hope it will help someone.

我希望它会帮助某人。

回答by Andy E

Yes, that is a good example of a useful closure. The call to warnUser creates the calledCountvariable in its scope and returns an anonymous function which is stored in the warnForTampervariable. 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 the warnForTamper()will increase the scoped variable and alert the value.

是的,这是一个有用的闭包的好例子。对 warnUser 的调用calledCount在其范围内创建变量并返回存储在该warnForTamper变量中的匿名函数。因为仍然有一个闭包使用了 calledCount 变量,它不会在函数退出时被删除,所以每次调用warnForTamper()都会增加作用域变量并警告值。

The most common issue I see on StackOverflow 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:

我在 StackOverflow 上看到的最常见的问题是有人想要“延迟”使用在每个循环中增加的变量,但是因为变量是有范围的,所以对变量的每个引用都将在循环结束之后,导致变量的结束状态:

for (var i = 0; i < someVar.length; i++)
    window.setTimeout(function () { 
        alert("Value of i was "+i+" when this timer was set" )
    }, 10000);

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:

这将导致每个警报显示相同的值i,即循环结束时增加的值。解决方案是创建一个新的闭包,一个单独的变量作用域。这可以使用立即执行的匿名函数来完成,该函数接收变量并将其状态存储为参数:

for (var i = 0; i < someVar.length; i++)
    (function (i) {
        window.setTimeout(function () { 
            alert("Value of i was "+i+" when this timer was set" )
        }, 10000);
    })(i); 

回答by maerics

In the JavaScript (or any ECMAScript) language, in particular, closures are useful in hiding the implementation of functionality while still revealing the interface.

特别是在 JavaScript(或任何 ECMAScript)语言中,闭包在隐藏功能实现的同时仍然显示接口很有用。

For example, imagine you are writing a class of date utility methods and you want to allow users to lookup weekday names by index but you don't want them to be able to modify the array of names you use under the hood.

例如,假设您正在编写一类日期实用程序方法,并且您希望允许用户通过索引查找工作日名称,但您不希望他们能够修改您在后台使用的名称数组。

var dateUtil = {
  weekdayShort: (function() {
    var days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
    return function(x) {
      if ((x != parseInt(x)) || (x < 1) || (x > 7)) {
        throw new Error("invalid weekday number");
      }
      return days[x - 1];
    };
  }())
};

Note that the daysarray could simply be stored as a property of the dateUtilobject 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 tamper-proof.

请注意,days数组可以简单地存储为dateUtil对象的属性,但脚本用户可以看到它,他们甚至可以根据需要更改它,甚至不需要您的源代码。但是,由于它被包含在返回日期查找函数的匿名函数中,因此它只能由查找函数访问,因此它现在是防篡改的。

回答by alex

回答by outis

Another common use for closures is to bind thisin a method to a specific object, allowing it to be called elsewhere (such as as an event handler).

闭包的另一个常见用途是将this方法绑定到特定对象,允许在其他地方调用它(例如事件处理程序)。

function bind(obj, method) {
    if (typeof method == 'string') {
        method = obj[method];
    }
    return function () {
        method.apply(obj, arguments);
    }
}
...
document.body.addEventListener('mousemove', bind(watcher, 'follow'), true);

Whenever a mousemove event fires, watcher.follow(evt)is called.

每当 mousemove 事件触发时,watcher.follow(evt)都会调用。

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,

闭包也是高阶函数的重要组成部分,通过参数化不同的部分,允许将多个相似函数重写为单个高阶函数的非常常见的模式。作为一个抽象的例子,

foo_a = function (...) {A a B}
foo_b = function (...) {A b B}
foo_c = function (...) {A c B}

becomes

变成

fooer = function (x) {
    return function (...) {A x B}
}

where A and B aren't syntactical units but source code strings (not string literals).

其中 A 和 B 不是句法单元而是源代码字符串(不是字符串文字)。

See "Streamlining my javascript with a function" for a concrete example.

有关具体示例,请参阅“使用函数简化我的 javascript”。

回答by Luke Schlangen

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/):

没有关闭(https://jsfiddle.net/lukeschlangen/pw61qrow/3/):

function greeting(firstName, lastName) {
  var message = "Hello " + firstName + " " + lastName + "!";
  console.log(message);
}

greeting("Billy", "Bob");
greeting("Billy", "Bob");
greeting("Billy", "Bob");
greeting("Luke", "Schlangen");
greeting("Luke", "Schlangen");
greeting("Luke", "Schlangen");

With a closure (https://jsfiddle.net/lukeschlangen/Lb5cfve9/3/):

关闭(https://jsfiddle.net/lukeschlangen/Lb5cfve9/3/):

function greeting(firstName, lastName) {
  var message = "Hello " + firstName + " " + lastName + "!";

  return function() {
    console.log(message);
  }
}

var greetingBilly = greeting("Billy", "Bob");
var greetingLuke = greeting("Luke", "Schlangen");

greetingBilly();
greetingBilly();
greetingBilly();
greetingLuke();
greetingLuke();
greetingLuke();

回答by EdwardGarson

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.

可以这样想:当你实例化两个 Person 对象时,你知道类成员变量“Name”在实例之间是不共享的;每个对象都有自己的“副本”。类似地,当您创建一个闭包时,自由变量(在上面的示例中为“callCount”)绑定到函数的“实例”。

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.

我认为您的概念上的飞跃受到了warnUser 函数返回的每个函数/闭包(除此之外:这是一个高阶函数)闭包绑定具有相同初始值 (0)的“调用计数”这一事实的轻微阻碍,而通常在创建闭包时将不同的初始化器传递给高阶函数更有用,就像将不同的值传递给类的构造函数一样。

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?).

因此,假设当 'CalledCount' 达到某个值时,您想结束用户的会话;您可能需要不同的值,具体取决于请求是来自本地网络还是糟糕的互联网(是的,这是一个人为的例子)。为了实现这一点,您可以将 calledCount 的不同初始值传递给 warnUser(即 -3 或 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 ;-)

文献的部分问题是用于描述它们的命名法(“词法范围”、“自由变量”)。不要让它愚弄你,闭包比看起来更简单......表面上看;-)