【问题标题】:JavaScript function order: why does it matter?JavaScript 函数顺序:为什么重要?
【发布时间】:2011-11-28 09:15:15
【问题描述】:

原问题:

JSHint 抱怨当我的 JavaScript 调用一个在页面下方定义的函数而不是对其调用时。但是,我的页面是用于游戏的,在整个下载之前不会调用任何函数。那么为什么订单函数出现在我的代码中很重要呢?

编辑:我想我可能已经找到了答案。

http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting

我在内心呻吟。看起来我需要再花一天时间重新订购六千行代码。使用 javascript 的学习曲线一点也不陡峭,但非常漫长。

【问题讨论】:

  • +1 以获得更新中的出色参考。我希望这能让你相信你真的不需要重新排序你的代码。 :)

标签: javascript function jslint jshint


【解决方案1】:

tl;dr 如果您在加载所有内容之前不调用任何内容,那么您应该没问题。


编辑:对于还涵盖一些 ES6 声明的概述(letconst):https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Scope_Cheatsheet

这种奇怪的行为取决于

  1. 如何定义函数和
  2. 当你打电话给他们时。

这里有一些例子。

bar(); //This won't throw an error
function bar() {}

foo(); //This will throw an error
var foo = function() {}
bar();
function bar() {
    foo(); //This will throw an error
}
var foo = function() {}
bar();
function bar() {
    foo(); //This _won't_ throw an error
}
function foo() {}
function bar() {
    foo(); //no error
}
var foo = function() {}
bar();

这是因为一个叫做提升的东西!

定义函数有两种方式:函数声明和函数表达式。区别很烦人而且很细微,所以让我们说这个稍微有点错误的事情:如果你写成function name() {},它是一个声明,而当你写成var name = function() {}(或一个分配给返回的匿名函数,诸如此类),它是一个函数表达式

首先,我们来看看变量是如何处理的:

var foo = 42;

//the interpreter turns it into this:
var foo;
foo = 42;

现在,函数声明是如何处理的:

var foo = 42;
function bar() {}

//turns into
var foo; //Insanity! It's now at the top
function bar() {}
foo = 42;

var 语句将foo创建“抛出”到最顶部,但尚未为其赋值。函数声明紧随其后,最后给foo赋值。

那么这个呢?

bar();
var foo = 42;
function bar() {}
//=>
var foo;
function bar() {}
bar();
foo = 42;

只有foo声明 被移到顶部。分配仅在调用 bar 之后进行,在所有提升发生之前。

最后,为了简洁:

bar();
function bar() {}
//turns to
function bar() {}
bar();

现在,函数表达式呢?

var foo = function() {}
foo();
//=>
var foo;
foo = function() {}
foo();

就像常规变量一样,首先foo在作用域的最高点声明,然后分配一个值。

让我们看看为什么第二个例子会抛出错误。

bar();
function bar() {
    foo();
}
var foo = function() {}
//=>
var foo;
function bar() {
    foo();
}
bar();
foo = function() {}

正如我们之前所见,只有 foo 的创建被提升,分配出现在“原始”(未提升)代码中。当bar被调用时,是在foo被赋值之前,所以foo === undefined。现在在bar 的函数体中,就好像你在做undefined(),这会引发错误。

【讨论】:

  • 抱歉挖掘了这个,但是像 Array.prototype.someMethod = function(){} 这样的重载被提升了吗?如果这些类型的内容出现在我的脚本末尾,我似乎会遇到错误。
  • 您如何确保所有内容都加载?有什么常见的做法吗?
【解决方案2】:

主要原因可能是 JSLint 只对文件进行一次传递,因此它不知道您定义这样的函数。

如果你使用了函数语句语法

function foo(){ ... }

实际上你在哪里声明函数并没有什么不同(它总是表现得好像声明在开头)。

另一方面,如果你的函数被设置为一个常规变量

var foo = function() { ... };

您必须保证在初始化之前不会调用它(这实际上可能是错误的来源)。


由于重新排序大量代码很复杂,并且本身可能是错误的来源,我建议您寻找一种解决方法。我很确定你可以事先告诉 JSLint 全局变量的名称,这样它就不会抱怨未声明的东西。

在文件的开头添加评论

/*globals foo1 foo2 foo3*/

或者你可以使用一个文本框。 (我也认为你可以在参数中将它传递给内部 jslint 函数,如果你可以干预的话。)

【讨论】:

  • 谢谢。那么 /* globals */ 行会起作用吗?好 - 任何让 JsHint 喜欢我的东西。我还是 JavaScript 新手,刷新页面时会出现莫名其妙的停顿,但没有报告错误。所以我认为解决方案是遵守所有规则,然后看看它是否仍然会发生。
【解决方案3】:

有太多人在推动关于如何编写 JavaScript 的任意规则。大多数规则都是垃圾。

函数提升是 JavaScript 中的一个特性,因为它是一个好主意。

当你有一个内部函数通常是内部函数的实用程序时,将它添加到外部函数的开头是一种可接受的代码编写方式,但它确实有一个缺点,你必须通读细节才能了解外部函数的作用。

您应该在整个代码库中坚持一个原则,即在模块或函数中将私有函数放在首位或放在最后。 JSHint 有利于增强一致性,但您应该绝对调整 .jshintrc 以满足您的需求,而不是根据其他人古怪的编码概念调整您的源代码。

一种你可能会在野外看到的编码风格,你应该避免,因为它没有给你带来任何好处,只会给你带来重构的痛苦:

function bigProcess() {
    var step1,step2;
    step1();
    step2();

    step1 = function() {...};
    step2 = function() {...};
}

这正是要避免的功能提升。只需学习语言并发挥其优势即可。

【讨论】:

    【解决方案4】:

    只提升函数声明而不提升函数表达式(赋值)。

    【讨论】:

      猜你喜欢
      • 2015-03-10
      • 2013-05-18
      • 1970-01-01
      • 1970-01-01
      • 2013-11-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多