JavaScript函数顺序:为什么重要?

时间:2021-12-16 21:29:27

Original Question:

JSHint complains when my JavaScript calls a function that is defined further down the page than the call to it. However, my page is for a game, and no functions are called until the whole thing has downloaded. So why does the order functions appear in my code matter?

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

EDIT: I think I may have found the answer.

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

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

I am groaning inside. Looks like I need to spend ANOTHER day re-ordering six thousand lines of code. The learning curve with javascript is not steep at all, but it is very loooooong.

我在里面呻吟。看起来我需要花一天时间重新订购六千行代码。使用javascript的学习曲线根本不陡峭,但它非常loooooong。

4 个解决方案

#1


253  

tl;dr If you're not calling anything until everything loads, you should be fine.

tl; dr如果你没有在任何东西加载之前调用任何东西,你应该没问题。


Edit: For an overview which also covers some ES6 declarations (let, const): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Scope_Cheatsheet

编辑:有关一些ES6声明(let,const)的概述:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Scope_Cheatsheet

This weird behavior depends on

这种奇怪的行为取决于

  1. How you define the functions and
  2. 如何定义函数和
  3. When you call them.
  4. 当你打电话给他们。

Here's some examples.

这是一些例子。

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();

This is because of something called hoisting!

这是因为所谓的吊装!

There are two ways to define functions: Function declaration and function expression. The difference is annoying and minute, so let's just say this slightly wrong thing: If you're writing it like function name() {}, it's a declaration, and when you write it like var name = function() {} (or an anonymous function assigned to a return, things like that), it's a function expression.

定义函数有两种方法:函数声明和函数表达式。差别很烦人,所以我只想说这个稍微错误的东西:如果你像函数name(){}一样写它,它就是一个声明,当你把它写成var name = function(){}时(或者分配给返回的匿名函数,类似的东西),它是一个函数表达式。

First, let's look at how variables are handled:

var foo = 42;

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

Now, how function declarations are handled:

var foo = 42;
function bar() {}

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

The var statements "throws" the creation of foo to the very top, but doesn't assign the value to it yet. The function declaration comes next in line, and finally a value is assigned to foo.

var语句将foo的创建“抛出”到最顶层,但是还没有为它赋值。函数声明接下来排成一行,最后将一个值赋给foo。

And what about this?

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

Only the declaration of foo is moved to the top. The assignment comes only after the call to bar is made, where it was before all the hoisting occurred.

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

And finally, for conciseness:

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

Now, what about function expressions?

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

Just like regular variables, first foo is declared at the highest point of the scope, then it is assigned a value.

就像常规变量一样,首先在范围的最高点声明foo,然后为其分配一个值。

Let's see why the second example throws an error.

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

As we've seen before, only the creating of foo is hoisted, the assignment comes where it appeared in the "original" (un-hoisted) code. When bar is called, it is before foo is assigned a value, so foo === undefined. Now in the function-body of bar, it's as if you're doing undefined(), which throws an error.

正如我们之前看到的那样,只有foo的创建被提升,分配才会出现在“原始”(未提升)代码中。当调用bar时,它在foo被赋值之前,所以foo === undefined。现在在bar的函数体中,就好像你在做undefined(),它会抛出一个错误。

#2


6  

The main reason is probably that JSLint does only one pass on the file so it doesn't know you will define such a function.

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

If you used functions statement syntax

如果使用函数语句语法

function foo(){ ... }

There is actually no difference at all where you declare the function (it always behaves as if the declaration is on the beginning).

在声明函数的地方实际上没有任何区别(它总是表现为声明在开头)。

On the other hand, if your function was set like a regular variable

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

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

You have to guarantee you wont call it before the initialization (this can actually be a source of bugs).

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


Since reordering tons of code is complicated and can be a source of bugs in itself, I would suggest you search for a workaround. I'm pretty sure you can tell JSLint the name of global variables beforehand so it doesn't complain about undeclared stuff.

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

Put a comment on the beggining of the file

对文件的开头发表评论

/*globals foo1 foo2 foo3*/

Or you can use a text box there for that. (I also think you can pass this in the arguments to the inner jslint function if you can meddle with it.)

或者你可以在那里使用一个文本框。 (我也认为你可以在内部jslint函数的参数中传递它,如果你可以插入它。)

#3


3  

There are way too many people pushing arbitrary rules about how JavaScript should be written. Most rules are utter rubbish.

很多人都在推行关于如何编写JavaScript的任意规则。大多数规则都是完全垃圾。

Function hoisting is a feature in JavaScript because it is a good idea.

函数提升是JavaScript中的一项功能,因为它是一个好主意。

When you have an internal function which is often the utility of inner functions, adding it to the beginning of the outer function is an acceptable style of writing code, but it does have the drawback that you have to read through the details to get to what the outer function does.

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

You should stick to one principle throughout your codebase either put private functions first or last in your module or function. JSHint is good for enforcing consistency, but you should ABSOLUTELY adjust the .jshintrc to fit your needs, NOT adjust your source code to other peoples wacky coding concepts.

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

One coding style that you might see in the wild you should avoid because it gives you no advantages and only possible refactoring pain:

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

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

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

This is exactly what function hoisting is there to avoid. Just learn the language and exploit its strengths.

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

#4


1  

Only function declaration are hoisted not function expression (assignment).

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

#1


253  

tl;dr If you're not calling anything until everything loads, you should be fine.

tl; dr如果你没有在任何东西加载之前调用任何东西,你应该没问题。


Edit: For an overview which also covers some ES6 declarations (let, const): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Scope_Cheatsheet

编辑:有关一些ES6声明(let,const)的概述:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Scope_Cheatsheet

This weird behavior depends on

这种奇怪的行为取决于

  1. How you define the functions and
  2. 如何定义函数和
  3. When you call them.
  4. 当你打电话给他们。

Here's some examples.

这是一些例子。

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();

This is because of something called hoisting!

这是因为所谓的吊装!

There are two ways to define functions: Function declaration and function expression. The difference is annoying and minute, so let's just say this slightly wrong thing: If you're writing it like function name() {}, it's a declaration, and when you write it like var name = function() {} (or an anonymous function assigned to a return, things like that), it's a function expression.

定义函数有两种方法:函数声明和函数表达式。差别很烦人,所以我只想说这个稍微错误的东西:如果你像函数name(){}一样写它,它就是一个声明,当你把它写成var name = function(){}时(或者分配给返回的匿名函数,类似的东西),它是一个函数表达式。

First, let's look at how variables are handled:

var foo = 42;

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

Now, how function declarations are handled:

var foo = 42;
function bar() {}

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

The var statements "throws" the creation of foo to the very top, but doesn't assign the value to it yet. The function declaration comes next in line, and finally a value is assigned to foo.

var语句将foo的创建“抛出”到最顶层,但是还没有为它赋值。函数声明接下来排成一行,最后将一个值赋给foo。

And what about this?

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

Only the declaration of foo is moved to the top. The assignment comes only after the call to bar is made, where it was before all the hoisting occurred.

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

And finally, for conciseness:

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

Now, what about function expressions?

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

Just like regular variables, first foo is declared at the highest point of the scope, then it is assigned a value.

就像常规变量一样,首先在范围的最高点声明foo,然后为其分配一个值。

Let's see why the second example throws an error.

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

As we've seen before, only the creating of foo is hoisted, the assignment comes where it appeared in the "original" (un-hoisted) code. When bar is called, it is before foo is assigned a value, so foo === undefined. Now in the function-body of bar, it's as if you're doing undefined(), which throws an error.

正如我们之前看到的那样,只有foo的创建被提升,分配才会出现在“原始”(未提升)代码中。当调用bar时,它在foo被赋值之前,所以foo === undefined。现在在bar的函数体中,就好像你在做undefined(),它会抛出一个错误。

#2


6  

The main reason is probably that JSLint does only one pass on the file so it doesn't know you will define such a function.

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

If you used functions statement syntax

如果使用函数语句语法

function foo(){ ... }

There is actually no difference at all where you declare the function (it always behaves as if the declaration is on the beginning).

在声明函数的地方实际上没有任何区别(它总是表现为声明在开头)。

On the other hand, if your function was set like a regular variable

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

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

You have to guarantee you wont call it before the initialization (this can actually be a source of bugs).

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


Since reordering tons of code is complicated and can be a source of bugs in itself, I would suggest you search for a workaround. I'm pretty sure you can tell JSLint the name of global variables beforehand so it doesn't complain about undeclared stuff.

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

Put a comment on the beggining of the file

对文件的开头发表评论

/*globals foo1 foo2 foo3*/

Or you can use a text box there for that. (I also think you can pass this in the arguments to the inner jslint function if you can meddle with it.)

或者你可以在那里使用一个文本框。 (我也认为你可以在内部jslint函数的参数中传递它,如果你可以插入它。)

#3


3  

There are way too many people pushing arbitrary rules about how JavaScript should be written. Most rules are utter rubbish.

很多人都在推行关于如何编写JavaScript的任意规则。大多数规则都是完全垃圾。

Function hoisting is a feature in JavaScript because it is a good idea.

函数提升是JavaScript中的一项功能,因为它是一个好主意。

When you have an internal function which is often the utility of inner functions, adding it to the beginning of the outer function is an acceptable style of writing code, but it does have the drawback that you have to read through the details to get to what the outer function does.

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

You should stick to one principle throughout your codebase either put private functions first or last in your module or function. JSHint is good for enforcing consistency, but you should ABSOLUTELY adjust the .jshintrc to fit your needs, NOT adjust your source code to other peoples wacky coding concepts.

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

One coding style that you might see in the wild you should avoid because it gives you no advantages and only possible refactoring pain:

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

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

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

This is exactly what function hoisting is there to avoid. Just learn the language and exploit its strengths.

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

#4


1  

Only function declaration are hoisted not function expression (assignment).

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