闭包
犀牛书:函数变量可以保存在函数作用域内 就可以叫做闭包
高级程序设计:闭包指有权访问另一个 函数作用域中的变量的函数(函数没有导出);
你不知道的javascript:当函数可以记住并访问所在的词法作用域时,就产生闭包,即使函数是在当前词发作用域之外执行
function foo(){
let n = 0;
}
//例子一
function(){
let n = 0;
function bar(){
console.log(n)
}
}
//例子二
// 闭包
function fn(callback){
let num = 18
return callback(num)
}
fn(function(){
console.log([].shift.call(arguments));
})
闭包的作用:是延长变量的生命周期
形成闭包的条件?
- 函数嵌套
- 函数导出
- 能够访问父函数的作用域
- 当函数的执行,导致函数被定义 或 抛出
闭包思考题
- test 里边能不能拿到n ?
- 为什么拿不到n?
- 如果我想要拿到应该怎么办
function fn(callback){
let n = 99
callback()
}
function test(){
console.log(n);
}
fn(test)
//可能见得多的都是这样 和上边有稍微不一样
function fn(callback){
let n = 99
return callback(n)
}
let res = fn(n => n)
console.log(res);
个人理解,解答:
1. 不能
2. 因为test 函数在当前函数体内 的 作用域 找到 n这个变量,然后就会往上一级找 也没有找到,
3. 如果想拿到,可以在callback()里边传入,然后test接收 `或者` 返回一个函数 定义 然后外边再调用,之所以可以打印到n的值 ,实际还是因为 函数还在调用 上一级的 函数作用域还没有被销毁,进而验证了 闭包的作用就是为了延长变量的生命周期
闭包的形式
1.函数的返回值是函数
function foo(fn){
let n = 0;
return function(){}
}
2.返回的变量是函数
function foo(){
let n = function(){}
return n
}
foo()
3.全局变量定义的闭包
let outter;
function foo(){
let a = 10;
outter = function(){
console.log(a)
}
}
foo();
outter()
4.函数参数的方式 个人感觉这种挺常见
let inner = function(fn){
console.log(fn())
}
;(function(){
let b = "local";
let n = function(){
return b
}
inner(n)
})()
5.循环赋值
function foo(){
let arr = []
for (let i = 0; i < 10; i++) {
arr[i] = function(){
console.log(i);
}
}
return arr
}
let res = foo();
res.forEach(fn => {
fn()
})
个人理解为什么闭包能延伸变量的作用域范围?
- 因为函数每次调用都会创建一个新作用域,且子函数被使用时父级环境将被保留
- 使用
let/const
可以将变量声明在块作用域中(放在新的环境中,而不是全局中)
出个题理解下闭包
function fun(n,o){
console.log(o);
return {
fun(m){
return fun(m,n)
}
}
}
//1.问 打印什么
let a = fun(0)
a.fun(1)
a.fun(2)
a.fun(3)
//undefined 0 0 0
- 第一个undefined不用说了 ,后边为啥是0?
- 因为子函数被使用时父级环境(就是作用域)将被保留,然后会找到 父函数中的n ,n当时传入的是0 所以就是打印0
我们分析下作用域
//伪代码
//执行 let a = fun(0)
|-- fun(n,o)作用域
n = 0 ,o = undefined //内部有函数执行 此作用域被保留
//执行a.fun(1)
a : {
fun(1) return fun(m,n) //(1,0)
//这个时候 父函数的作用域会被保留m = 1 ,然后会向上找n,找到 n = 0(这里的n就是fun函数的 第一个形参 ,不要被迷惑了)
}
//执行a.fun(2)
a : {
fun(2) return fun(m,n) //(2,0) n会向上找
}
//执行a.fun(3)
a : {
fun(2) return fun(m,n) //(3,0) n会向上找
}
改成这样子呢?
function fun(n,o){
console.log(o);
return {
fun(m){
return fun(m,n)
}
}
}
//2.问 打印什么
let b = fun(0).fun(1).fun(2).fun(3);
//undefined 0 1 2
我们也用伪代码分析下作用域
//伪代码
//执行fun(0)
function(n,o){
//n = 0 , o = undefined 该作用域变量没有被销毁
console.log(o) //打印 undefined
//执行fun(0).fun(1)
function(n,o){
//n = 1,o=0
console.log(o) //打印 0
//执行fun(0).fun(1).fun(2)
function(n,o){
//n = 2 ,o = 1
console.log(o) //打印 1
//执行fun(0).fun(1).fun(2).fun(3)
function(n,o){
//n = 3 ,o = 2
console.log(o) //打印 2
}(3,2)
}(2,1) //此时上一个作用域里找到了 n = 1
}(1,0) //此时上一个作用域里找到了 n = 0
}(0)
- 一层一层嵌套 上一个作用域还没有被销毁
看到这里是不是可以理解为 闭包的作用 呢 ? 就是延长变量的生命周期,
为什么会延长?因为子函数在使用时,父函数的作用域不会被销毁
再改一下
function fun(n,o){
console.log(o);
return {
fun(m){
return fun(m,n)
}
}
}
let c = fun(0).fun(1);
c.fun(2);
c.fun(3);
- 看到这题应该可以直接出答案了
- undefine 0 1 1
总结
闭包的作用 呢 ,就是延长变量的生命周期
为什么会延长?因为子函数在使用时,父函数的作用域不会被销毁
形成闭包的条件?
- 函数嵌套
- 函数导出
- 能够访问父函数的作用域
- 当函数的执行,导致函数被定义 或 抛出