前置知识:Javascript的词法作用域
function createIncrementor(start) {
return function () {
return start++;
};
}
var inc = createIncrementor(5);
inc() // 5
inc() // 6
inc() // 7
概念
MDN:
一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure)。也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域。在 JavaScript 中,每当创建一个函数,闭包就会在函数创建的同时被创建出来。
JavaScript权威指南:
从技术的角度讲,所有的JavaScript函数都是闭包:它们都是对象,它们都关联到作用域链。
JavaScript高级教程:
闭包是指有权访问另一个函数作用域中的变量的函数。
你不知道的JavaScript:
当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数在当前词法作用域之外执行。
- 闭包就是子函数可以有权访问父函数的变量、父函数的父函数的变量、一直到全局变量。归根结底,就是利用词法(静态)作用域,即作用域链在函数创建的时候就确定了。
- 子函数如果不被销毁,整条作用域链上的变量仍然保存在内存中。
闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。
JavaScript中的闭包:https://segmentfault.com/a/1190000011612140 https://mp.weixin.qq.com/s/G8r5DL19ypOY-HCRpqv4BA
原理
function func() {
const one = '111';
function func2() {
const two = '222';
function func3 () {
const three = '333';
debugger
console.log(one)
console.log(two)
}
return func3;
}
return func2;
}
const func2 = func();
func2()()
闭包是返回函数的时候扫描函数内的标识符引用,把用到的本作用域的变量打成 Closure 包,放到 [[Scopes]] 里。
所以上面的函数会在 func3 返回的时候扫描函数内的标识符,把 one、two 扫描出来了,就顺着作用域链条查找这俩变量,过滤出来打包成两个 Closure(因为属于两个作用域,所以生成两个 Closure),再加上最外层 Global,设置给函数 func3 的 [[scopes]] 属性,让它打包带走。
调用 func3 的时候,JS 引擎 会取出 [[Scopes]] 中的打包的 Closure + Global 链,设置成新的作用域链, 这就是函数用到的所有外部环境了,有了外部环境,自然就可以运行了。
使用
//封装私有变量
function Person(name) {
var _age;
function setAge(n) {
_age = n;
}
function getAge() {
return _age;
}
return {
name: name,
getAge: getAge,
setAge: setAge
};
}
var p1 = Person('张三');
p1.setAge(25);
p1.getAge() // 25