Skip to content

函数作用域

函数作用域的含义是指,属于这个函数的全部变量都可以在整个函数的范围内使用及复用(事实上在嵌套的作用域中也可以使用)。

js
function foo(a) {
  var b = 2
  
  function bar() {}

  var c = 2
}

在以上代码中,标识符a、b、c、bar都附属于foo的作用域,因此无法从foo的外部访问这些标识符。

函数声明和函数表达式

区分函数声明和函数表达式最简单的方法就是看function关键字出现在声明中的位置(不仅仅是一行代码,而是整个声明中的位置)。如果function是声明中的第一个词,那么就是一个函数声明,否则就是函数表达式。

函数声明的一些例子:

js
function foo() {}

function outer() {
  function inner() {}
}

函数表达式的是一些例子:

js
var fn = function () {}

var bar = function baz() {}

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

setTimeout( function () {
  ...
}, 1000)

函数声明和函数表达式的区别

函数声明和函数表达式之间最重要的区别是它们的名称标识符会绑定在何处。

观察一下前面两个例子中的foo函数,函数声明中的foo被绑定在所在作用域中,可以直接通过foo()来调用它。函数表达式中的foo被绑定到了函数表达式自身的函数中而不是所在作用域。

换句话说,(function foo() { ... })()作为函数表达式意味着foo只能在...所代表的位置中被访问,外部作用域无法访问foo,这也意味着foo变量名被隐藏在了自身中,不会对外部作用域造成污染。

匿名和具名

函数声明必须是具名的,而函数声明可以是匿名的。

匿名函数表达式有几个缺点:

  1. 匿名函数表达式在栈追踪中不会显示有意义的函数名,使得调试困难。
  2. 如果没有函数名,当函数需要引用自身时,只能通过已经过期的arguments.callee`引用,比如在递归中。
  3. 匿名函数表达式省略了对于代码的可读性。

缺点2代码:

js
var foo = function () {
    console.log(foo.name)
}

var bar = foo

bar() // foo
foo = null
bar() // typeError

始终给函数表达式命名是一个最佳实践。