Shyno
Don't be shy,no problem!

在谈闭包前,我们需要知道作用域和变量生存期

作用域和变量生命期

js中的常见作用域分为两种,全局作用域和函数作用域。

let a =1 //全局变量,整个js文件执行完才会被销毁
let test =()=>{
    let b =2 //函数作用域变量,该函数被执行后被摧毁
    console.log('b',b) //2
}
test()
console.log(a) //1
console.log(b) //not defined

 从这个例子我们可以得出,函数内部声明的变量只能在函数内部使用,这就是作用域的限制.而b这个变量也在函数test执行后就被销毁了,我想再拿到这个变量也拿不到了.所以我们需要在调用test函数的时候将b保存起来,在后面就可以用了.

比如

let a =1 //全局变量,整个js文件执行完才会被销毁
let c = 0 //声明一个全局变量
let test =()=>{
    let b =2 //函数作用域变量,该函数被执行后被摧毁
    c=b  //执行函数时将b的值赋值给全局变量c
    console.log('b',b) //2
}
test()
console.log(a) //1
console.log(c) //2
console.log(b) //not defined

 这种方案就是将函数变量的值保存在全局变量里面,这样我在需要使用这个值的时候就可以从全局变量里拿.

优点:变量由函数作用域提升到了全局作用域,可以跨函数使用.

缺点:1.额外的变量声明,容易造成变量污染.

       2.代码的耦合性变高,函数的独立性遭到了破坏

 

作用域链

let a =1 //全局变量,整个js文件执行完才会被销毁
let b= 0 //声明一个全局变量
let test =()=>{
    let b =2 //函数作用域变量,该函数被执行后被摧毁
   let fn= ()=>{
       //这里打印一个b,虽然函数fn中没有b,但是有两个b,一个是函数作用域的b,一个是全局的b
       //就近原则,先在本函数中找,没有就往上面的找,上面没有就往更上面,直到找到一个
       console.log(b) //2
   }
   fn()
}
test()
console.log(a) //1
console.log(b) //0

 

 

闭包

由作用域链可以知道,函数内部的函数可以获得函数的变量.搁这儿绕口令呢?

就是子函数可以获得父函数的变量.假如我需要获得一个函数的变量,那我就给他一个儿子.

但是呢.就算是子函数拿到数据了,执行了就又把内部的变量给摧毁了.所以,我们需要给他一个子函数,而且我还不直接执行子函数,但是又必须在我需要的时候能执行子函数.这时我们就可以返回这个子函数.

let a =1 //全局变量,整个js文件执行完才会被销毁
let b= 0 //声明一个全局变量
let test =()=>{
    let b =2 //函数作用域变量,该函数被执行后被摧毁
   let fn= ()=>{
       //这里打印一个b,虽然函数fn中没有b,但是有两个b,一个是函数作用域的b,一个是全局的b
       //就近原则,先在本函数中找,没有就往上面的找,上面没有就往更上面,直到找到一个
       return b  //只要执行了这个函数,就可以拿到test这个函数内部的b
   }
  return fn  //只要执行了test这个函数,就能获得test函数的子函数fn
}
let myFn = test()
console.log(a) //1
console.log('b',myFn()) //2

 所以,所谓的闭包,从目的的角度来分析就是:利用作用域链,延长函数内部变量生命期.从手段来说就是,在一个函数中返回一个函数,返回的这个函数中包含上层函数的变量.

 

应用

memo函数/reduce函数

即,我现在有一个函数add(1,2),是将1和2加起来的,但是我希望有个memo函数对这个函数进行处理,处理完之后返回一个新函数,这个函数会判断你的传参,假如你的传参已经在之前传过一次了,下次调用的时候直接给你结果而不去计算了.

1.返回一个新函数

2.每次调用都需要去对比以前的数据,所以需要数据保存

先看下以下的代码

let add =(x,y,z)=>{
    return x+y+z
}
//这个函数是用来处理函数的,所以参数是一个函数
let curry =(fn)=>{
    let list = []
    return (x,y,z)=>{
        list.push(fn(x,y,z))
        console.log('list',list)
       return fn
    }
}
let curryAdd =curry(add)
curryAdd(1,2,3)   //list [6]

 到这里为止,我们返回了一个函数,这个函数的入参和之前一摸一样,而且我们也把计算的数据存了起来.那是不是每次存都有用呢

let add =(x,y,z)=>{
    return x+y+z
}
//这个函数是用来处理函数的,所以参数是一个函数
let curry =(fn)=>{
    let list = []
    return (x,y,z)=>{
        list.push(fn(x,y,z))
        console.log('list',list)
       return fn
    }
}
let curryAdd =curry(add)
curryAdd(1,2,3) //list [6]
curryAdd(1,2,4)//list [6,7]
curryAdd(1,2,5)//list [6,7,8]

 所以,我们现在做到了每次计算都能把数据存到一个共同的数组里.所以我们就可以优化一下内部逻辑,存的时候存对象,然后把结果也存起来,每次进来都判断一下,之前没调过相同参数就调用fn,有用过就将保存的结果输出.具体就不细写了.而reduce函数更简单了,每次只要保存上一次的值就行了.

 

 

 

 

 

 

 

  

posted on 2022-03-15 17:25  Shyno  阅读(58)  评论(0编辑  收藏  举报