在Javascript中使用Closure的好例子

时间:2012-01-30 00:53:43

标签: javascript closures

好吧,我最近了解了Javascript中的闭包。

虽然我觉得这个概念真的太棒了,但我还没有为他们找到一个好的应用程序。

在所有博客文章中,我找到的所有tuturials,我都很好地解释了它们是什么以及如何使用它们。

我在任何地方都找不到的例子让我想到:“哇!你可以用封口做这件事吗?很棒!!!”。我找到的所有例子都是纯粹的学术性的。

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

所以,我想知道你们中是否有人可以与这些特殊的功能分享一些令人兴奋的经历。

我知道这是一个悬而未决的问题,但我会将答案归咎于让我最哇哇的人。

由于

7 个答案:

答案 0 :(得分:7)

闭包一直用于稍后调用的回调函数,因为它们允许您访问主机调用函数的局部变量,或者可以用于将局部变量的值“冻结”为特定的私有变量当代码继续执行时,但在调用回调之前,局部变量本身将更改为另一个值时进行回调。

以下是我在SO上提供的答案中的闭包示例。

从setTimeout回调访问父本地变量:https://stackoverflow.com/a/7032671/816620

将非静态信息传递给延迟回调:https://stackoverflow.com/a/8660518/816620

我知道上个月我在SO答案中已经使用了几十次关闭(我只是不确定如何快速找到更多搜索示例而不涉及大量帖子)。

而且,这是一个有用的闭包,它创建了一个私有变量:

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

答案 1 :(得分:5)

嗯,你可以做的一件好事是私有变量:

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

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

现在,当您创建new Thing时,它将采用getXincrement方法,但无法降低x的值。对于x的每个实例,Thing的此值也是唯一的。

Crockford有一个关于这种模式的页面:http://javascript.crockford.com/private.html

答案 2 :(得分:5)

一个基本的例子:

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

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

这个想法是分配一个IIFE,它将一个函数返回给一个变量。在此赋值之后,变量保存从IIFE返回的函数。由于此函数嵌套在IIFE中,因此它可以访问其所有局部变量和参数,即使IIFE本身不再存在。

因此,上述示例中IIFE的整个目的是定义一个days数组,它充当getDay函数的私有变量。

答案 3 :(得分:5)

合并变量

由于“闭包”只是表示函数始终保留其原始变量范围的一种方式,因此有很多方法可以利用它。

Currying似乎是人们喜欢的东西。


创建咖喱值操纵器

这里我创建了一个函数curry,它将返回一个函数,该函数将用于生成与原始curried值一起使用的新函数。

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

创建一个用于压缩字符串的函数

因此,如果我想创建函数来处理字符串,我可以讨论字符串......

var workWithName = curry("Bubba");

...并使用返回的函数创建以各种方式使用给定字符串的新函数。


创建一个将咖喱字符串放入句子

的函数

在这里,我创建一个talkToName函数,根据传递的参数将名称合并到各种句子中......

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

所以现在我有一个talkToName函数接受包含curried字符串的2个字符串。

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

注意我将两个参数传递给talkToName函数,但赋给workWithName的函数接受3个参数。

第一个参数由我们从workWithName()创建的函数传递,我们给talkToName的两个参数在原始curried参数之后添加。


创建一个函数,增加curried字符串的字符

在这里,我使用原始的workWithName函数创建一个完全不同的函数,该函数将采用“Bubba”字符串,并返回一个字符串,其中字母增加给定值...

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

所以我将新的incrementName函数传递给一个数字,它会增加名称中的字母,并返回新的字符串......

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

所以你可以看到我们给了curry()一个值,它给了我们一个函数,可以用来创建与原始值一起工作的新函数。

再次注意我将一个参数传递给incrementName函数,但赋予workWithName的函数接受2个参数。第一个论点是咖喱。


其他带数字的例子

这是一个创建函数生成器的示例,该函数生成器使用数字35

var workWith3And5 = curry(3, 5);

创建使用咖喱数字执行各种操作的函数

因此,使用workWith3And5函数,我们创建一个接受数字参数的新函数,并返回一个带有给定数字的咖喱数字总和的数组......

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

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

另一个使用相同的workWith3And5函数来计算创建3 x 5阵列数组的数字35,其中嵌套数组被赋予一些内容。

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

答案 4 :(得分:3)

这真的不是那么令人兴奋。像Java这样的语言没有闭包,但你仍然可以用它们编写好的软件。

也就是说,能够做

这样的事情有很多方便
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
});

答案 5 :(得分:3)

我使用闭包为我正在处理的库实现lambda表达式。

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

我知道这看起来并不多,但它允许你使用库中的其他方法(实际上提供LINQ-to-XML)的内容是写下面的内容:

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

闭包提供了一个工厂,它将字符串转换为为Sequence对象中的每个元素执行的Function。如果参数已经是Function,则直接返回。

关于闭包,你可以做一件事。

答案 6 :(得分:1)

我想我非常喜欢这个例子来解释Javascript中的闭包..

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

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