我正在尽最大努力理解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);

其他回答

闭包的另一个常见用途是将方法中的this绑定到特定对象,允许在其他地方调用它(例如作为事件处理程序)。

function bind(obj, method) {
    if (typeof method == 'string') {
        method = obj[method];
    }
    return function () {
        method.apply(obj, arguments);
    }
}
...
document.body.addEventListener('mousemove', bind(watcher, 'follow'), true);

每当鼠标移动事件触发时,都会调用watch .follow(evt)。

闭包也是高阶函数的重要组成部分,通过参数化不同部分,可以将多个相似函数重写为一个高阶函数,这是非常常见的模式。举个抽象的例子,

foo_a = function (...) {A a B}
foo_b = function (...) {A b B}
foo_c = function (...) {A c B}

就变成了

fooer = function (x) {
    return function (...) {A x B}
}

其中A和B不是语法单位,而是源代码字符串(不是字符串字面量)。

具体示例请参见“用函数简化我的javascript”。

特别是在JavaScript(或任何ECMAScript)语言中,闭包在隐藏功能实现的同时仍然显示接口方面非常有用。

例如,假设您正在编写一个日期实用工具方法类,您希望允许用户通过索引查找工作日名称,但不希望他们能够修改您在底层使用的名称数组。

var dateUtil = {
  weekdayShort: (function() {
    var days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
    return function(x) {
      if ((x != parseInt(x)) || (x < 1) || (x > 7)) {
        throw new Error("invalid weekday number");
      }
      return days[x - 1];
    };
  }())
};

注意,days数组可以简单地存储为dateUtil对象的属性,但是脚本用户可以看到它,他们甚至可以根据需要更改它,甚至不需要您的源代码。但是,由于它被返回日期查找函数的匿名函数所包围,因此只能由查找函数访问,因此现在它是防篡改的。

你举的例子很好。闭包是一种抽象机制,允许您非常清晰地分离关注点。您的示例是将插装(计数调用)与语义(错误报告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.

在这里,我有一句想说好几遍的问候语。如果我创建一个闭包,我可以简单地调用该函数来记录问候语。如果我不创建闭包,我必须每次都传递我的名字。

没有闭包(https://jsfiddle.net/lukeschlangen/pw61qrow/3/):

function greeting(firstName, lastName) {
  var message = "Hello " + firstName + " " + lastName + "!";
  console.log(message);
}

greeting("Billy", "Bob");
greeting("Billy", "Bob");
greeting("Billy", "Bob");
greeting("Luke", "Schlangen");
greeting("Luke", "Schlangen");
greeting("Luke", "Schlangen");

使用闭包(https://jsfiddle.net/lukeschlangen/Lb5cfve9/3/):

function greeting(firstName, lastName) {
  var message = "Hello " + firstName + " " + lastName + "!";

  return function() {
    console.log(message);
  }
}

var greetingBilly = greeting("Billy", "Bob");
var greetingLuke = greeting("Luke", "Schlangen");

greetingBilly();
greetingBilly();
greetingBilly();
greetingLuke();
greetingLuke();
greetingLuke();

我曾经写过一篇关于如何使用闭包来简化事件处理代码的文章。它比较ASP。NET事件处理到客户端jQuery。

http://www.hackification.com/2009/02/20/closures-simplify-event-handling-code/