最近在看《Javascript for Web Developers》,虽然在VT项目中的一个小demo中担任了前端工程师,开始了javascript之路,但是真正来看javascript高级语法的时候还是懵逼了。
function declaration 和 function expression在讲function的时候貌似讲到了,当时看的时候也是以为自己记得了,再看块作用域的时候还是不太清楚。于是在网上找到了一篇文章,看完做个记录也好。
什么是 javascript declaration
Function declaration定义了一个命名的函数变量,但是并没有变量赋值(without requiring variable assignment.)。 Function declration必须作为一个单独的构造器出现,而不能被放在no-function的块中。
可以简单地这么想,function declaration 就想variable declaration的兄弟一样,只是变量声明首先写的是var, 而函数声明必须以function开头。
ECMA5 定义的语法是这样的: function Identifier(FormalParameterListopt) {FunctionBody}
function bar() {return 3;}
bar(); //3
bar; // function
什么是function expression?
Function Expression定义了一个函数,使它作为一个更大的expression语法的一部分(一般是变量赋值)。 通过Function Expression定义的函数可以是匿名的, 不需要以function开头。
//anonymous function expression
var a =function() {
return 3;
}
//named function expression
var a =function bar() {
return 3;
}
//self invoking function expression
(functionsayHello() {
alert("hello!");
})();
ECMA5定义的语法是:function Identifieropt(FormalParameterListopt) {FunctionBody}
The function name (if any) is not visible outside of its scope (contrast with Function Declarations).
案例一
function foo(){
function bar() {
return 3;
}
return bar();
function bar() {
return 8;
}
}
alert(foo()); //8
Hoisting?
Function declarations and function variables are always moved (‘hoisted’) to the top of their JavaScript scope by the JavaScript interpreter
因此在案例一中,第二次声明的bar()被放到return bar(); 语句之前了,相当于重新定义了bar, 然后在invoke了bar(), 因此返回值为8.
我们按照习惯,总觉得return之后的代码是执行不到的?
在Javascript的执行过程中有一个Context和process。 Context被分为lexical environment, variable environment 和 this binding。 declartions在进入执行域时是在variable中的,与statement不一样,因此和它们的执行流程不一样。
案例二
但是在function expression中就不一样了。
functionfoo(){
var bar =function() {
return 3;
};
return bar();
var bar =function() {
return 8;
};
}
alert(foo()); // 3
(ECMA 5 12.2 A variable with an initializer is assigned the value of itsAssignmentExpressionwhen theVariableStatementis executed, not when the variable is created.)
因此该案例的实际执行顺序如下
//**Simulated processing sequence for Question 2**
functionfoo(){
//a declaration for each function expression
varbar = undefined;
varbar = undefined;
//first Function Expression is executed
bar =function() {
return3;
};
// Function created by first Function Expression is invoked
returnbar();
// second Function Expression unreachable
}
alert(foo());//3
I can see how using Function Declarations can cause confusion but are there any benefits?
Well you could argue that Function Declarations are forgiving – if you try to use a function before it is declared, hoisting fixes the order and the function gets called without mishap. But that kind of forgiveness does not encourage tight coding and in the long run is probably more likely to promote surprises than prevent them. After all, programmers arrange their statements in a particular sequence for a reason.
And there are other reasons to favor Function Expressions?
How did you guess?
a) Function Declarations feel like they were intended to mimic Java-style method declarations but Java methods are very different animals. In JavaScript functions are living objects with values. Java methods are just metadata storage. Both the following snippets define functions but only the Function Expression suggests that we are creating an object.
//Function Declaration
function add(a,b) {returna + b};
//Function Expression
var add =function(a,b) {returna + b};
b) Function Expressions are more versatile. A Function Declaration can only exist as a “statement” in isolation. All it can do is create an object variable parented by its current scope. In contrast, a Function Expression (by definition) is part of a larger construct. If you want to create an anonymous function or assign a function to a prototype or as a property of some other object you need a Function Expression. Whenever you create a new function using a high order application such as curry or compose you are using a Function Expression. Function Expressions and Functional Programming are inseparable.
//Function Expression
var sayHello = alert.curry("hello!");
Do Function Expressions have any drawbacks?
Typically functions created by Function Expressions are unnamed. For instance, the following function is anonymous,today is just a reference to an unnamed function:
1
var today = function() {return newDate()}
Does this really matter? Mostly it doesn’t, but as Nick Fitzgerald has pointed out debugging with anonymous functions can be frustrating. He suggests using Named Function Expressions (NFEs) as a workaround:
1
var today =function today() {returnnewDate()}
However as Asen Bozhilov points out (and Kangax documents) NFEs do not work correctly in IE < 9
Conclusions?
Badly placed Function Declarations are misleading and there are few (if any) situations where you can’t use a Function Expression assigned to a variable instead. However if you must use Function Declarations, it will minimize confusion if you place them at the top of the scope to which they belong. I would never place a Function Declarations in an if statement.
Having said all this you may well find yourself in situations where it makes sense to use a Function Declaration. That’s fine. Slavish adherence to rules is dangerous and often results in tortuous code. Much more important is that you understand the concepts so that you can make your own informed decisions. I hope this article helps in that regard.