闭包
犀牛书:函数变量可以保存在函数作用域内 就可以叫做闭包
高级程序设计:闭包指有权访问另一个 函数作用域中的变量的函数(函数没有导出);
你不知道的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
总结
闭包的作用 呢 ,就是延长变量的生命周期
为什么会延长?因为子函数在使用时,父函数的作用域不会被销毁
形成闭包的条件?
- 函数嵌套
- 函数导出
- 能够访问父函数的作用域
- 当函数的执行,导致函数被定义 或 抛出










