Javascript中闭包的确切用法是什么?

时间:2013-06-26 07:11:48

标签: javascript closures

嗨,我仍然不确定在javascript中使用闭包的确切用法。我对闭包有一个想法“闭包是一个内部函数,可以访问外部(封闭)函数的变量 - 作用域链”。但我不知道为什么我们在javascript中使用闭包。

5 个答案:

答案 0 :(得分:0)

它允许您简洁地表达逻辑,而无需重复自己或为回调函数提供大量参数和参数。

此处提供了更多信息:javascript closure advantages?

答案 1 :(得分:0)

一般来说,闭包的主要用途是创建一个从其上下文中捕获状态的函数。考虑该函数具有捕获的变量,但它们不作为参数传递。

所以,你可以把它想象成一种创建函数族的方法。例如,如果您需要一系列只有一个值不同的函数,但是您不能将该值作为参数传递,则可以使用闭包创建它们。

Mozilla开发者网络有一个很好的introduction关闭。它显示了以下示例:

function init() {
  var name = "Mozilla";
  function displayName() {
    alert(name);
  }
  displayName();
}
init();

在这种情况下,函数displayName已捕获变量name。虽然这个例子不是很有用,但你可以考虑返回函数的情况:

function makeFunc() {
  var name = "Mozilla";
  function displayName() {
    alert(name);
  }
  return displayName;
}

var myFunc = makeFunc();
myFunc();

此处函数makeFunc返回已捕获变量displayName的函数name。现在可以通过将函数赋值给变量myFunc来调用该函数。

要继续此示例,请立即考虑捕获的变量name实际上是否为参数:

function makeFunc(name) {
  function displayName() {
    alert(name);
  }
  return displayName;
}

var myFunc = makeFunc("Mozilla");
myFunc();

在这里你可以看到makeFunc创建一个函数,显示一条消息,其中文本作为参数传递。因此,它可以创建一整套函数,仅根据该变量的值(示例中为"Mozilla")而有所不同。使用此功能,我们可以多次显示该消息。

这里的相关内容是按摩中显示的值已被封装。我们以类似的方式保护这个值,一个类的私有字段隐藏了其他语言的值。

这允许您创建一个计数的函数:

function makeFunc(value) {
  function displayName() {
    alert(value);
    value++;
  }
  return displayName;
}

var myFunc = makeFunc(0);
myFunc();

在这种情况下,每次调用存储在myFunc中的函数时,您将获得下一个数字,第一个0,下一个1,2 ......依此类推。


更高级的例子是来自Mozilla开发者网络的“计数器”“类”。它演示了模块模式:

var Counter = (function() {
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  }   
})();

alert(Counter.value()); /* Alerts 0 */
Counter.increment();
Counter.increment();
alert(Counter.value()); /* Alerts 2 */
Counter.decrement();
alert(Counter.value()); /* Alerts 1 */

在这里,您可以看到Counter是一个对象,其方法increment推进privateCounter变量,方法decrement递减它。可以通过调用方法value来查询此变量的值。

归档的方式是自动调用匿名函数,该函数创建一个声明变量privateCounter的隐藏作用域。现在,只能从捕获其值的函数中访问此变量。

答案 2 :(得分:0)

这是因为information hiding

var myModule = (function (){

    var privateClass = function (){};
    privateClass.prototype = {
        help: function (){}
    };

    var publicClass = function (){
        this._helper = new privateClass();
    };
    publicClass.prototype = {
        doSomething: function (){
            this._helper.help();
        }
    };

    return {
        publicClass: publicClass
    };

})();

var instance = new myModule.publicClass();
instance.doSomething();

在javascript中,您没有包含ppp(公共,受保护,私有)属性的类,因此您必须使用闭包来隐藏信息。在类级别上,这将非常昂贵,因此您可以做的唯一事情是在模块级别上使用闭包更好的代码质量,并在该闭包中使用私有帮助器类。因此,闭包是为了隐藏信息。它不便宜,它耗费资源(内存,CPU等),所以你必须在适当的地方使用闭包。所以永远不要在课堂上使用它来隐藏信息,因为它太贵了。

使用回调对bind方法实例的另一种用法。

Function.prototype.bind = function (context){
    var callback = this;
    return function (){
        return callback.apply(context, arguments);
    };
};

绑定函数的典型用法是异步调用:defer,ajax,事件监听器等......

var myClass = function (){
    this.x = 10;
};
myClass.prototype.displayX = function (){
    alert(this.x);
};

var instance = new myClass();
setTimeout(instance.displayX.bind(instance), 1000); //alerts "x" after 1 sec

答案 3 :(得分:0)

想象一下,而不是

   alert("Two plus one equals" + (2+1) );

你被迫为你曾经使用的每个常数声明一个变量。

   var Two = 2;
   var One = 1;
   var myString = "Two plus one equals";
   alert(myAlert + (Two + One) );

如果你必须为每一个常数定义一个变量,你就会疯狂。

在闭包的情况下访问局部变量是一个优点,但主要作用 - 有用性 - 是使用函数作为主表达式,常量。

  function Div1OnClick()
  {
       Counter.clickCount ++; 
  }

  $('#Div1').click(Div1OnClick);

  $('#Div1').click(function(){ Counter.clickCount++; });

您不会在上面的""中创建一个新的功能名称。命名空间只需使用一次。实际活动就在那里使用它 - 你不需要在代码中追逐它来写它的位置。您可以将实际函数用作常量,而不是先定义和命名它,然后按名称调用它,虽然有无数的警告,优势和技巧与闭包相关联,但这是销售它们的一个属性。 / p>

答案 4 :(得分:0)

闭包是一种功能强大的构造,用于在JavaScript中实现许多其他功能。例如,闭包可用于公开私有状态,如下所示:

function getCounter() {
    var count = 0;

    return function () {
        return ++count;
    };
}

var counter = getCounter();

alert(counter()); // 1
alert(counter()); // 2
alert(counter()); // 3

在我们调用getCounter的上述示例中,我们创建了一个私有变量count。然后我们返回一个返回count递增的函数。因此,我们返回的函数是一个闭包,它关闭变量count,并允许您在count超出范围后访问它。

这很多信息都塞满了几行。我们分解吧?

好的,所以变量就像人一样有生命。他们出生,他们活着,他们死了。起始范围标志着变量的诞生,范围的结束标志着变量的死亡。

JavaScript只有函数范围。因此,当你在函数中声明一个变量时,它会被提升到函数的开头(它出生的地方)。

当您尝试访问未声明的变量时,您将获得ReferenceError。但是,当您尝试访问稍后声明的变量时,您将获得undefined。这是因为JavaScript中的声明已被提升。

function undeclared_variable() {
    alert(x);
}

undeclared_variable();

当您尝试访问未声明的变量时,您会获得ReferenceError

function undefined_variable() {
    alert(x);

    var x = "Hello World!";
}

undefined_variable();

当您尝试访问稍后在函数中声明的变量时,您获得undefined,因为只有声明被提升。定义将在稍后出现。


返回范围变量在超出范围时死亡(通常在声明变量的函数结束时)。

例如,以下程序将提供ReferenceError,因为x未在全局范围内声明。

function helloworld() {
    var x = "Hello World!";
}

helloworld();

alert(x);

闭包很有意思,因为即使声明变量的函数结束,它们也允许您访问变量。例如:

function getCounter() {
    var count = 0;

    return function () {
        return ++count;
    };
}

var counter = getCounter();

alert(counter()); // 1
alert(counter()); // 2
alert(counter()); // 3

在上面的程序中,变量count在函数getCounter中定义。因此,当对getCounter的调用结束时,变量count也会死亡。

但事实并非如此。这是因为getCounter返回访问count的函数。因此,只要该函数(counter)存活,变量count也将保持活跃。

在这种情况下,返回的函数(counter)称为闭包,因为关闭变量count,称为upvalue counter

用途

足够的解释。为什么我们还需要关闭?

正如我之前已经提到的,闭包的主要用途是公开私有状态,就像getCounter函数一样。

闭包的另一个常见用例是partial application。例如:

function applyRight(func) {
    var args = Array.prototype.slice.call(arguments, 1);

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

function subtract(a, b) {
    return a - b;
}

var decrement = applyRight(subtract, 1);

alert(decrement(1)); // 0

在上面的程序中,我们有一个名为subtract的函数。我们使用部分应用程序从此decrement函数创建另一个名为subtract的函数。

正如您所看到的,decrement函数实际上是一个闭包,关闭变量funcargs

这几乎是关于闭包的所有知识。