1

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

4

5 回答 5

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称为upvalueof的变量counter

用途

解释就够了。为什么我们仍然需要闭包?

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

闭包的另一个常见用例是部分应用。例如:

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.

这就是你需要知道的关于闭包的全部内容。

于 2013-06-26T07:58:10.040 回答
0

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

此处提供了更多信息:javascript 闭包优势?

于 2013-06-26T07:14:20.123 回答
0

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

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

Mozilla 开发者网络对闭包有很好的介绍。它显示了以下示例:

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 Developer Network 的“Counter”“类”。它演示了模块模式:

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声明了变量。现在这个变量只能从捕获它的值的函数中访问。

于 2013-06-26T07:16:01.513 回答
0

这是因为信息隐藏

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 等),所以你必须在适当的地方使用闭包。所以永远不要在类级别上使用它来隐藏信息,因为它太昂贵了。

通过使用回调将方法绑定到实例的另一种用法。

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
于 2013-06-26T07:18:31.840 回答
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++; });

您不会为了使用一次而在“above”命名空间中创建新的函数名称。实际活动就在使用它的地方——你不需要在代码中追逐它到它被编写的地方。您可以将实际函数用作常量,而不是首先定义和命名它,然后按名称调用它,虽然有无数与闭包相关的警告、优势和技巧,但这是销售它们的一个属性。

于 2013-06-26T07:34:18.757 回答