0、此处为本篇最重要的部分
闭包:私有状态,可和函数联系起来,形成黑盒调用
最重要的部分:JavaScript 中的每个函数都维护对其外部词法环境的引用。此引用用于配置调用函数时创建的执行上下文。此引用使函数内部的代码能够“查看”函数外部声明的变量,而不管函数何时何地被调用
此时引出一个新的内容:作用域链
如果一个函数被一个函数调用,而该函数又被另一个函数调用,那么就会创建一个指向外部词法环境的引用链。这个链称为作用域链。
1、再谈谈概念
t0:内部 return 后的函数 可以使用 外层作用域的变量和函数调用,但外部不能反过来!
因为 外部调用函数时,不能看到内层的 闭包部分,被视为一块不可见的‘状态框’,而这个状态框为一个黑盒部分
1、相信这个概念你肯定多次看到,并且在实际工作中使用,vue date(){} 这是个典型的闭包,相信你早就注意到了
2、闭包是一个函数,和对该函数外部作用域的引用(词法环境)
那么此处词法环境又是什么呢?
词法环境是每个执行上下文(堆栈帧)的一部分,是标识符(即局部变量名称)和值之间的映射(简单理解为一个执行环境)
3、还有一个有意思的事情
JavaScript 中的函数可以像变量(一等函数)一样传递,这意味着这些功能和状态的配对可以在您的程序中传递
4、如果没有闭包,函数调用直接将变得麻烦而且混乱
5、如果您希望函数始终可以访问私有状态,则可以使用闭包
6、可多次访问,在另一个函数中声明一个函数,那么外部函数的局部变量在从它返回后仍然可以访问
2、举几个例子
// 第一个简单例子
// 闭包维护对原始变量本身的引用
function foo() {
let x = 42;
let inner = function () {
console.log(x);
};
x = x + 1;
return inner;
}
var f = foo();
f(); // logs 43
// 第二个例子
// 每次调用 createObject 时,都会创建一个新的执行上下文(堆栈帧)和一个全新的变量 x,并创建一组新的函数
function createObject() {
let x = 42;
return {
log() {
console.log(x);
},
increment() {
x++;
},
update(value) {
x = value;
},
};
}
const o = createObject();
o.increment();
o.log(); // 43
o.update(5);
o.log(); // 5
const p = createObject();
p.log(); // 42
// 第三个例子
// 每次循环时,都会创建一个新函数inner,它关闭i。但是因为 var i 被提升到循环之外,所有这些内部函数都关闭了同一个变量
function foo() {
var result = []
for (var i = 0; i < 3; i++) {
result.push(function inner() { console.log(i) } )
}
return result
}
const result = foo()
// The following will print `3`, three times...
for (var i = 0; i < 3; i++) {
result[i]()
}
3、值得注意的事情
1、JS中创建函数就会形成一个闭包
声明函数时会创建一个闭包;这个闭包用于在调用函数时配置执行上下文
每次调用函数时都会创建一组新的局部变量
2、从另一个函数内部返回一个函数 这是最常用的闭包
3、每当您在函数内使用 eval() 时,都会使用闭包。你 eval 的文本可以引用函数的局部变量,在非严格模式下,你甚至可以使用 eval('var foo = …') 创建新的局部变量(用的少)
4、当您在函数内使用 new Function(...)(Function 构造函数)时,它不会关闭其词法环境:而是关闭全局上下文。新函数不能引用外部函数的局部变量
5、JavaScript 中的闭包就像在函数声明点保留对作用域的引用(而不是副本),而后者又保留对其外部作用域的引用,依此类推,一直到顶部的全局对象作用域链。