在Javascript中使用闭包的好例子

Good examples for using a Closure in Javascript

本文关键字:闭包 Javascript      更新时间:2023-09-26

我最近了解了Javascript中的闭包。

虽然我觉得这个概念真的很神奇,但我自己还没有找到一个好的应用程序。

在我发现的所有博客文章和图图里亚尔中,我都很好地解释了它们是什么以及如何使用它们。

我在任何地方都找不到让我思考的例子:"哇!你可以用闭包做到这一点?太棒了!!"。我发现的所有例子都是像这样纯粹的学术性例子。

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.上提供的答案中的闭包示例

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

货币变量

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

咖喱似乎是人们喜欢的东西。


创建当前值操纵器

在这里,我创建了一个函数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));
        };
    };
}

创建一个使字符串变为curry的函数

所以,如果我想创建使用字符串的函数,我可以修改字符串。。。

var workWithName = curry("Bubba");

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


创建一个函数,将curried字符串放入句子中

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

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

现在我有了一个talkToName函数,它接受两个字符串来包装curried字符串。

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

请注意我向talkToName函数传递了两个参数,但给workWithName的函数接受了三个参数。

第一个参数由我们从workWithName()创建的函数传递,我们给talkToName的两个参数添加在原始的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的函数接受了两个参数。第一个论点被推翻了。


其他带数字的示例

下面是一个创建函数生成器的示例,该函数生成器使用数字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函数,该函数包含数字35,创建了一个3 x 5数组,其中嵌套的数组被赋予了一些内容。。。

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

好吧,你可以做的一件简单的事情就是拥有私有变量:

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

一个基本示例:

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

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

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

我使用闭包为我正在开发的库实现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到XML)进行以下操作:

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

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

这是使用闭包可以做的一件事。

这真的没有那么令人震惊。像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
});

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

      var juice = "Mango";
    var foo = function() {
     var juice = "Apple";
  return function(){
   return juice;
}
    };
var juicebar = foo();
console.log(juice);
console.log(juicebar());