在Javascript中使用Closure的好例子

And*_*dez 5 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());
Run Code Online (Sandbox Code Playgroud)

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

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

谢谢

jfr*_*d00 7

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

以下是我在此处提供的答案中的闭包示例.

从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");
Run Code Online (Sandbox Code Playgroud)


Tik*_*vis 5

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

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

  this.increment = function () {
    x++;
  }
}
Run Code Online (Sandbox Code Playgroud)

现在,当你创建一个new Thing,它将有一个getXincrement方法,但没有办法降低值x.x每个实例的值也是唯一的Thing.

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


Šim*_*das 5

一个基本的例子:

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

    return function ( n ) {
        return days[ n - 1 ];
    };
}());
Run Code Online (Sandbox Code Playgroud)

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

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


小智 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));
        };
    };
}
Run Code Online (Sandbox Code Playgroud)

创建一个用于调整字符串的函数

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

var workWithName = curry("Bubba");
Run Code Online (Sandbox Code Playgroud)

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


创建一个将curried字符串放在句子中的函数

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

var talkToName = workWithName(function(curried_str, before, after) {
    return before + curried_str + after;
});
Run Code Online (Sandbox Code Playgroud)

所以现在我有一个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."
Run Code Online (Sandbox Code Playgroud)

请注意,我将两个参数传递给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;
});
Run Code Online (Sandbox Code Playgroud)

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

incrementName(3);  // "Exeed"
incrementName(8);  // "J}jji"
incrementName(0);  // "Bubba"
Run Code Online (Sandbox Code Playgroud)

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

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


其他带数字的例子

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

var workWith3And5 = curry(3, 5);
Run Code Online (Sandbox Code Playgroud)

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

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

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

addNTo3And5( 8 );  // [11, 13];
addNTo3And5( -4 ); // [-1, 1];
Run Code Online (Sandbox Code Playgroud)

另一个使用相同的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]]
Run Code Online (Sandbox Code Playgroud)