闭包(回顾)

在这里插入图片描述

概念

闭包(closure)指有权访问另一个函数作用域中变量的函数 — Javacript高级程序设计 p309
简单理解,一个作用域可以访问另一个函数内部的私有变量

// 其中 test就是一个闭包
function fn(){
  var num = 10
  function test () { console.log(num) }
}

作用

保护作用

在上下文中会有一些私有的变量AO(XXX),这些私有变量和外界的变量不会冲突(互不影响)

应用

  1. 团队协作开发中,为防止全局变量的冲突污染,建议每个开发者,把自己的代码放到一个闭包中(立即执行函数即可)保护起来
  2. 封装一个插件或类库等,防止自己定义的变量和方法与用户定义的冲突,需要把所写的代码放到一个闭包中,例如 Jquery

保存作用

某些情况下,上下文中的某些内容被为外界占用后,当前上下文并不会出栈销毁,这样开一把上下文中的一些信息存储起来

应用

1.在某些需求下,经常需要形成一个闭包,存储一些值(且不能销毁),供后面的程序运行使用,例如 惰性函数柯里化函数(bind)compose函数等

优缺点

优点:保护和保存代码不受污染
缺点:会产生不销毁的上下文,导致栈/堆内存消耗过大,也会导致内存泄露(该内存空间使用完毕之后未回收),影响页面的运行性能

循环输出问题

for(var i = 1; i <= 5; i ++){
  setTimeout(function timer(){
    console.log(i) // 6 6 6 6 6 6
  }, 0)
} 

原因

  • 1.var 属于函数作用域,在for循环属于块级作用域,存在变量提升机制, var 命令实际上只会执行一次
  • 2.因为setTimeout为宏任务,由于JS中单线程eventLoop机制,在主线程同步任务执行完后才去执行宏任务,因此循环结束后setTimeout中的回调才依次执行,但输出i的时候当前作用域没有,往上一级再找,发现了i,此时循环已经结束,i变成了6。
// 可以理解为
for (var i = 0; i <= 5; i++) {}
console.log('哦吼~'); // 主线程结束
// 进入事件队列执行, 此时的 i 已经是6了
setTimeout(function () {
    console.log(i)
})
setTimeout(function () {
    console.log(i)
})
setTimeout(function () {
    console.log(i)
})
setTimeout(function () {
    console.log(i)
})
setTimeout(function () {
    console.log(i)
})

解决方法

  • 1.利用IIFE(立即执行函数)
for(var i = 1;i <= 5;i++){
  (function(j){
    setTimeout(function timer(){
      console.log(j)
    }, 0)
  })(i)
}
  • 2.定时器的第三个参数, 作为timer函数的第一个函数参数
for(var i=1;i<=5;i++){
  setTimeout(function timer(j){
    console.log(j)
  }, 0, i)
}
  • 3.使用ES6中的let,let属于块级作用域
for(let i = 1; i <= 5; i++){
  setTimeout(function timer(){
    console.log(i)
  },0)
}
// i = 1
{
  setTimeout(function timer(){
    console.log(1)
  },0)
}
// i = 2
{
  setTimeout(function timer(){
    console.log(2)
  },0)
}
// i = 3
...

命名空间

概念

给每个对象的堆内存起一个变量名,这个变量就是’命名空间’

作用

  1. 避免变量,函数名的冲突
var a = 1
var utils = { 方法... }
  1. 单例设计模式 (闭包)
    各板块暴露到全局只有一个变量,避免全局变量的污染,实现了闭包之间的方法公用性
var utils = (function(){
  var num = 10
  function test(){console.log(num)}
  //函数...
  return {
    test // 相当于 test:test
    // 方法...
  }
}())
utils.test()  // 直接用命名空间 utils 调用其中方法
  1. 基于 window.xxx = 方法 ,暴露到全局
    这种方法暴露到全局对象GO上,也可能导致方法之间的冲突

柯里化函数

概念

柯里化(Currying):把接受多个参数的参数变换成接受一个单一参数(最初函数的第一个参数)
的函数,并且返回接受余下的参数且返回结果的新函数的技术

作用

参数复用,提前返回和延迟执行

例题

  • 实现 fn(11).add(1).mine(2).take(1).divide(8).res()
    函数实现计算器
const fn = (num) => {
    return {
        add: (n) => fn(num + n),
        mine: (n) => fn(num - n),
        take: (n) => fn(num * n),
        divide: (n) => fn(num / n),
        res: () => num
    }
}
  • 实现 add(1)(2)(3)(4)
function add(a, b, c, d) {
    return [...arguments].reduce((sum, x) => sum + x)
}
function currying(fn){
    let len  = fn.length
    let args = []
    return function HOC(...newArg){
        args = [...args,...newArg]
        if(args.length <len){
            return HOC
        }else{
            return fn.apply(null,args.slice(0,len))
        }
    }
}
let addCurry =  currying(add)

let res = addCurry(1)(2)(3)(4)
  • 实现 add(1)(2)(3)(4)…(100)()
function add(...args) {
    return args.reduce((sum, x) => sum + x)
}

function currying(fn) {
    let args = []
    return function HOC(...newArg) {
        if (newArg.length) {
            args = [...args, ...newArg]
            return HOC
        } else {
            return fn.apply(null, args)
        }
    }
}

let addCurry = currying(add)

let res = addCurry(1)(2)(3)(4)(5)()
console.log(res)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柳晓黑胡椒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值