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

JavaScript中闭包的实际用途是什么?,javascript,closures,terminology,generators,Javascript,Closures,Terminology,Generators,我是最难理解JavaScript闭包的人 通过返回一个内部函数,它可以访问在其直接父函数中定义的任何变量 这在哪里对我有用?也许我还没有完全了解它。大多数代码都没有提供任何真实的代码,只是模糊的示例 有人能告诉我闭包的真实用法吗 例如,这是一个吗 var warnUser = function (msg) { var calledCount = 0; return function() { calledCount++; alert(msg + '\nY

我是最难理解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();

我使用闭包来完成以下操作:

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

    return {
        publicfunction : function () {
            privatefunction();
        }
    }
})();
如您所见,
a
现在是一个对象,其方法
publicfunction
a.publicfunction()
)调用
privatefunction
,该函数只存在于闭包内部。您可以直接调用
privatefunction
(即
a.privatefunction()
),只需
publicfunction()


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

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

我在堆栈溢出中看到的最常见的问题是,有人希望“延迟”使用在每个循环中增加的变量,但由于变量的作用域是有限的,因此对变量的每个引用都将在循环结束后进行,从而导致变量的结束状态:

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

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

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

  • 模拟面向对象编程:

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

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

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

    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];
        };
      }())
    };
    
    var dateUtil={
    weekdayShort:(函数(){
    var days=[‘周一’、‘周二’、‘周三’、‘周四’、‘周五’、‘周六’、‘太阳’];
    返回函数(x){
    if((x!=parseInt(x))| |(x<1)| |(x>7)){
    抛出新错误(“无效的工作日编号”);
    }
    返回天数[x-1];
    };
    }())
    };
    

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

    我不久前写了一篇文章,介绍了如何使用闭包简化事件处理代码。它将ASP.NET事件处理与客户端jQuery进行比较


    如果您熟悉在面向对象的意义上实例化一个类的概念(即创建该类的对象),那么您就接近理解闭包了

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

    我认为,warnUser函数(旁白:这是一个高阶函数)闭包返回的每个函数/闭包都将“calledCount”与相同的初始值(0)绑定在一起,这一事实稍微妨碍了您在概念上的飞跃,而在创建闭包时,将不同的初始值传递给高阶函数更有用,很像将不同的值传递给类的构造函数

    因此,假设当“calledCount”达到某个值时,您希望结束用户的会话;您可能需要不同的值,这取决于请求是来自本地网络还是大坏的互联网(是的,这是一个人为的例子)。要实现这一点,您可以将calledCount的不同初始值传递给warnUser(即-3或0?)


    文献中的部分问题是用来描述它们的术语(“词汇范围”、“自由变量”)。不要让它愚弄你,闭包比看起来更简单。。。表面证据;-)

    在上有一节。

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

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

    闭包也是高阶函数的重要组成部分,通过参数化不同部分,可以将多个类似函数重写为单个高阶函数。作为一个抽象的例子

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

    fooer = function (x) {
        return function (...) {A x B}
    }
    
    其中A和B不是语法单位,而是源代码字符串(不是字符串文字)


    有关具体示例,请参见“”。

    闭包是创建按需递增序列的有用方法:

    var foobar=function(i){var count=count | | i;return function(){return++count;}
    baz=foobar(1);
    log(“第一次调用:+baz());
    
    foo_a = function (...) {A a B}
    foo_b = function (...) {A b B}
    foo_c = function (...) {A c B}
    
    fooer = function (x) {
        return function (...) {A x B}
    }
    
    <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>
    
    function makeAdder(x) {
    
        return function(y) {
            return x + y;
        };
    }
    
    var addFive = makeAdder(5);
    
    console.assert(addFive(2) === 7);
    console.assert(addFive(-5) === 0);
    
    [1, 2, 3].sort(function (a, b) {
        ... // Sort conditions
    });
    
    [1, 2, 3].map(function (element) {
        return element * 2;
    }); // [2, 4, 6]
    
    someCollection.find(function (element) {
        return element.someProperty == 'searchCondition';
    });
    
    [1, 2, 3].forEach(function (element) {
        if (element % 2 != 0) {
            alert(element);
        }
    }); // 1, 3
    
    (function () {
        alert([].join.call(arguments, ';')); // 1;2;3
    }).apply(this, [1, 2, 3]);
    
    var a = 10;
    setTimeout(function () {
        alert(a); // 10, after one second
    }, 1000);
    
    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
    };
    
    var foo = {};
    (function (object) {
        var x = 10;
        object.getX = function _getX() {
            return x;
        };
    })(foo);
    
    alert(foo.getX()); // Get closured "x" – 10
    
    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);
        }
      };
    
    })();
    
    <button onclick="updateClickCount()">click me</button>
    
     var counter = 0;
    
     function updateClickCount() {
         ++counter;
         // Do something with counter
     }
    
     function updateClickCount() {
         var counter = 0;
         ++counter;
         // Do something with counter
     }
    
     function countWrapper() {
         var counter = 0;
         function updateClickCount() {
             ++counter;
             // Do something with counter
         }
         updateClickCount();
         return counter;
     }
    
     var updateClickCount = (function(){
         var counter = 0;
    
         return function(){
             ++counter;
             // Do something with counter
         }
     })();
    
    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");
    
    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();
    
    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;
    
    <!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>
    
    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)
        }
      }
    }
    
    const debounce = (func, delay) => {
      let debouncing
      return function() {
        const context = this
        const args = arguments
        clearTimeout(debouncing)
        debouncing = setTimeout(() => func.apply(context, args), delay)
      }
    }
    
    function buildName(name) {
        const greeting = "Hello, " + name;
        return greeting;
    }
    
    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
    
    
    <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>
    
    
      let count = 0;
      return () => {
        document.getElementById("first").innerHTML = count++;
      };
    })();