Skip to content

箭头函数与普通函数的区别

Arrow Functions: 箭头函数表达式 - JavaScript | MDN

  1. 箭头函数没有独立的 this、arguments 和 super 绑定,并且不可被用作方法。
  2. 箭头函数不能用作构造函数。使用 new 调用它们会引发 TypeError。它们也无法访问 new.target 关键字。
  3. 箭头函数不能在其主体中使用 yield,也不能作为生成器函数创建。

不能用作方法(this)

箭头函数表达式只能用于非方法函数,因为它们没有自己的 this。让我们看看将它们用作方法时会发生什么:

js
"use strict";

const obj = {
  i: 10,
  b: () => console.log(this.i, this),
  c() {
    console.log(this.i, this);
  },
};

obj.b(); // 输出 undefined, Window { /* … */ }(或全局对象)
obj.c(); // 输出 10, Object { /* … */ }

另外一个示例涉及到 Object.defineProperty()

js
"use strict";

const obj = {
  a: 10,
};

Object.defineProperty(obj, "b", {
  get: () => {
    console.log(this.a, typeof this.a, this); // undefined 'undefined' Window { /* … */ }(或全局对象)
    return this.a + 10; // 代表全局对象 'Window',故 `this.a' 返回 'undefined'
  },
});

由于类体具有 this 上下文,因此作为类字段的箭头函数会关闭类的 this 上下文,箭头函数体中的 this 将正确指向实例(对于静态字段来说是类本身)。 但是,由于它是一个闭包,而不是函数本身的绑定,因此 this 的值不会根据执行上下文而改变

js
class C {
  a = 1;
  autoBoundMethod = () => {
    console.log(this.a);
  };
}

const c = new C();
c.autoBoundMethod(); // 1
const { autoBoundMethod } = c;
autoBoundMethod(); // 1
// 如果这是普通方法,此时应该是 undefined

箭头函数属性通常被称作“自动绑定方法”,因为它与普通方法的等价性相同:

js
class C {
  a = 1;
  constructor() {
    this.method = this.method.bind(this);
  }
  method() {
    console.log(this.a);
  }
}

备注: 类字段是在实例(instance)上定义的,而不是在原型(prototype)上定义的,因此每次创建实例都会创建一个新的函数引用并分配一个新的闭包,这可能会导致比普通非绑定方法更多的内存使用。

出于类似原因,call()apply()bind() 方法在箭头函数上调用时不起作用,因为箭头函数是根据箭头函数定义的作用域来建立 this 的,而 this 值不会根据函数的调用方式而改变。

没有参数绑定(arguments)

箭头函数没有自己的 arguments 对象。因此,在本例中,arguments 是对外层作用域参数的引用:

js
function foo(n) {
  const f = () => arguments[0] + n; // foo 的隐式参数绑定。arguments[0] 为 n
  return f();
}

foo(3); // 3 + 3 = 6

备注: 在严格模式下不能声明名为 arguments 的变量,因此上面的代码会出现语法错误。这使得 arguments 的范围效应更容易理解。

在大多数情况下,使用剩余参数是比使用 arguments 对象更好的选择。

js
function foo(n) {
  const f = (...args) => args[0] + n;
  return f(10);
}

foo(1); // 11

不能用作构造函数

箭头函数不能用作构造函数,当使用 new 调用时会出错。它们也没有 `prototype 属性。

js
const Foo = () => {};
const foo = new Foo(); // TypeError: Foo is not a constructor
console.log("prototype" in Foo); // false

不能用作生成器

箭头函数的主体中不能使用 yield 关键字(除非在箭头函数进一步嵌套的生成器函数中使用)。因此,箭头函数不能用作生成器。

Released under the MIT License.