最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Good examples for using a Closure in Javascript - Stack Overflow

programmeradmin5浏览0评论

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

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

Share Improve this question edited Jan 30, 2012 at 1:08 André Alçada Padez asked Jan 30, 2012 at 0:53 André Alçada PadezAndré Alçada Padez 11.6k26 gold badges71 silver badges128 bronze badges 4
  • All functions create closures in JavaScript. Could you describe specifically the concept you're talking about? – user1106925 Commented Jan 30, 2012 at 0:57
  • Well, post some of those academic examples... – Šime Vidas Commented Jan 30, 2012 at 0:59
  • 2 You may want to reword your question. The way it is right now, it's very open ended with no "correct" answer. – user1106925 Commented Jan 30, 2012 at 1:04
  • Well not exactly mind-blowing, but a real-world use that comes to mind that is not very many steps from the academic example you quoted is a setTimeout() / clearTimeout() wrapper object that manages the timer ids for you. – nnnnnn Commented Jan 30, 2012 at 1:14
Add a comment  | 

7 Answers 7

Reset to default 7

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

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

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

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.

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.

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

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());
发布评论

评论列表(0)

  1. 暂无评论