JavaScript闭包 | 从机制到应用

🤗JavaScript语言中的作用域和闭包(closure)详解

变量的作用域

Javascript语言有特有的”链式作用域”结构(chain scope)

  • 在函数内部可以直接读取全局变量
  • 在函数外部自然无法读取函数内的局部变量

什么是闭包

闭包(Closure)是计算机编程中的一个概念,通常用于描述函数(或者其他可执行的代码块)以及其在定义时所处的环境(词法环境)之间的关系。简单来说,闭包是一个函数和其相关的引用环境的组合体。

在许多编程语言中,函数是第一类对象,可以被当作参数传递、赋值给变量、作为返回值等。当一个函数在定义时引用了其外部作用域中的变量,即使在该变量的作用域消失后,只要该函数还存在,它依然能够访问和操作这些变量,这种函数就形成了闭包

闭包的主要特点包括:

  1. 内部函数引用了外部函数的变量或参数。
  2. 外部函数返回内部函数。
  3. 外部函数的作用域消失后,内部函数仍然可以访问外部函数的变量。

闭包在实际编程中有着广泛的应用,例如在 JavaScript 中,闭包经常用于创建私有变量、模块化设计、实现回调函数等。


闭包产生的条件

1
2
3
4
5
6
7
8
9
10
11
12
function f1() {
var a = 10
function f2() {
a++
console.log(a)
}
return f2
}

var f = f1() // f1()执行的结果是f2;变量f指向函数f2
f() // 11 执行f(),改变变量a的值
f() // 12 继续执行f(),变量a状态会维持

f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收

返回一个函数,并且这个函数对局部变量存在引用,这就形成了闭包的包含关系,维持局部变量的关系


Vue中的闭包

在 Vue.js 中,闭包通常与组件的数据和方法相关联。Vue 组件中的数据和方法通常被封装在组件实例中,这些数据和方法可以通过组件模板进行访问和操作。闭包在 Vue 组件中的应用主要体现在以下几个方面:

  1. 数据封装和访问控制:Vue 组件中的数据可以通过闭包进行封装,从而实现私有数据的效果。通过在组件定义中使用函数来返回数据,可以利用闭包的特性确保数据的封装性和访问控制,避免数据被意外修改或篡改。
1
2
3
4
5
6
7
8
9
10
11
Vue.component('my-component', {
data() {
let privateData = 'Private Data';
return {
getPrivateData() {
return privateData;
}
};
},
template: '<div>{{ getPrivateData() }}</div>'
});
  1. 事件处理函数:在 Vue 组件中,事件处理函数通常需要访问组件实例中的数据和方法。通过使用闭包,可以在事件处理函数中访问组件实例的数据和方法,从而实现事件处理函数与组件实例的绑定。
1
2
3
4
5
6
7
8
9
10
11
12
13
Vue.component('my-component', {
data() {
return {
count: 0
};
},
methods: {
increment() {
this.count++;
}
},
template: '<button @click="increment">Click me</button>'
});
  1. 异步操作:在 Vue 组件中,有时需要进行异步操作,例如发起 HTTP 请求或定时器回调等。闭包可以帮助保存组件实例中的数据和方法的引用,确保在异步操作中能够正确地访问和操作组件数据。
1
2
3
4
5
6
7
8
9
10
11
12
13
Vue.component('my-component', {
data() {
return {
message: 'Initial Message'
};
},
created() {
setTimeout(() => {
this.message = 'Updated Message';
}, 1000);
},
template: '<div>{{ message }}</div>'
});

练习一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var name = "The Window";

  var object = {
    name : "My Object",

    getNameFunc : function(){
      return function(){
        return this.name;
      };

    }

  };

  alert(object.getNameFunc()());
// The window
// 相当于就是this.name
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var name = "The Window";

  var object = {
    name : "My Object",

    getNameFunc : function(){
      var that = this;
      return function(){
        return that.name;
      };

    }

  };

  alert(object.getNameFunc()());
// My object
// getNameFunc的内部函数引用了that,所以getNameFunc作用域不会被销毁,that指向object

掘金大佬文