What is a practical use for a closure in JavaScrip

2019-01-03 07:28发布

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

20条回答
老娘就宠你
2楼-- · 2019-01-03 08:03

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 the sort method of arrays which accepts as an argument the sort-condition function:

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

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

[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
查看更多
爱情/是我丢掉的垃圾
3楼-- · 2019-01-03 08:03

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

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


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


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

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.


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

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 the add function!

More lively example on 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>
查看更多
beautiful°
4楼-- · 2019-01-03 08:03
乱世女痞
5楼-- · 2019-01-03 08:07

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,

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

查看更多
成全新的幸福
6楼-- · 2019-01-03 08:07

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

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.

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

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

查看更多
劳资没心,怎么记你
7楼-- · 2019-01-03 08:08

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

查看更多
登录 后发表回答