闭包的理解
闭包是一个晦涩难懂的概念,很难确定到底是一个什么东西,本文探究 JavaScript 中的闭包
《你不知道的 JavaScript》: 当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前此法作用域之外执行
《JavaScript 高级程序设计》: 闭包指的是那些引用了另一个函数作用域中变量的函数,通常是在嵌套函数中实现的
《JavaScript 权威指南》: 函数对象与作用域(即一组变量绑定)组合起来解析函数变量的机制,在计算机科学文献中被称作闭包
《MDN》:一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure)。也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域。在 JavaScript 中,每当创建一个函数,闭包就会在函数创建的同时被创建出来。
大概理解闭包就是能够读取其他作用域变量,这个作用域与其函数的组合
其实闭包的理解:闭包让你可以在一个内层函数中访问到其外层函数的作用域
说到闭包就应该说一下js的作用域
作用域
作用域就是用于存储并方便之后查找变量的规则,在 JavaScript 中的作用域为词法作用域,简单来如何查找变量是由你的代码写在哪里来决定的,而作用域链就是当在你自身的作用域找不到该变量便会向上级作用域查找,如果还找不到就逐级向上查找,直到在全局作用域也找不到的话就抛出 ReferenceError 异常
var a = 1;
function fun1() {
var a = 2;
console.log(a);
};
function fun2() {
console.log(a);
};
fun1() // 2
fun2() // 1
全局作用域
在浏览器中,全局作用域就是 window 对象,我们在全局作用域声明一个变量,会自动添加到 window 属性中
window.a // undefined
var a = 1;
window.a // 1
1
函数作用域
在 JavaScript 函数是有自己的作用域的,在函数作用域的外部无法直接访问函数作用域内部的变量
var a = 1;
function fun1() {
var a = 2;
cosnole.log(a) // 2
}
fun1();
console.log(a) // 1
块作用域
ES5 中的块作用域
一般来说,在 ES5 中是没有块作用域的
var a = 1;
{
var a = 2;
console.log(a); // 2
}
console.log(a); // 2
但也不是完全没有,其中 with 和 try/catch 语法是拥有块级作用域
可以看出例子中 catch 中的 a 是块级作用域,只在 catch 代码块内,而全局作用域的 a 并没有被覆盖
var a = 1;
try {
throw 2;
} catch(a) {
console.log(a); // 2
}
console.log(a); // 1
ES6 中的块作用域
在 ES6 中,增加了 let 和 const 关键字用于声明块级作用域
let a = 1;
const b = 2
{
let a = 3;
const b = 4
console.log(a); // 3
console.log(b); // 4
}
console.log(a) // 1
console.log(b); // 2
let d = 1;
console.log(window.d) // undefined
//注意是定义一个局部变量 d,它并不会直接添加到 window 对象上
了解完作用域就可以了解闭包了
闭包
先看一段代码
function fun1() {
var a = 1;
return function fun2() {
console.log(a);
}
}
var a = fun1()
a(); // 1
在立即执行函数表达式 fun2() 中通过使用了 fun1() 的变量 a ,则此时便产生了闭包
在函数返回函数的时候,他是可以访问返回函数的上层函数作用域也就是js的闭包机制,
再来一个案例
function fun1() {
var a = 1;
function fun2() {
console.log(a);
}
fun2();
}
fun1();
例子中我们在 fun1() 中我们定义了 fun2() ,然后我们在 fun1() 作用域中又直接调用了 fun2() ,那么此时是否产生了闭包呢???
我的想法是:fun2 虽然在 fun1 中被定义,但由于它被立即调用,没有被返回或赋值给外部变量,因此在这个具体的例子中,并没有产生显式的闭包。
闭包通常涉及到在函数内部定义一个函数,并将这个内部函数作为返回值,或者被赋值给外部的变量,这样的情况才会形成明显的闭包。在你的例子中,fun2 虽然能够访问 fun1 内部的变量 a,但它没有逃逸到外部,因此没有形成显式的闭包。
总的来说,在 fun1 内调用 fun2 并不会创建显式的闭包,但是 fun2 仍然可以访问 fun1 内的变量 a,因为 JavaScript 函数都具有词法作用域,内部函数可以访问外部函数的变量。
闭包的应用
模块
模块主要就是对逻辑进行分块,各自封装,相互独立,然后自行决定对外暴露什么,同时自行决定引入执行那些外部代码.
而通过闭包,我们即可以很简单的完成最基本的模块的封装
模块主要就是对逻辑进行分块,各自封装,相互独立,然后自行决定对外暴露什么,同时自行决定引入执行那些外部代码.
而通过闭包,我们即可以很简单的完成最基本的模块的封装