在我之前的文章中,我们介绍了闭包在封装私有变量、函数防抖和节流场景中的应用。今天,我们将继续深入探讨闭包的另一个核心作用——绑定上下文。
关于封装、防抖、节流的文章,可以看《闭包的应用场景:封装、防抖、节流!》
一、闭包的简单介绍
闭包(Closure)是指函数能够访问并记住其词法作用域,即使该函数在其作用域外执行。通俗地说,闭包就像是一个背包,里面装着函数创建时的环境信息(如变量、参数、this 等),从而在后续调用中依然可以访问这些信息。
闭包的核心特点:
- 延长变量生命周期:即使外部函数执行完毕,数据也不会立即释放,内部函数依然可访问其作用域中的变量。
- 数据封装:通过闭包隐藏内部变量,只暴露受控接口,保护内部数据安全。
- 绑定上下文:利用闭包“记住”外部的
this,避免在回调或异步中丢失上下文。
二、绑定上下文的三种方法
在 JavaScript 中,this 的指向问题一直让很多人头大,尤其是在事件回调或异步操作中,this 容易丢失上下文,而闭包通过“记住”外部作用域的变量和函数,为我们提供了三种经典的解决方案:
- 箭头函数:自动继承父级作用域的
this。 bind方法:显式绑定函数的this。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>
内容分析:
在上面的这段代码中,当你点击按钮后,便会打印杂鱼~,杂鱼~的字样,其中闭包作用如下:
-
箭头函数
() => { console.log(this.message); }其实没有自己的this,它继承init方法中的this(即obj)。 -
这里闭包“记住”了
obj的上下文,可以确保this.message正确指向obj.message。 -
对比传统函数:
button.addEventListener("click", function () { console.log(this.message); // this 指向按钮元素,输出 undefined });其中,传统函数中的
this指向按钮元素,而this.message则会丢失上下文。 -
适用场景:
- 事件监听器(如
click、input)。 - 异步操作(如
setTimeout、Promise)。
- 事件监听器(如
绑定上下文方法2 :bind
bind 方法会创建一个新函数,并永久绑定其 this,保存绑定后的 this,并在后续调用中使用。
案例:
<script>
const person = {
name: "艾伦耶格尔",
sayHello() {
// 使用 bind 绑定上下文
setTimeout(
function () {
console.log(`${this.name}: 机油哒`);
}.bind(this),
1000
);
}
};
person.sayHello(); // 输出 "艾伦耶格尔: 机油哒"
</script>
分析:
-
闭包作用:
.bind(this)创建了一个新函数,其this被永久绑定为person。- 闭包确保即使
setTimeout的回调函数在全局作用域执行,this.name仍指向person.name。
-
对比未绑定的函数:
setTimeout(function () { console.log(`${this.name}: 机油哒`); // this 指向 window,输出 undefined }, 1000);由于
this指向全局对象windows,导致this.name会丢失上下文,输出undefined -
适用场景:
- 需要长期绑定
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>
分析:
- 闭包的作用:
const that = this将obj的上下文保存到变量that中。- 回调函数
function () { console.log(that.message); }通过闭包访问that,从而保留obj.message的引用。
-
对比不保存
this:button.addEventListener("click", function () { console.log(this.message); // this 指向按钮元素,输出 undefined });由于
this指向按钮元素,this.message会丢失上下文,输出undefined。 -
适用场景:
- 旧版 JavaScript(ES5 及之前)中无法使用箭头函数时。
- 需要兼容不支持箭头函数的浏览器或环境。
总结
三种方法的对比:
| 方法 | 特点 | 闭包作用 |
|---|---|---|
| 箭头函数 | 简洁,自动继承父级 this | 无需显式绑定,闭包自动继承作用域 |
bind | 显式绑定 this,创建新函数 | 闭包保存绑定后的 this,确保异步/回调中上下文正确 |
that = this | 通过变量保存 this,兼容性好 | 闭包通过变量引用保存上下文,避免丢失 |
在绑定上下文中,闭包的核心价值在于保存并传递上下文信息。无论是通过箭头函数的隐式绑定、bind 的显式绑定,还是通过变量保存 this,闭包始终是连接外部作用域和内部函数的桥梁。
一句话概括就是:闭包就像一张“照片”,让函数在脱离原始作用域后,依然能“记住”上下文的样貌,从而避免
this丢失的陷阱。