8

从 DailyJS “让我们构建一个 JavaScript 框架”中,我不太确定下面的代码,显然用作全局消减技术。

到目前为止,我的理解还停留在 (function(){}) 上。我了解设置 turing var,将 global.turing 设置为 turing,并返回 window 或 this(如果不在浏览器中),但是 (function(global){})(this or window) 事情让我感到困惑......我见过类似的事情

var mything = {}并将所有代码设置在神话之下,但是这个成语让我有点困惑。

我真的很想了解这里的推理而不是记住它“有效”

(function(global) {
  var turing = {
    VERSION: '0.0.1',
    lesson: 'Part 1: Library Architecture'
  };

  if (global.turing) {
    throw new Error('turing has already been defined');
  } else {
    global.turing = turing;
  }
})(typeof window === 'undefined' ? this : window);
4

2 回答 2

10

(这个答案已经超过 4 年了(截至 2015 年 4 月),虽然它仍然是正确的,但我认为它需要一个更一般的解释 - 见下文)

原始答案

想想这个:

(function (x) {
    // ...
})(y);

作为:

function functionName(x) {
    // ...
}
functionName(y);

但不需要给它一个名字(比如functionName)。

所以这:

(function(global) {
    // ...
})(typeof window === 'undefined' ? this : window);

真的只是:

function functionName(global) {
    // ...
}
functionName(typeof window === 'undefined' ? this : window);

它是一个带有一个参数的函数(global在函数内调用),它被调用时typeof window === 'undefined' ? this : window的含义与以下内容相同:

function functionName(global) {
    // ...
}
if (typeof window === 'undefined') {
    functionName(this);
} else {
    functionName(window);
}

但使用较短的符号(并且没有命名函数)。

更一般的解释

我在 4 年前写了这个答案,我认为是时候对这里涉及的概念添加一些更一般的解释了。

正如我上面解释的,这是:

(function (x) {
    // ...
})(y);

是这个的匿名版本:

function functionName(x) {
    // ...
}
functionName(y);

其中(如果您只调用一次)通常(见下文例外)也与此相同:

function functionName() {
    var x = y;
    // ...
}
functionName();

回到匿名立即调用的函数,这个:

(function (x) {
    // ...
})(y);

与此相同:

(function () {
    var x = y;
    // ...
})();

对于大多数人来说,这可能具有更明显的含义。(这里立即调用的函数没有参数,只是为了给我们一个隔离的变量和其他嵌套函数的作用域,这样我们就不会污染外部或全局作用域——这也是在首先立即调用匿名函数。)

在括号内

顺便说一句,这个:

(function () {
    // ...
})();

与此相同:

(function () {
    // ...
}());

由于语言歧义,函数周围的括号是必需的,但它们可能包括()实际调用函数的,也可能不包括 - 尽管有些人认为这里的第二种形式看起来更清楚。有关更多详细信息以及为什么他认为第一个版本看起来像“狗球”,请参阅Douglas Crockford 的解释。

例外

之前我说过这个:

(function (x) {
    // ...
}(y));

与此相同:

(function () {
    var x = y;
    // ...
}());

这在大多数情况下对于一个参数是正确的(它不会在外部范围内屏蔽同名变量,同时取决于它的值)并且通常对于多个参数是正确的(如果他们不这样做) t 也相互依赖)。我希望它会在示例中变得更加清晰。

当我们有这段代码时:

(function (x) {
    // ...
}(x + 1));

那么我们不能把它翻译成:

(function () {
    var x = x + 1;
    // ...
}());

因为在第一个版本中,我们将 1 添加到外部 x并将结果绑定到内部 x,一旦我们进入函数内部,我们就只有内部x可以使用(即使是 newlet语句也无济于事)。

另一个例子:

(function (x, outer_x) {
    // ...
}(1, x));

在这里你可以设置从外部作用域和内部作用域的old_x值到一个新的值1,你不必担心顺序。但如果你这样做了:xx

概括

如您所见,在某些情况下您不能简单地翻译:

(function (x) {
    // ...
}(y));

进入:

(function () {
    var x = y;
    // ...
}());

但我会争辩说,如果它可以翻译成第二种形式,那么它应该是为了可读性。特别是对于较大的函数,您必须滚动到函数的末尾才能知道函数顶部使用的变量的含义是不方便的。

回到问题

这意味着我会从问题中翻译这段代码:

(function(global) {
  var turing = {
    VERSION: '0.0.1',
    lesson: 'Part 1: Library Architecture'
  };

  if (global.turing) {
    throw new Error('turing has already been defined');
  } else {
    global.turing = turing;
  }
})(typeof window === 'undefined' ? this : window);

进入这个:

(function () {
  var global = typeof window === 'undefined' ? this : window;
  var turing = {
    VERSION: '0.0.1',
    lesson: 'Part 1: Library Architecture'
  };

  if (global.turing) {
    throw new Error('turing has already been defined');
  } else {
    global.turing = turing;
  }
}());

我希望这个答案能解释为什么这两者是等价的,甚至可以写成:

(function (global, turing) {
  if (global.turing) {
    throw new Error('turing has already been defined');
  } else {
    global.turing = turing;
  }
})(typeof window === 'undefined' ? this : window,
   {VERSION: '0.0.1', lesson: 'Part 1: Library Architecture'});

它仍然意味着相同,同时可读性要低得多。

另请参阅我解释类似概念的其他答案。

于 2011-03-15T22:41:49.863 回答
1

这是一个匿名或 lambda 函数。大多数语言都支持这一点。

使用匿名函数确实是作者的偏好问题。如果不设置匿名函数来处理先前调用的返回值,您将不得不在客户端分配额外的内存,方法是初始化一个变量以包含返回的对象,然后稍后将其传递给全局声明的函数串行方式。

如果是1次函数,则节省空间、内存、时间。

作为旁注:通过谷歌搜索函数式编程背后的一些概念,您可能会对此有更好的感觉。Lambda 函数最近似乎风靡一时,尤其是在 Python 中。

于 2011-03-15T22:46:06.863 回答