Difference Between Function Declaration and Function Expression

2015-09-17 Jason Liao 更多博文 » 博客 » GitHub »

原文链接 http://jasonliao.me/posts/2015-09-17-FD-and-FE.html
注:以下为加速网络访问所做的原文缓存,经过重新格式化,可能存在格式方面的问题,或偶有遗漏信息,请以原文为准。


FunctionDeclaration : function Identifier ( FormalParameterList opt ) { FunctionBody }

FunctionExpression : function Identifier opt ( FormalParameterList opt ) { FunctionBody }

Function Declaration

函数声明一般具有以下特性

  • 有函数名
  • 在进入上下文时创建出来
  • 会影响变量对象
  • 符合下列这种形式
function foo () {}

会影响变量对象的意思就是,通过函数声明的函数在进入上下文的时候就会被创建出来,即在执行代码阶段就已经存在,简单来说就是 Hoisting。

Function Expression

函数表达式一般具有以下特性

  • 名字可有可无
  • 在执行代码阶段创建出来
  • 不会影响变量对象
  • 必须在表达式的位置
  • 最简单的形式
var foo = function () {};

正如上面的形式,我们把一个匿名函数赋给了变量 foo,之后就可以通过 foo 来访问函数了 - foo()

当然也可以有名字的,像这样

var foo = function _foo () {}

even though inline functions can be named, those names are only visible within the functions themselves. —— 《Secrets of the JavaScript Ninja》

因为在执行代码的时候,当看到 NFE 之后,该函数在作用域链中就会多出一个特殊的对象,这个特殊对象中有一个唯一的属性,key 为这个 NFE 的名字,其值就是对 这个 NFE 的引用。当退出这个上下文的时候,这个特殊的对象就会被移除,所以只能在函数内部通过 NFE 的名字调用

当然啦,在函数内部调用自己本身我们还有一个方法,你们知道是什么吗?

What Function Expression for?

我们常用到函数表达式的是

  • 充当对象的方法
  • IIFE
  (function () {})();
  (function () {}());

Immediately-invoked Function Expression

我们常用IIFE来封装作用域来对上下文隐藏辅助数据

(function () {

  // 初始化作用域  

})();

我们一般是使用 () 来调用我们的函数,但是却不可以直接 function () {}() 这样子来调用,() 前一定要为 函数名 或者 函数表达式

那怎么把函数变成函数表达式呢,我们用的还是 ()

不管是 (function foo () {}),还是 (function () {}),因为函数处于表达式的位置,所以就变成了 函数表达式,所以就可以用 () 来调用了

还有一种情况就是

var foo = {

  bar: function (x) {
    return x % 2 != 0 ? 'yes' : 'no';
  }(1)

};

alert(foo.bar); // 'yes'

这个时候为什么就可以直接调用了呢?就是因为函数本来就在于表达式的位置,就是说本来就是函数表达式,所以就可以直接调用

总结一下,如果你想创建函数后立即调用,有两种情况:

  1. 函数为函数声明,需要手动添加括号转成函数表达式,然后再用括号调用
  2. 函数本为函数表达式,则直接用括号调用

那你现在可以知道这两者的区别了吗?

(function () {})();
(function () {}());

Reference