# 函数进阶-执行上下文

# 函数绑定

当将对象方法作为回调进行传递,例如传递给 setTimeout,这儿会存在一个常见的问题:“丢失 this”。

# 绑定上下文

函数对象提供了一个内建方法 bind,它可以绑定 this。

基本的语法是:

// 稍后将会有更复杂的语法
let boundFunc = func.bind(context);
let user = {
  firstName: "John"
};

function func(phrase) {
  alert(phrase + ", " + this.firstName);
}

// 将 this 绑定到 user
let funcUser = func.bind(user);

funcUser("Hello"); // Hello, John(参数 "Hello" 被传递,并且 this=user)

便捷方法:bindAll

如果一个对象有很多方法,并且我们都打算将它们都传递出去,那么我们可以在一个循环中完成所有方法的绑定:

for (let key in user) {
  if (typeof user[key] == "function") {
    user[key] = user[key].bind(user);
  }
}

# 绑定参数

我们不仅可以绑定 this,还可以绑定参数(arguments)。虽然很少这么做,但有时它可以派上用场。

bind 的完整语法如下:

let bound = func.bind(context, [arg1], [arg2], ...);

# 箭头函数

让我们深入研究一下箭头函数。

箭头函数不仅仅是编写简洁代码的“捷径”。它还具有非常特殊且有用的特性。

JavaScript 充满了我们需要编写在其他地方执行的小函数的情况。

# 箭头函数没有 “this”

我们常说的一句话就是箭头函数没有 “this”,在箭头函数中访问 this,会从箭头函数的声明上下文中获取。

let group = {
  title: "Our Group",
  students: ["John", "Pete", "Alice"],

  showList() {
    this.students.forEach((student) => alert(this.title + ": " + student));
  }
};

group.showList();

这里 forEach 中使用了箭头函数,所以其中的 this.title 其实和外部方法 showList 的完全一样。那就是:group.title。 如果我们使用正常的函数,则会出现错误,报错是因为 forEach 运行它里面的这个函数,但是这个函数的 this 为默认值 this=undefined

不能对箭头函数进行 new 操作

不具有 this 自然也就意味着另一个限制:箭头函数不能用作构造器(constructor)。不能用 new 调用它们。

箭头函数 VS bind

  • .bind(this) 创建了一个该函数的“绑定版本”
  • 箭头函数 => 没有创建任何绑定。箭头函数只是没有 this。this 的查找与常规变量的搜索方式完全相同:在外部词法环境中查找。

# 箭头函数没有 “arguments”

箭头函数也没有 arguments 变量。

当我们需要使用当前的 this 和 arguments 转发一个调用时,这对装饰器(decorators)来说非常有用。

function defer(f, ms) {
  return function () {
    setTimeout(() => f.apply(this, arguments), ms);
  };
}

不用箭头函数的话,可以这么写:

function defer(f, ms) {
  return function (...args) {
    let ctx = this;
    setTimeout(function () {
      return f.apply(ctx, args);
    }, ms);
  };
}

在这里,我们必须创建额外的变量 args 和 ctx,以便 setTimeout 内部的函数可以获取它们。

# 总结

箭头函数:

  • 没有 this
  • 没有 arguments
  • 不能使用 new 进行调用,也没有 super