如何定义一个JavaScript函数?

标签 IT工具网 javascript

原问题:var functionName = function() {} vs function functionName() {}

PS:对于这个问题,SO上的各个答案之间对于JavaScript语法解析和执行过程有不同意见。其中包含了不少文章和ECMAScript5文档。以下内容也引用了其中一些文档,如果有疑问请先参考那些引用文档的描述。

定义一个JavaScript函数,有两种方式(Function Definition - ECMAScript5):

  • 赋值语句(Function Expression) var functionOne = function() { // Some code };
  • 函数声明(Function Declaration) function functionTwo() { // Some code }

区别 - hoist

尽管平时写代码会随意地把这两种方式混用,但它们在语义上还是有区别的。区别的原因就在于JavaScript解释器对程序的解析过程中的一个特性——hoist > Function declarations and function variables are always moved (‘hoisted’) to the top of their JavaScript scope by the JavaScript interpreter. – JavaScript Scoping and Hoisting

也就是说,functionTwo在被hoist之后是这样的:

var functinoTwo;
// Some code

function functionTwo() {
    // Some code
}

这个hoist是在解析过程中的variable instantiation进行的(这一步是在执行代码run time之前):所有var定义的变量都被初始化为undefined,而function declaration定义的函数将被正常初始化。 关于解析代码的具体过程的规定,请参考ECMAScript5 - Executable Code and Execution Contexts(我扫了一遍,与上面那个文章里说的是一致的)。

有了hoist的概念,我们就能理解一个直观的区别——可见性: - functionOne只对其后面的代码可见

xyz(); // UNDEFINED!!! we can't call it here
xyz = function(){}  // now it is defined
xyz(); // we can call it here
  • 而functionTwo对其所在的scope都是可见的 abc(); // works. we can call it here function abc(){} // yet it is defined down there abc(); // works. we can call it again

共同点

尽管在parse time时二者不同,但到了run time它们都是普通的函数引用。 - 最终在run time的时候,它俩都变成global object的一个*non-deleteable*属性 - 都可以被重新指向。比如:functionOne = functionTwo; functionTwo = null;

总结

其实对这两类函数定义方式,我看不出很明显的应用场景的差别。 只是从实践经验来看,使用自执行函数或者模块化JavaScript的方式下:

  • 使用functionOne定义公开成员
  • 使用functionTwo定义私有方法

这部分内容写到这里就差不多了。但我对hoist还有要注意的地方。举个例子:

var foo = 1;
function bar() {
  if (!foo) {
    console.log('foo is still falsy');
    var foo = 10
  }
  return foo;
}
bar(); // 输出10

在函数bar()foo被hoisted,初始化为undefined。所以!foo为真,然后执行foo = 10;。等于说第一行定义的var foo = 1;压根没起到任何作用。

改一个地方

var foo = 1;
function bar() {
  if (!foo) {
    console.log('foo is still falsy');
    foo = 10
  }
  return foo;
}
bar(); // 输出1

函数bar()内没有var foo的定义,因此用的是代码第一行定义的var foo = 1;,。

hoist是把双刃剑:

  • 增加JavaScript的灵活性,不用拘泥于先定义后使用
  • 对于函数是好的,很方便
  • 对于变量,还是建议遵循先定义后使用的原则

相关文章:

Java 是否支持默认的参数值

javascript - 如何提交输入字段的ID

javascript - 向下滚动后粘性标题

javascript - 根据对象中的键长度/计数对对象数组进行排序

比较java枚举成员使用equal还是==

StringBuilder和StringBuffer有哪些区别呢

JavaScript的参数传递方式:传值or传引用

java 如何分割(split)string字符串

javascript - rails 5 : refresh partial in another partial with AJAX update action

javascript - 当浏览器宽度低于950px时如何动态调用jquery函数?