JS代码有一个特殊的规则,那就是hoisted提升规则,函数的调用可以放在函数实现的前面,但是仅限于函数是以非表达式的场合声明实现的,那么变量的提升又有什么不同呢,在开发过程中还会出现哪些经常犯的错误,本文对此进行了介绍。
One of the trickier aspects of JavaScript for new JavaScript developers is the fact that variables and functions are "hoisted." Rather than being available after their declaration, they might actually be available beforehand. How does that work? Let's take a look at variable hoisting first.
1 2 | // ReferenceError: noSuchVariable is not defined console.log(noSuchVariable); |
// ReferenceError: noSuchVariable is not defined console.log(noSuchVariable);
This is more or less what one would expect. An error is thrown when you try to access the value of a variable that doesn't exist. But what about this case?
1 2 3 4 5 6 7 | // Outputs: undefined console.log(declaredLater); var declaredLater = "Now it's defined!"; // Outputs: "Now it's defined!" console.log(declaredLater); |
// Outputs: undefined console.log(declaredLater); var declaredLater = "Now it's defined!"; // Outputs: "Now it's defined!" console.log(declaredLater);
What is going on here? It turns out that JavaScript treats variables which will be declared later on in a function differently than variables that are not declared at all. Basically, the JavaScript interpreter "looks ahead" to find all the variable declarations and "hoists" them to the top of the function. Which means that the example above is equivalent to this:
1 2 3 4 5 6 7 8 9 | var declaredLater; // Outputs: undefined console.log(declaredLater); declaredLater = "Now it's defined!"; // Outputs: "Now it's defined!" console.log(declaredLater); |
var declaredLater; // Outputs: undefined console.log(declaredLater); declaredLater = "Now it's defined!"; // Outputs: "Now it's defined!" console.log(declaredLater);
One case where this is particularly likely to bite new JavaScript developers is when reusing variable names between an inner and outer scope. For example:
1 2 3 4 5 6 7 8 9 10 11 | var name = "Baggins"; (function () { // Outputs: "Original name was undefined" console.log("Original name was " + name); var name = "Underhill"; // Outputs: "New name is Underhill" console.log("New name is " + name); })(); |
var name = "Baggins"; (function () { // Outputs: "Original name was undefined" console.log("Original name was " + name); var name = "Underhill"; // Outputs: "New name is Underhill" console.log("New name is " + name); })();
In cases like this, the developer probably expected name to retain its value from the outer scope until the point that name was declared in the inner scope. But due to hoisting, name is undefinedinstead.
Because of this behavior JavaScript linters and style guides often recommend putting all variable declarations at the top of the function so that you won't be caught by surprise.
So that covers variable hoisting, but what about function hoisting? Despite both being called "hoisting," the behavior is actually quite different. Unlike variables, a function declaration doesn't just hoist the function's name. It also hoists the actual function definition.
1 2 3 4 5 6 | // Outputs: "Yes!" isItHoisted(); function isItHoisted() { console.log("Yes!"); } |
// Outputs: "Yes!" isItHoisted(); function isItHoisted() { console.log("Yes!"); }
As you can see, the JavaScript interpreter allows you to use the function before the point at which it was declared in the source code. This is useful because it allows you to express your high-level logic at the beginning of your source code rather than the end, communicating your intentions more clearly.
1 2 3 4 5 | travelToMountDoom(); destroyTheRing(); function travelToMountDoom() { /* Traveling */ } function destroyTheRing() { /* Destruction */ } |
travelToMountDoom(); destroyTheRing(); function travelToMountDoom() { /* Traveling */ } function destroyTheRing() { /* Destruction */ }
However, function definition hoisting only occurs for function declarations, not function expressions. For example:
1 2 3 4 5 6 7 8 9 10 11 12 13 | // Outputs: "Definition hoisted!" definitionHoisted(); // TypeError: undefined is not a function definitionNotHoisted(); function definitionHoisted() { console.log("Definition hoisted!"); } var definitionNotHoisted = function () { console.log("Definition not hoisted!"); }; |
// Outputs: "Definition hoisted!" definitionHoisted(); // TypeError: undefined is not a function definitionNotHoisted(); function definitionHoisted() { console.log("Definition hoisted!"); } var definitionNotHoisted = function () { console.log("Definition not hoisted!"); };
Here we see the interaction of two different types of hoisting. Our variable definitionNotHoistedhas its declaration hoisted (thus it is undefined), but not its function definition (thus theTypeError.)
You might be wondering what happens if you use a named function expression.
1 2 3 4 5 6 7 8 9 | // ReferenceError: funcName is not defined funcName(); // TypeError: undefined is not a function varName(); var varName = function funcName() { console.log("Definition not hoisted!"); }; |
// ReferenceError: funcName is not defined funcName(); // TypeError: undefined is not a function varName(); var varName = function funcName() { console.log("Definition not hoisted!"); };
As you can see, the function's name doesn't get hoisted if it is part of a function expression.
And that is how variable and function hoisting works in JavaScript.
引用:http://designpepper.com/blog/drips/variable-and-function-hoisting