闭包应用场景:绑定上下文

105 阅读4分钟

在我之前的文章中,我们介绍了闭包在封装私有变量函数防抖和节流场景中的应用。今天,我们将继续深入探讨闭包的另一个核心作用——绑定上下文

关于封装防抖节流的文章,可以看《闭包的应用场景:封装、防抖、节流!》


一、闭包的简单介绍

闭包(Closure)是指函数能够访问并记住其词法作用域,即使该函数在其作用域外执行。通俗地说,闭包就像是一个背包,里面装着函数创建时的环境信息(如变量、参数、this 等),从而在后续调用中依然可以访问这些信息。

闭包的核心特点:

  1. 延长变量生命周期:即使外部函数执行完毕,数据也不会立即释放,内部函数依然可访问其作用域中的变量。
  2. 数据封装:通过闭包隐藏内部变量,只暴露受控接口,保护内部数据安全。
  3. 绑定上下文:利用闭包“记住”外部的 this,避免在回调或异步中丢失上下文。

二、绑定上下文的三种方法

在 JavaScript 中,this 的指向问题一直让很多人头大,尤其是在事件回调或异步操作中,this 容易丢失上下文,而闭包通过“记住”外部作用域的变量和函数,为我们提供了三种经典的解决方案:

  1. 箭头函数:自动继承父级作用域的 this
  2. bind 方法:显式绑定函数的 this
  3. that = this:通过变量保存当前 this,供内部函数访问。

以下,是关于这三种方法的详细介绍:


绑定上下文方法1 :箭头函数

箭头函数(=>)它没有自己的 this,而是继承父级作用域的 this,这种特性使得箭头函数天然适合绑定上下文。

先看个案例:

<button id="myButton">点我啊~</button>
<script>
  const obj = {
    message: "杂鱼~ ,杂鱼~",
    init() {
      const button = document.getElementById("myButton");
      // 使用箭头函数绑定上下文
      button.addEventListener("click", () => {
        console.log(this.message); // 输出 "杂鱼~ ,杂鱼~"
      });
    }
  };
  obj.init();
</script>

内容分析:

在上面的这段代码中,当你点击按钮后,便会打印杂鱼~,杂鱼~的字样,其中闭包作用如下:

  1. 箭头函数 () => { console.log(this.message); } 其实没有自己的 this,它继承 init 方法中的 this(即 obj

  2. 这里闭包“记住”了 obj 的上下文,可以确保 this.message 正确指向 obj.message

  3. 对比传统函数

    button.addEventListener("click", function () {
      console.log(this.message); // this 指向按钮元素,输出 undefined
    });
    

    其中,传统函数中的 this 指向按钮元素,而this.message 则会丢失上下文。

  4. 适用场景

    • 事件监听器(如 clickinput)。
    • 异步操作(如 setTimeoutPromise)。

绑定上下文方法2 :bind

bind 方法会创建一个新函数,并永久绑定其 this,保存绑定后的 this,并在后续调用中使用。

案例:

<script>
  const person = {
    name: "艾伦耶格尔",
    sayHello() {
      // 使用 bind 绑定上下文
      setTimeout(
        function () {
          console.log(`${this.name}: 机油哒`);
        }.bind(this),
        1000
      );
    }
  };
  person.sayHello(); // 输出 "艾伦耶格尔: 机油哒"
</script>

分析:

  1. 闭包作用

    • .bind(this) 创建了一个新函数,其 this 被永久绑定为 person
    • 闭包确保即使 setTimeout 的回调函数在全局作用域执行,this.name 仍指向 person.name
  2. 对比未绑定的函数

    setTimeout(function () {
      console.log(`${this.name}: 机油哒`); // this 指向 window,输出 undefined
    }, 1000);
    

    由于 this 指向全局对象windows,导致this.name 会丢失上下文,输出undefined

  3. 适用场景

    • 需要长期绑定 this 的函数(如事件处理函数、回调函数)。
    • 需要传递参数时结合 bind(如 fn.bind(this, arg1, arg2))。

绑定上下文方法3:this = that

这个方法通过变量(如 that)来保存当前的 this,在内部函数中访问该变量,通过保存 this 的引用,避免内部函数丢失上下文。

案例:

<button id="myButton">点我啊~</button>
<script>
  const obj = {
    message: "杂鱼~ ,杂鱼~",
    init() {
      const button = document.getElementById("myButton");
      const that = this; // 保存当前 this
      // 使用普通函数 + 闭包绑定上下文
      button.addEventListener("click", function () {
        console.log(that.message); // 输出 "杂鱼~ ,杂鱼~"
      });
    }
  };
  obj.init();
</script>

分析:

  1. 闭包的作用
  • const that = thisobj 的上下文保存到变量 that 中。
  • 回调函数 function () { console.log(that.message); } 通过闭包访问 that,从而保留 obj.message 的引用。
  1. 对比不保存 this

    button.addEventListener("click", function () {
      console.log(this.message); // this 指向按钮元素,输出 undefined
    });
    

    由于 this 指向按钮元素,this.message 会丢失上下文,输出 undefined

  2. 适用场景

    • 旧版 JavaScript(ES5 及之前)中无法使用箭头函数时。
    • 需要兼容不支持箭头函数的浏览器或环境。

总结

三种方法的对比:

方法特点闭包作用
箭头函数简洁,自动继承父级 this无需显式绑定,闭包自动继承作用域
bind显式绑定 this,创建新函数闭包保存绑定后的 this,确保异步/回调中上下文正确
that = this通过变量保存 this,兼容性好闭包通过变量引用保存上下文,避免丢失

在绑定上下文中,闭包的核心价值在于保存并传递上下文信息。无论是通过箭头函数的隐式绑定、bind 的显式绑定,还是通过变量保存 this,闭包始终是连接外部作用域和内部函数的桥梁。

一句话概括就是:闭包就像一张“照片”,让函数在脱离原始作用域后,依然能“记住”上下文的样貌,从而避免 this 丢失的陷阱。