Good examples for using a Closure in Javascript

一笑奈何 提交于 2021-02-04 11:41:08

问题


Well, I recently learned about closures in Javascript.

While i find it's concept truly amazing, i have yet to find a good application for them, myself.

In all the blog posts, all the tuturials i found, i got a good explanation of what they are and how to work with them.

What i can't find anywhere are examples that make me think: "Wow! you can do THIS with closures? Awesome!!!". All the examples i find are purely academic like this one.

function say667() {
  // Local variable that ends up within closure
  var num = 666;
  var sayAlert = function() { alert(num); }
  num++;
  return sayAlert;
}

var sayNumber = say667();
alert(sayNumber());

So, i was wondering if any of you can share some mind-blowing experiences with these special kind of functions.

I know that this is sort of an open question, but i will attribute the answer to whoever makes me WOW the most.

Thanks


回答1:


Currying variables

Since "closure" is just a way of saying that a function always retains its original variable scope, there are many ways you can take advantage of that.

Currying seems to be something that people like.


Creating curried value manipulators

Here I've created a function curry that will return a function that will be used to generate new functions that work with the original curried value.

function curry() {
    var args = Array.prototype.slice.call(arguments);
    return function(fn) {
        return function() {
            var args2 = Array.prototype.slice.call(arguments);
            return fn.apply(this,args.concat(args2));
        };
    };
}

Creating a function that curries a string

So if I want to create functions to work with a string, I could curry the string...

var workWithName = curry("Bubba");

...and use the function returned to create new functions that work with the given string in various ways.


Create a function that puts the curried string in a sentence

Here I create a talkToName function that will incorporate the name into various sentences based on the arguments passed...

var talkToName = workWithName(function(curried_str, before, after) {
    return before + curried_str + after;
});

So now I have a talkToName function that accepts 2 strings that wrap the curried string.

talkToName("Hello there ", ". How are you?"); // "Hello there Bubba. How are you?"
talkToName("", " is really super awesome.");  // "Bubba is really super awesome."

Notice that I pass two arguments to the talkToName function, but the function given to workWithName accepts 3 arguments.

The first argument is passed by the function we created from workWithName(), and the two arguments we give to talkToName are added after the original curried argument.


Create a function increments the characters of the curried string

Here I create an entirely different function using the original workWithName function that will take the "Bubba" string, and return a string with the letters incremented by the given value...

var incrementName = workWithName(function(curried_str, n) {
    var ret = '';
    for(var i = 0; i < curried_str.length; i++) {
        ret += String.fromCharCode(curried_str[i].charCodeAt() + n);
    }
    return ret;
});

So I pass my new incrementName function a number, and it increments the letters in the name, and returns the new string...

incrementName(3);  // "Exeed"
incrementName(8);  // "J}jji"
incrementName(0);  // "Bubba"

So you can see that we gave curry() a value, and it gave us back a function that can be used to create new functions that work with the original value.

Notice again that I pass one argument to the incrementName function, but the function given to workWithName accepts 2 arguments. The first argument is curried.


Other examples with numbers

Here's an example that creates a function generator that works with the numbers 3 and 5.

var workWith3And5 = curry(3, 5);

Create functions that do various things with the curried numbers

So using the workWith3And5 function, we make a new function that will accept a number argument, and return an Array of the sums of the curried numbers with the given number...

var addNTo3And5 = workWith3And5(function(x, y, n) {
    return [3 + n, 5 + n];
});

addNTo3And5( 8 );  // [11, 13];
addNTo3And5( -4 ); // [-1, 1];

And another one using the same workWith3And5 function that curries the numbers 3 and 5 that creates a 3 x 5 Array of Arrays, where the nested Array is given some content...

var create3By5GridWithData = workWith3And5(function(x, y, data) {
    var ret = []
    for(var i = 0; i < x; i++) {
        ret[i] = [];
        for(var j = 0; j < y; j++) {
           ret[i][j] = data;
        }
    }
    return ret;
});

create3By5GridWithData( 'content' ); // [Array[5], Array[5], Array[5]]



回答2:


Closures are used all the time with callback functions that are called some time later because they allow you to access the local variables of the host calling function or can be used to "freeze" values of local variables into private variables for a particular callback when the local variable itself will be changing to another value as the code continues to execute, but before the callback is called.

Here are examples of closures in answers I've supplied here on SO.

Access parent local variables from setTimeout callback: https://stackoverflow.com/a/7032671/816620

Pass non-static information into a delayed callback: https://stackoverflow.com/a/8660518/816620

I know I've used closures dozens of times in the last month just here in SO answers (I'm just not sure how to quickly find more examples with search without wading through lots of posts).

And, here's a useful closure that creates a private variable:

function slides(images) {
    var slideImages = images || [];

    // because of this closure, the variable slideImages is available
    // to the method defined in here even though the slides function
    // has already finished executing
    this.addSlide = function(url) {
        slideImages.push(url);
    }
    this.clearSlides = function() {
        slideImages = [];
    }
}

// the slideImages variable is not available out here
// it is truly private inside the clsoure
var slideshow = new slides(imgArray);
slideshow.addSlide("xxx.jpeg");



回答3:


Well, one neat thing you can do is have private variables:

function Thing() {
  var x = 10;
  this.getX = function () {
    return x;
  }

  this.increment = function () {
    x++;
  }
}

Now, when you create a new Thing, it will have a getX and increment method but no way to lower the value of x. This value of x will also be unique per instance of Thing.

Crockford has a page about this pattern: http://javascript.crockford.com/private.html




回答4:


A basic example:

var getDay = (function () {
    var days = [
        'Monday',
        'Tuesday',
        'Wednesday',
        'Thursday',
        'Friday',
        'Saturday',
        'Sunday'
    ];

    return function ( n ) {
        return days[ n - 1 ];
    };
}());

The idea is to assign an IIFE which returns a function to a variable. After this assignment, the variable holds the function that was returned from the IIFE. Since this function was nested inside the IIFE, it has access to all its local variables and arguments, even though the IIFE itself doesn't exist anymore.

So, the whole purpose of the IIFE in the above example was to define an days array which acts as a private variable of the getDay function.




回答5:


I used closures to implement lambda expressions for a library I'm working on.

JLinx.Delegate=function() {
  var validateArg=function(arg) {
    if(typeof arg!=="string"&&typeof arg!=="function"&&arg!==null&&arg!==undefined) {
      throw new ArgumentException("arg");
    }
  };
  var funcBody;
  function prepBody(code,returnsResult) {
    var temp=code.trimLeft().trimRight();
    if(returnsResult&&temp.indexOf("return ")== -1) {temp="return "+temp;}
    if(temp.substr(temp.length-1,1)!=";") {temp+=";";}
    return temp;
  }
  function getDelegate(arg,defaultLambda,returnsResult) {
    validateArg(arg);
    if(typeof arg==="function") {return arg;}
    arg=(arg===null||arg===undefined)?defaultLambda:arg;
    if(arg.indexOf("=>")> -1) {
      var parts=arg.split("=>");
      var argList=parts[0];
      funcBody=prepBody(parts[1],returnsResult);
      argList=argList.trimLeft().trimRight()==""?"e":argList;
      argList=(argList.indexOf(",")> -1)?argList.split(","):[argList];
      switch(argList.length) {
        case 1:
          return new Function(argList[0],funcBody);
        case 2:
          return new Function(argList[0],argList[1],funcBody);
        default:
          throw new InvalidOperationException("Invalid number of arguments to action delegate.");
      }
    }
    else {
      funcBody=prepBody(arg,returnsResult);
      return new Function("e",funcBody);
    }
  }
  var factory=
    {
      actionFrom: function(arg) { return getDelegate(arg,"e => return;",false); },
      accumulatorFrom: function(arg) { return getDelegate(arg,"e, v => return v;",true); },
      comparerFrom: function(arg) { return getDelegate(arg,"l,r=>return l<r?-1:l>r?1:0;",true); },
      joinSelectorFrom: function(arg) { return getDelegate(arg,"o, i = { return { o : o, i : i }; };",true); },
      predicateFrom: function(arg) { return getDelegate(arg,"e => return true;",true); },
      selectorFrom: function(arg) { return getDelegate(arg,"e => return e;",true); }
    };
  return factory;
} ();

I know this doesn't look like much, but what it allows you to do with the other methods in the library (which actually provides LINQ-to-XML) is write the following:

var exists = myXmlElement.Any("e.name == 'foo' || e.name == 'bar';');

The closure provides a factory that converts a string to a Function that is executed for each element in a Sequence object. If the argument is already a Function, it is returned directly.

That's one thing you can do with closures.




回答6:


It's not really that mind blowing. Languages like Java don't have closures, but you can still write good software with them.

That said, there is a lot of convenience to being able to do something like

var that = this; // that is defined outside of the function below, but is still in its
                 // lexical scope
arry.each(function(item){
   that.doSomething(item); // which means 'that' is "closed-in" to this function
});



回答7:


I think I like this example very much to explain the closures in Javascript..

      var juice = "Mango";
    var foo = function() {
     var juice = "Apple";
  return function(){
   return juice;
}

    };
var juicebar = foo();
console.log(juice);
console.log(juicebar());


来源:https://stackoverflow.com/questions/9058248/good-examples-for-using-a-closure-in-javascript

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!