简体   繁体   English

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

[英]What is a practical use for a closure in JavaScript?

I'm trying my 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.我通过返回一个内部 function 来得到它,它将可以访问其直接父级中定义的任何变量。

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

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 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 :您可以使用一个全局变量和一个函数来增加计数器

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


  1. Now, you might be thinking of declaring the variable inside the function:现在,您可能正在考虑在函数内声明变量:

     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。


  1. 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 function countWrapper() :在这个例子中,内部函数updateClickCount()可以访问父函数countWrapper()的 counter 变量:

     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 = 0 only once not everytime.如果您可以从外部访问updateClickCount()函数,并且您还需要找到一种方法,而不是每次都只执行一次counter = 0 ,那么这可以解决计数器的困境。


  1. Closure to the rescue!关闭救援! (self-invoking function) : (自调用函数)

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

    The self-invoking function only runs once.自调用函数只运行一次。 It sets the counter to zero (0), and returns a function expression.它将counter设置为零 (0),并返回一个函数表达式。

    This way updateClickCount becomes a function.这样updateClickCount就变成了一个函数。 The "wonderful" part is that it can access the counter in the parent scope. “精彩”的部分是它可以访问父作用域中的计数器。

    This is called a JavaScript closure .这称为JavaScript 闭包 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 the add function! counter受匿名函数作用域保护,只能使用 add 函数更改!

A more lively example on closures关于闭包的一个更生动的例子

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


Reference: JavaScript Closures参考: JavaScript 闭包

I've used closures to do things like:我使用闭包来做类似的事情:

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

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

As you can see there, a is now an object, with a method publicfunction ( a.publicfunction() ) which calls privatefunction , which only exists inside the closure.正如你在那里看到的, a现在是一个对象,有一个方法publicfunction ( a.publicfunction() ) 调用privatefunction ,它只存在于闭包中。 You can not call privatefunction directly (ie a.privatefunction() ), just publicfunction() .不能直接调用privatefunction (即a.privatefunction() ),只能publicfunction()

It's a minimal example, but maybe you can see uses to it?这是一个最小的例子,但也许你可以看到它的用途? We used this to enforce public/private methods.我们用它来强制执行公共/私有方法。

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).您的示例是将检测(计数调用)与语义(错误报告 API)分离的情况。 Other uses include:其他用途包括:

  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.实现奇特的流控制,例如 jQuery 的事件处理和 AJAX API。

JavaScript closures can be used to implement throttle and debounce functionality 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 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."如“仅当 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 application should have to provide smooth UI experience functionality.正如你所看到的,闭包有助于实现两个漂亮的特性,每个 Web 应用程序都应该提供这些特性来提供流畅的 UI 体验功能。

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 the warnForTamper variable.到warnUser呼叫创建calledCount在其范围内可变,并返回其被存储在匿名函数warnForTamper变量。 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.因为仍然有一个闭包使用了 calledCount 变量,它不会在函数退出时被删除,所以每次调用warnForTamper()都会增加作用域变量并警告值。

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:我在 Stack Overflow 上看到的最常见的问题是有人想要“延迟”使用在每个循环中增加的变量,但是因为变量是有作用域的,所以对变量的每次引用都会在循环结束之后,导致变量的结束状态:

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.这将导致每个警报显示相同的i值,该值在循环结束时增加到。 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:这可以使用立即执行的匿名函数来完成,该函数接收变量并将其状态存储为参数:

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

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

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 days array could simply be stored as a property of the dateUtil 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.请注意, days数组可以简单地存储为dateUtil对象的属性,但是脚本的用户可以看到它,他们甚至可以根据需要更改它,甚至不需要您的源代码。 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.但是,由于它被包含在返回日期查找函数的匿名函数中,因此它只能由查找函数访问,因此它现在是防篡改的。

If you're comfortable with the concept of instantiating a class in the object-oriented sense (ie 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;可以这样想:当你实例化两个 Person 对象时,你知道类成员变量“Name”在实例之间是不共享的; 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.类似地,当您创建一个闭包时,自由变量(在上面的示例中为“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;因此,假设当 'CalledCount' 达到某个值时,您想结束用户的会话; 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 (ie -3, or 0?).为了实现这一点,您可以将 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 ;-)不要让它愚弄你,闭包比看起来更简单......表面上看;-)

Mozilla 开发者网络中有一个关于实用闭包的部分。

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

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).闭包的另一个常见用途是在方法中将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)调用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 ”。

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.我在示例中添加了我的 JSFiddle 链接。 It contains a small product list of three items and one cart counter.它包含一个包含三件商品和一个购物车柜台的小型产品清单。

JSFiddle JSFiddle

 // Counter closure implemented function; var CartCouter = function(){ var counter = 0; function changeCounter(val){ counter += val } return { increment: function(){ changeCounter(1); }, decrement: function(){ changeCounter(-1); }, value: function(){ return counter; } } } var cartCount = CartCouter(); function updateCart() { document.getElementById('cartcount').innerHTML = cartCount.value(); } var productlist = document.getElementsByClassName('item'); for(var i = 0; i< productlist.length; i++){ productlist[i].addEventListener('click', function(){ if(this.className.indexOf('selected') < 0){ this.className += " selected"; cartCount.increment(); updateCart(); } else{ this.className = this.className.replace("selected", ""); cartCount.decrement(); updateCart(); } }) }
 .productslist{ padding: 10px; } ul li{ display: inline-block; padding: 5px; border: 1px solid #DDD; text-align: center; width: 25%; cursor: pointer; } .selected{ background-color: #7CFEF0; color: #333; } .cartdiv{ position: relative; float: right; padding: 5px; box-sizing: border-box; border: 1px solid #F1F1F1; }
 <div> <h3> Practical use of a JavaScript closure concept/private variable. </h3> <div class="cartdiv"> <span id="cartcount">0</span> </div> <div class="productslist"> <ul> <li class="item">Product 1</li> <li class="item">Product 2</li> <li class="item">Product 3</li> </ul> </div> </div>

Use of Closures:闭包的使用:

Closures are one of the most powerful features of JavaScript.闭包是 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). JavaScript 允许函数嵌套,并授予内部函数对外部函数内部定义的所有变量和函数(以及外部函数可以访问的所有其他变量和函数)的完全访问权限。 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:例子:

<script>
var createPet = function(name) {
  var sex;
  
  return {
    setName: function(newName) {
      name = newName;
    },
    
    getName: function() {
      return name;
    },
    
    getSex: function() {
      return sex;
    },
    
    setSex: function(newSex) {
      if(typeof newSex == "string" && (newSex.toLowerCase() == "male" || newSex.toLowerCase() == "female")) {
        sex = newSex;
      }
    }
  }
}

var pet = createPet("Vivie");
console.log(pet.getName());                  // Vivie

console.log(pet.setName("Oliver"));   
console.log(pet.setSex("male"));
console.log(pet.getSex());                   // male
console.log(pet.getName());                  // Oliver
</script>

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.上面的代码中,外层函数的name变量对内层函数是可以访问的,除了通过内层函数,没有其他方式可以访问到内层变量。 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.阅读此处了解详细信息。

I like Mozilla's function factory example .我喜欢 Mozilla 的函数工厂示例

function makeAdder(x) {

    return function(y) {
        return x + y;
    };
}

var addFive = makeAdder(5);

console.assert(addFive(2) === 7);
console.assert(addFive(-5) === 0);

The JavaScript module pattern uses closures. JavaScript 模块模式使用闭包。 Its nice pattern allows you to have something alike "public" and "private" variables.它的漂亮模式允许您拥有类似“公共”和“私有”变量的东西。

var myNamespace = (function () {

  var myPrivateVar, myPrivateMethod;

  // A private counter variable
  myPrivateVar = 0;

  // A private function which logs any arguments
  myPrivateMethod = function(foo) {
      console.log(foo);
  };

  return {

    // A public variable
    myPublicVar: "foo",

    // A public function utilizing privates
    myPublicFunction: function(bar) {

      // Increment our private counter
      myPrivateVar++;

      // Call our private method using bar
      myPrivateMethod(bar);
    }
  };

})();

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.我自己还在学习这些东西。

<!doctype html>
<html>

<head>
    <meta charset="utf-8">
    <title>Closures on button presses</title>

    <script type="text/javascript">

        window.addEventListener("load" , function () {
            /*
                Grab the function from the first closure,
                and assign to a temporary variable
                this will set the totalButtonCount variable
                that is used to count the total of all button clicks
            */
            var buttonHandler = buttonsCount();

            /*
                Using the result from the first closure (a function is returned)
                assign and run the sub closure that carries the
                individual variable for button count and assign to the click handlers
            */
            document.getElementById("button1").addEventListener("click" , buttonHandler() );
            document.getElementById("button2").addEventListener("click" , buttonHandler() );
            document.getElementById("button3").addEventListener("click" , buttonHandler() );

            // Now that buttonHandler has served its purpose it can be deleted if needs be
            buttonHandler = null;
        });


        function buttonsCount() {
            /*
                First closure level
                - totalButtonCount acts as a sort of global counter to count any button presses
            */
            var totalButtonCount = 0;

            return  function () {
                // Second closure level
                var myButtonCount = 0;

                return function (event) {
                    // Actual function that is called on the button click
                    event.preventDefault();
                    /*
                       Increment the button counts.
                       myButtonCount only exists in the scope that is
                       applied to each event handler and therefore acts
                       to count each button individually, whereas because
                       of the first closure totalButtonCount exists at
                       the scope just outside, it maintains a sort
                       of static or global variable state
                    */

                    totalButtonCount++;
                    myButtonCount++;

                    /*
                        Do something with the values ... fairly pointless
                        but it shows that each button contributes to both
                        its own variable and the outer variable in the
                        first closure
                    */
                    console.log("Total button clicks: "+totalButtonCount);
                    console.log("This button count: "+myButtonCount);
                }
            }
        }
    </script>
</head>

<body>
    <a href="#" id="button1">Button 1</a>
    <a href="#" id="button2">Button 2</a>
    <a href="#" id="button3">Button 3</a>
</body>

</html>

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.它将 ASP.NET 事件处理与客户端 jQuery 进行了比较。

http://www.hackification.com/2009/02/20/closures-simplify-event-handling-code/ http://www.hackification.com/2009/02/20/closures-simplify-event-handling-code/

Explaining the practical use for a closure in JavaScript解释 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:此类函数的一个简单示例如下:

function buildName(name) {
    const greeting = "Hello, " + name;
    return greeting;
}

In above example, the function buildName() declares a local variable greeting and returns it.在上面的例子中,函数 buildName() 声明了一个局部变量 greeting 并返回它。 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:让我们看看下一个函数:

 function buildName(name) { const greeting = "Hello, " + name + " Welcome "; const sayName = function() { console.log(greeting); }; return sayName; } const sayMyName = buildName("Mandeep"); sayMyName(); // Hello, Mandeep Welcome

The function sayName() from this example is a closure.这个例子中的函数 sayName() 是一个闭包。 The sayName() function has its own local scope (with variable welcome) and has also access to the outer (enclosing) function's scope. sayName() 函数有它自己的局部作用域(带有变量welcome)并且还可以访问外部(封闭)函数的作用域。 In this case, the variable greeting from buildName().在这种情况下,来自 buildName() 的变量 greeting。

After the execution of buildName is done, the scope is not destroyed in this case.在 buildName 执行完成后,在这种情况下不会破坏作用域。 The sayMyName() function still has access to it, so it won't be garbage collected. sayMyName() 函数仍然可以访问它,因此它不会被垃圾收集。 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.闭包充当全局上下文和外部作用域之间的网关。

There are various use cases of closures.Here, I am going to explain most important usage of Closure concept.闭包有多种用例。这里,我将解释闭包概念的最重要用法。

  • Closure can be used to create private methods and variables just like an object-oriented language like java, c++ and so on.闭包可用于创建私有方法和变量,就像 Java、c++ 等面向对象的语言一样。 Once you implemented private methods and variables, your variables defined inside a function won't be accessible by window object.一旦你实现了私有方法和变量,你在函数中定义的变量就不能被 window 对象访问。 This helps in data hiding and data security.这有助于数据隐藏和数据安全。
const privateClass = () => {
  let name = "sundar";
  function setName(changeName) {
    name = changeName;
  }
  function getName() {
    return name;
  }
  return {
    setName: setName,
    getName: getName,
  };
};

let javaLikeObject = privateClass(); \\ similar to new Class() in OOPS.

console.log(javaLikeObject.getName()); \\this will give sundar
javaLikeObject.setName("suresh");
console.log(javaLikeObject.getName()); \\this will give suresh

  • Another real-life example of closure :另一个真实的闭包例子:

Create index.html:创建 index.html:

<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Program with Javascript</title>
  </head>
  <body>
    <p id="first"></p>
    <p id="second"></p>
    <button onclick="applyingConcepts()">Click</button>
    <script src="./index.js"></script>
  </body>
</html>

2)In index.js: 2)在 index.js 中:

  let count = 0;
  return () => {
    document.getElementById("first").innerHTML = count++;
  };
})();

  • In this example, when you click a button, then your count will be updated on p#id.在此示例中,当您单击按钮时,您的计数将在 p#id 上更新。 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.当您检查时,您会注意到您无法使用 window 对象更改 count 的值。 This means you have declared private variable count so this prevents your states from being spoiled by the client.这意味着您已经声明了私有变量 count,这样可以防止您的状态被客户端破坏。

Closures are a useful way to create , a sequence incremented on-demand:闭包是一种创建的有用方法,一个按需递增的序列:

 var foobar = function(i){var count = count || i; return function(){return ++count;}} baz = foobar(1); console.log("first call: " + baz()); //2 console.log("second call: " + baz()); //3

The differences are summarized as follows:差异总结如下:

Anonymous functions                                    Defined functions

Cannot be used as a method                             Can be used as a method of an object

Exists only in the scope in which it is defined        Exists within the object it is defined in

Can only be called in the scope in which it is defined Can be called at any point in the code

Can be reassigned a new value or deleted               Cannot be deleted or changed

References参考

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:一个例子是数组的排序方法,它接受排序条件函数作为参数:

[1, 2, 3].sort(function (a, b) {
    ... // Sort conditions
});

Mapping functionals as the map method of arrays which maps a new array by the condition of the functional argument:将泛函映射为数组的 map 方法,它根据功能参数的条件映射新数组:

[1, 2, 3].map(function (element) {
    return element * 2;
}); // [2, 4, 6]

Often it is convenient to implement search functions with using functional arguments defining almost unlimited conditions for search:通常,使用定义几乎无限搜索条件的函数参数来实现搜索函数是很方便的:

someCollection.find(function (element) {
    return element.someProperty == 'searchCondition';
});

Also, we may note applying functionals as, for example, a forEach method which applies a function to an array of elements:此外,我们可能会注意到应用函数,例如,将函数应用于元素数组的 forEach 方法:

[1, 2, 3].forEach(function (element) {
    if (element % 2 != 0) {
        alert(element);
    }
}); // 1, 3

A function is applied to arguments (to a list of arguments — in apply, and to positioned arguments — in call):一个函数应用于参数(应用于参数列表——在应用中,以及定位参数——在调用中):

(function () {
    alert([].join.call(arguments, ';')); // 1;2;3
}).apply(this, [1, 2, 3]);

Deferred calls:延迟调用:

var a = 10;
setTimeout(function () {
    alert(a); // 10, after one second
}, 1000);

Callback functions:回调函数:

var x = 10;
// Only for example
xmlHttpRequestObject.onreadystatechange = function () {
    // Callback, which will be called deferral ,
    // when data will be ready;
    // variable "x" here is available,
    // regardless that context in which,
    // it was created already finished
    alert(x); // 10
};

Creation of an encapsulated scope for the purpose of hiding auxiliary objects:创建封装范围以隐藏辅助对象:

var foo = {};
(function (object) {
    var x = 10;
    object.getX = function _getX() {
        return x;
    };
})(foo);

alert(foo.getX()); // Get closured "x" – 10

Much of the code we write in front-end JavaScript is event-based — we define some behavior, then attach it to an event that is triggered by the user (such as a click or a keypress).我们在前端 JavaScript 中编写的大部分代码都是基于事件的——我们定义一些行为,然后将其附加到由用户触发的事件(例如点击或按键)。 Our code is generally attached as a callback: a single function which is executed in response to the event.我们的代码通常作为回调附加:响应事件而执行的单个函数。 size12, size14, and size16 are now functions which will resize the body text to 12, 14, and 16 pixels, respectively. size12、size14 和 size16 现在是将正文文本大小分别调整为 12、14 和 16 像素的函数。 We can attach them to buttons (in this case links) as follows:我们可以将它们附加到按钮(在本例中为链接),如下所示:

function makeSizer(size) {
    return function() {
    document.body.style.fontSize = size + 'px';
    };
}

var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);

document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;

Fiddle 小提琴

In the given sample, the value of the enclosed variable 'counter' is protected and can be altered only using the given functions (increment, decrement).在给定的示例中,封闭变量“counter”的值受到保护,只能使用给定的函数(增量、减量)进行更改。 Because it is in a closure,因为是封闭的,

 var MyCounter = function (){ var counter = 0; return { increment:function () {return counter += 1;}, decrement:function () {return counter -= 1;}, get:function () {return counter;} }; }; var x = MyCounter(); // Or var y = MyCounter(); alert(x.get()); // 0 alert(x.increment()); // 1 alert(x.increment()); // 2 alert(y.increment()); // 1 alert(x.get()); // x is still 2

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.他们每个人都可以看到实际状态并更新它。

 function createUserWarningData(user) { const data = { name: user, numberOfWarnings: 0, }; function addWarning() { data.numberOfWarnings = data.numberOfWarnings + 1; } function getUserData() { console.log(data); return data; } return { getUserData: getUserData, addWarning: addWarning, }; } const user1 = createUserWarningData("Thomas"); const user2 = createUserWarningData("Alex"); //USER 1 user1.getUserData(); // Returning data user object user1.addWarning(); // Add one warning to specific user user1.getUserData(); // Returning data user object //USER2 user2.getUserData(); // Returning data user object user2.addWarning(); // Add one warning to specific user user2.addWarning(); // Add one warning to specific user user2.getUserData(); // Returning data user object

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:我想提供一个闭包用例列表:

  • suppose you want to count no of times a button is clicked;假设你想计算一个按钮被点击的次数; Closure is the best choice.封闭是最好的选择。
  • Throttling and Debounce节流和去抖动
  • Currying咖喱
  • Memorize记住
  • Maintaining state in the async world在异步世界中维护 state
  • Functions like once类似一次的功能
  • setTimeouts设置超时
  • Iterators迭代器

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM