我正在尽最大努力理解JavaScript闭包。

通过返回一个内部函数,它可以访问直接父函数中定义的任何变量。

这对我有什么用?也许我还没完全搞清楚。我在网上看到的大多数示例都没有提供任何真实的代码,只是一些模糊的示例。

有人能告诉我一个闭包的真实用法吗?

比如这个吗?

var warnUser = function (msg) {
    var calledCount = 0;
    return function() {
       calledCount++;
       alert(msg + '\nYou have been warned ' + calledCount + ' times.');
    };
};

var warnForTamper = warnUser('You can not tamper with our HTML.');
warnForTamper();
warnForTamper();

当前回答

我喜欢Mozilla的函数工厂示例。

function makeAdder(x) {

    return function(y) {
        return x + y;
    };
}

var addFive = makeAdder(5);

console.assert(addFive(2) === 7);
console.assert(addFive(-5) === 0);

其他回答

在Mozilla开发者网络上有一个关于实用闭包的章节。

参考:闭包的实际使用

在实践中,闭包可以创建优雅的设计,允许自定义各种计算、延迟调用、回调、创建封装的作用域等。

一个例子是数组的sort方法,它接受sort条件函数作为参数:

[1, 2, 3].sort(function (a, b) {
    ... // Sort conditions
});

将函数函数映射为数组的map方法,它根据函数参数的条件映射一个新数组:

[1, 2, 3].map(function (element) {
    return element * 2;
}); // [2, 4, 6]

通常,通过使用函数参数定义几乎无限的搜索条件来实现搜索函数是很方便的:

someCollection.find(function (element) {
    return element.someProperty == 'searchCondition';
});

此外,我们可能会注意到应用函数,例如,forEach方法将函数应用于元素数组:

[1, 2, 3].forEach(function (element) {
    if (element % 2 != 0) {
        alert(element);
    }
}); // 1, 3

函数应用于实参(在apply中应用于实参列表,在call中应用于定位实参):

(function () {
    alert([].join.call(arguments, ';')); // 1;2;3
}).apply(this, [1, 2, 3]);

延迟调用:

var a = 10;
setTimeout(function () {
    alert(a); // 10, after one second
}, 1000);

回调函数:

var x = 10;
// Only for example
xmlHttpRequestObject.onreadystatechange = function () {
    // Callback, which will be called deferral ,
    // when data will be ready;
    // variable "x" here is available,
    // regardless that context in which,
    // it was created already finished
    alert(x); // 10
};

创建一个用于隐藏辅助对象的封装作用域:

var foo = {};
(function (object) {
    var x = 10;
    object.getX = function _getX() {
        return x;
    };
})(foo);

alert(foo.getX()); // Get closured "x" – 10

解释JavaScript闭包的实际用法

当我们在另一个函数中创建一个函数时,我们是在创建一个闭包。闭包功能强大,因为它们能够读取和操作其外部函数的数据。无论何时调用函数,都会为该调用创建一个新的作用域。在函数内部声明的局部变量属于该作用域,并且只能从该函数访问它们。当函数完成执行时,作用域通常被销毁。

这类函数的一个简单例子是:

function buildName(name) {
    const greeting = "Hello, " + name;
    return greeting;
}

在上面的例子中,函数buildName()声明了一个局部变量greeting并返回它。每次函数调用都会创建一个新的作用域和一个新的局部变量。在函数执行完成后,我们无法再次引用该作用域,因此它被垃圾收集。

但如果我们有一个指向范围的链接呢?

让我们看看下一个函数:

函数buildName(name) { const greeting = "Hello, " + name + " Welcome "; const sayName = function() { console.log(问候); }; 返回sayName; } const sayMyName = buildName("Mandeep"); sayMyName ();//你好,曼迪普

本例中的sayName()函数是一个闭包。sayName()函数有自己的局部作用域(变量welcome),也可以访问外部(封闭)函数的作用域。在本例中,是来自buildName()的greeting变量。

在执行buildName之后,在本例中不会销毁作用域。sayMyName()函数仍然可以访问它,因此它不会被垃圾收集。但是,除了闭包之外,没有其他从外部作用域访问数据的方法。闭包充当全局上下文和外部作用域之间的网关。

你举的例子很好。闭包是一种抽象机制,允许您非常清晰地分离关注点。您的示例是将插装(计数调用)与语义(错误报告API)分离的例子。其他用途包括:

Passing parameterised behaviour into an algorithm (classic higher-order programming): function proximity_sort(arr, midpoint) { arr.sort(function(a, b) { a -= midpoint; b -= midpoint; return a*a - b*b; }); } Simulating object oriented programming: function counter() { var a = 0; return { inc: function() { ++a; }, dec: function() { --a; }, get: function() { return a; }, reset: function() { a = 0; } } } Implementing exotic flow control, such as jQuery's Event handling and AJAX APIs.

闭包的使用:

闭包是JavaScript最强大的特性之一。JavaScript允许函数嵌套,并授予内部函数对外部函数中定义的所有变量和函数的完全访问权(以及外部函数可以访问的所有其他变量和函数)。但是,外部函数不能访问内部函数内部定义的变量和函数。

这为内部函数的变量提供了一种安全性。此外,由于内部函数可以访问外部函数的作用域,如果内部函数能够在外部函数的生命周期之后继续存在,那么外部函数中定义的变量和函数将比外部函数本身的生命周期更长。当内部函数以某种方式对外部函数之外的任何作用域可用时,就会创建闭包。

例子:

<script>
var createPet = function(name) {
  var sex;
  
  return {
    setName: function(newName) {
      name = newName;
    },
    
    getName: function() {
      return name;
    },
    
    getSex: function() {
      return sex;
    },
    
    setSex: function(newSex) {
      if(typeof newSex == "string" && (newSex.toLowerCase() == "male" || newSex.toLowerCase() == "female")) {
        sex = newSex;
      }
    }
  }
}

var pet = createPet("Vivie");
console.log(pet.getName());                  // Vivie

console.log(pet.setName("Oliver"));   
console.log(pet.setSex("male"));
console.log(pet.getSex());                   // male
console.log(pet.getName());                  // Oliver
</script>

在上面的代码中,外部函数的name变量可以被内部函数访问,除了通过内部函数,没有其他方法访问内部变量。内部函数的内部变量充当内部函数的安全存储。它们保存“持久的”但安全的数据,供内部函数使用。这些函数甚至不需要赋值给变量,也不需要有名称。 详情请阅读此处。