何时嵌套函数通常适用于JavaScript?

时间:2017-03-31 02:51:11

标签: javascript

我正在通过this website学习JavaScript。链接是我现在正在阅读的特定章节。

在书中,作者讨论了将模块的实现细节保留在本地范围内。他通过以下方式实现了这一目标:

var dayName = function() {
  var names = ["Sunday", "Monday", "Tuesday", "Wednesday",
               "Thursday", "Friday", "Saturday"];
  return function(number) {
    return names[number];
  };
}();

我理解这是如何运作的,但我不明白为什么他这样做。他为什么不这么做......

function dayName(number) {
  var names = ["Sunday", "Monday", "Tuesday", "Wednesday",
               "Thursday", "Friday", "Saturday"];
  return names[number];
}

...对我来说,它看起来像一个更干净,更易读的代码?鉴于他的目标是将names保留在本地范围内,创建嵌套的lambda似乎是多余的。

在这种情况下使用嵌套函数有什么好处吗?或者他是仅仅出于教育目的而使用它?

谢谢!

5 个答案:

答案 0 :(得分:2)

您展示的玩具示例并不十分引人注目。以这种方式编写它的主要优点是数组只创建一次,而不是每次调用该函数。这使得它更快一些,代价是保持为整个会话分配的数组(一个共同的时空权衡)。在实践中,很少有人以这种方式编程。

当您有多个功能并且它们对共享数据进行操作时,该技术会变得更有用。如果其中一个函数修改了变量,则会在闭包中记住它,并在以后调用它们时对其他函数可见。

var dayName = function() {
  var names = ["Sunday", "Monday", "Tuesday", "Wednesday",
               "Thursday", "Friday", "Saturday"];
  return {
    getDay: function(number) {
        return names[number];
    },
    setDay: function(number, newName) {
        names[number] = newName;
    }
  };
}();

现在你可以写:

console.log(dayName.getDay(3)); // prints "Wednesday"
dayObj.setDay(3, "Mercredi");
console.log(dayName.getDay(3)); // prints "Mercredi"

你不能用你的第二种形式的函数来做这件事,因为从一个调用到下一个函数它没有内存。

答案 1 :(得分:1)

可能会返回类似函数的一种情况是创建多个类似的事件侦听器。例如,假设您有名为button2button3button1.click(() => console.log(1)); button2.click(() => console.log(5)); button1.click(() => console.log(42)); 的按钮。你可以像这样添加点击监听器(为了简洁起见使用JQuery):

function makeLogger(x) {
    return () => console.log(x);
}

button1.click(makeLogger(1));
button2.click(makeLogger(5));
button1.click(makeLogger(42));

这可以改为:

{{1}}

如果出于兼容性原因(不使用Babel)而无法使用箭头功能,则会产生更大的差异。

答案 2 :(得分:1)

在现代JavaScript中,您可以将这样的代码放在模块中(CommonJSAMDES6)。在这种情况下,这将是好的

const names = ["Sunday", "Monday", "Tuesday", "Wednesday",
               "Thursday", "Friday", "Saturday"];

function dayName(number) {
  return names[number];
}

module.exports = {
  dayName: dayName,
};

否则是的,按照他的方式去做是有道理的。您每次执行该函数时都会重新创建names数组。他的方式只创建一次并且它不会污染命名空间(除了dayName之外没有其他人可以看到names数组。

你应该真正考虑使用这种或那种类型的模块。

回答问题什么时候嵌套函数通常适用于JavaScript?一般答案是,当你需要闭包时。

在你发布内部函数的情况下是一个闭包。 关闭 names变量。

Closures是JavaScript的一个非常基本和常见的功能。它们对回调特别有用。

答案 3 :(得分:0)

此代码的一个可能的优点是缓存高度计算代码的结果,该代码必须只执行一次(尽管这很少见),然后将该结果用于其他目的。浏览器相关变量通常使用此策略进行缓存。

答案 4 :(得分:0)

你说:

  

我理解这是如何运作的,但我不明白为什么他这样做

如果你不明白为什么他这样做,那么你就不能真正理解它是如何运作的。

你在那个例子中看到的是一个闭包。在我看来,这是对闭包的更好介绍,而不是很多介绍性的例子。

闭包有点像全局变量。使用全局变量时,函数访问其自身范围之外的数据。例如:

var x = 2;

function amplify (n) {
    return n * x; // here a function is using x which does not exist
                  // in the function's own scope
}

闭包类似,但它们隐藏在全局范围内。每次内部函数从外部函数访问变量时,它使用与全局变量的工作方式相同的机制,除了该变量在全局范围内不可见。现在让我们看一个类似于帖子中的简单示例:

var amplify;

function init () {
    var x = 2;
    amplify = function (n) { return n * x };
};
init();

console.log(amplify(5)); // prints 10
console.log(x); // syntax error

因此,在这种情况下,与您发布的代码一样,变量x的行为类似于全局变量,但在全局范围内不可见。就像全局变量一样,它在它所在范围内声明的所有函数都是可见的。而且因为x函数使用了amplify(),所以init()var amplify; var setFactor; var getFactor; function init () { var x = 2; amplify = function (n) { return n * x }; setFactor = function (n) { x = n }; getFactor = function () { return x }; }; init(); amplify(5); // returns 10 getFactor(); // returns 2 setFactor(4); amplify(5); // returns 20 console.log(x); // still a syntax error 没有释放或垃圾收集函数返回。这就是所有闭包的含义 - 私有全局变量的这种奇怪的矛盾概念。没什么。

在这个特定的用例中,优点很小。每次调用函数时都不通过分配数组来节省内存,但这是关于它的。但闭包比这更强大。例如,您可以使用多个函数共享相同的变量,但仍将其隐藏在全局范围内。例如:

1. Object
 1.1 member: integer
 1.2 member: list
   2.1 list: element 1
   2.2 list: element 2
 1.3 member: string

如你所见。闭包允许您在相关功能之间捕获和共享数据,但隐藏所有其他功能的数据。在某种程度上,闭包几乎是对象的功能等价物。所以我关于何时使用闭合的指南是当你需要模块化但是对象太过分时。最常见的用例是捕获回调状态。但是,如您发布的示例代码所示,缓存(memoization)也是一个很好的用例。