logo
Published on

var functionName = function() {} vs function functionName() {}

Authors
  • Name
    Twitter

在JavaScript中,有多种方式可以定义函数,主要包括函数声明和函数表达式。本文将详细探讨这些不同的形式及其语法和作用,帮助开发者在实际编码中选择适合的方式。

函数的基本形式

术语解释:

函数声明 (Function Declaration)

首先是函数声明,这种形式如下所示:

function x() {
  console.log('x')
}

函数声明是一种声明性语句,而不是表达式,因此不需要在其后加分号(即使加了也不会出错)。函数声明在执行上下文中被预处理,这意味着在实际代码逐步执行之前,函数已被创建并且可以被调用。因此你可以这样使用:

x() // 虽然在声明的上方调用,但依然有效
function x() {
  console.log('x')
}

在ES2015之前,如果在控制结构(如 try, if, switch, while)中声明函数,不同的JavaScript引擎会有不同的处理方式。自ES2015起,规范对此做了明确规定,并根据不同的模式(宽松模式、严格模式)做了不同的处理。在严格模式下,函数声明在块内是局部的,即具有块作用域,并且会被提升到块顶部。

匿名函数表达式 ("Anonymous" function Expression)

第二种常见形式是匿名函数表达式:

var y = function () {
  console.log('y')
}

作为表达式,它在代码执行到达它的时候被求值。在ES5中,这创建的函数是匿名的。在ES2015中,可以通过上下文推断为函数命名。上面的例子中,函数名将会是 y

命名函数表达式 (Named function Expression, NFE)

第三种形式是命名函数表达式:

var z = function w() {
  console.log('zw')
}

这种形式创建的函数具有一个正式名称(在这个例子中是 w)。和所有表达式一样,这种形式在代码执行到达它的时候被求值。函数名在函数内部是有效的,但在函数外部是不可见的:

var z = function w() {
  console.log(typeof w) // "function"
}
console.log(typeof w) // "undefined"

访问器函数初始化 (Accessor Function Initializer, ES5+)

有时函数会悄然出现,比如在访问器函数中:

var obj = {
  value: 0,
  get f() {
    return this.value
  },
  set f(v) {
    this.value = v
  },
}
console.log(obj.f) // 0
console.log(typeof obj.f) // "number"

注意在使用函数时不需要 (),因为这是属性的访问器函数。可以通过 Object.defineProperty 等方法创建访问器函数。

箭头函数表达式 (Arrow Function Expression, ES2015+)

ES2015为我们带来了箭头函数,示例如下:

var a = [1, 2, 3]
var b = a.map((n) => n * 2)
console.log(b.join(', ')) // 2, 4, 6

箭头函数没有自己的 this,而是继承自定义它的位置的 this。它的语法也更为简洁。

对象初始化中的方法声明 (Method Declaration in Object Initializer, ES2015+)

ES2015允许更简洁的属性声明形式:

var o = {
  foo() {},
}

这基本上等同于:

var o = {
  foo: function foo() {},
}

不同之处在于方法可以使用 super,但函数不能。

类中的构造函数和方法声明 (Constructor and Method Declarations in class, ES2015+)

ES2015引入了 class 语法,包括构造函数和方法声明:

class Person {
  constructor(firstName, lastName) {
    this.firstName = firstName
    this.lastName = lastName
  }

  getFullName() {
    return this.firstName + ' ' + this.lastName
  }
}

有两个函数声明:一个是构造函数 Person,另一个是 getFullName,它是分配给 Person.prototype 的函数。

在实际开发中,不同的函数声明和表达式有不同的用武之地。理解它们的特点和差异现对提高代码的可读性和维护性有着重要作用。