我正在尽最大努力理解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();

当前回答

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

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

这将导致每个警报都显示相同的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);

其他回答

这个帖子极大地帮助我更好地理解闭包是如何工作的。

从那以后,我自己做了一些实验,并提出了这个相当简单的代码,它可能会帮助其他人了解如何以实际的方式使用闭包,以及如何在不同的级别上使用闭包来维护类似于静态和/或全局变量的变量,而不会有它们被覆盖或与全局变量混淆的风险。

这将跟踪按钮点击,无论是在本地级别上的每个按钮,还是在全局级别上的每个按钮,计算每个按钮的点击,为单个数字做出贡献。请注意,我没有使用任何全局变量来完成此操作,这是练习的要点—拥有一个可以应用于任何按钮的处理程序,该按钮也对全局具有贡献。

专家们,如果我在这里犯了什么错误,请告诉我!我自己也在学习这些东西。

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

我正在尝试学习闭包,我认为我创建的示例是一个实际的用例。您可以运行一个代码片段并在控制台中查看结果。

我们有两个不同的用户,他们拥有不同的数据。它们中的每一个都可以看到实际的状态并进行更新。

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

闭包有各种各样的用例。在这里,我将解释闭包概念的最重要的用法。

闭包可以用来创建私有方法和变量,就像面向对象的语言,如java、c++等。一旦你实现了私有方法和变量,你在函数中定义的变量将不能被窗口对象访问。这有助于数据隐藏和数据安全。

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

另一个关于闭包的现实例子:

创建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)在index.js:

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

在本例中,当您单击一个按钮时,计数将在p#id上更新。 注意:您可能想知道这段代码有什么特别之处。检查时,您将注意到不能使用window对象更改count的值。这意味着你已经声明了私有变量count,这样可以防止你的状态被客户端破坏。

参考:闭包的实际使用

在实践中,闭包可以创建优雅的设计,允许自定义各种计算、延迟调用、回调、创建封装的作用域等。

一个例子是数组的sort方法,它接受sort条件函数作为参数:

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

将函数函数映射为数组的map方法,它根据函数参数的条件映射一个新数组:

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

通常,通过使用函数参数定义几乎无限的搜索条件来实现搜索函数是很方便的:

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

此外,我们可能会注意到应用函数,例如,forEach方法将函数应用于元素数组:

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

函数应用于实参(在apply中应用于实参列表,在call中应用于定位实参):

(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

这里我有一个闭包概念的简单例子,我们可以在我们的电子商务网站或其他许多网站上使用它。

我正在添加示例的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>