闭包的实际应用

 

 

1、使用闭包实现累加函数

      function addFn() {
        let num = 0
        return function() {
          console.log(num++)
        }
      }
      const add = addFn()
      add() // 0
      add() // 1
      add() // 2

  或

      function addFn() {
        let num = 0
        return function() {
          return num++
        }
      }
      const add = addFn()
      console.log(add()) // 0
      console.log(add()) // 1
      console.log(add()) // 2

 

2、使用闭包实现私有变量

  (1)通过set/get设置和读取变量

      function fn() {
        const obj = {}
        return {
          set: function(key, val) {
            obj[key] = val
          },
          get: function(key) {
            return obj[key]
          }
        }
      }
      const f = fn()
      console.log(f.get('name')) // undefined
      f.set('name', '小明')
      console.log(f.get('name')) // 小明

  构造函数的写法

      function Fn() {
        const obj = {}
        this.set = function(key, val) {
          obj[key] = val
        }
        this.get = function(key) {
          return obj[key]
        }
      }
      const f = new Fn()
      f.set('name', '小明')
      console.log(f.get('name'))

 

  (2)通过add/minus实现加减

      function counter() {
        let count = 0
        return {
          add: function() {
            count++
          },
          minus: function() {
            count--
          },
          value: function() {
            return count
          }
        }
      }
      const myCounter = counter()
      console.log(myCounter.value()) // 0
      myCounter.add()
      myCounter.add()
      console.log(myCounter.value()) // 2
      myCounter.minus()
      console.log(myCounter.value()) // 1

  第二种写法

      const counter = (() => {
        let count = 0
        return {
          add: function() {
            count++
          },
          minus: function() {
            count--
          },
          value: function() {
            return count
          }
        }
      })()
      console.log(counter.value()) // 0
      counter.add()
      counter.add()
      console.log(counter.value()) // 2
      counter.minus()
      console.log(counter.value()) // 1

 

  (3)保存用户点击链接的次数和地址----设计思路:模块化

    tracker.js

var accessCounter = 0
var adAccessRec = []
// CommonJS语法导出
module.exports = {
  storeAccessPage: function(page) {
    adAccessRec.push(page)
  },
  checkAdAccessRec: function() {
    return adAccessRec
  },
  increaseCounter: function() {
    accessCounter++
  },
  getAccessCounter: function() {
    return accessCounter
  }
}

    test.js

// var tracker = (function() {
//   var accessCounter = 0
//   var adAccessRec = []
//   return {
//     storeAccessPage: function(page) {
//       adAccessRec.push(page)
//     },
//     checkAdAccessRec: function() {
//       return adAccessRec
//     },
//     increaseCounter: function() {
//       accessCounter++
//     },
//     getAccessCounter: function() {
//       return accessCounter
//     }
//   }
// })()
// 将tracker的内容封装成模块
var tracker = require('./tracker') // CommonJS语法导入

console.log(tracker.getAccessCounter()) // 0
tracker.increaseCounter()
tracker.increaseCounter()
console.log(tracker.getAccessCounter()) // 2

var page1 = 'xxx'
var page2 = 'yyy'
tracker.storeAccessPage(page1)
tracker.storeAccessPage(page2)
console.log(tracker.checkAdAccessRec()) // [ 'xxx', 'yyy' ]

 

 

3、回调函数的本质是闭包

<style>
  #box {
    position: absolute;
  }
</style>
<div id="box">hello world</div>
<script>
  function animate(elementId) {
    const elem = document.getElementById(elementId)
    let count = 0
    let timer = setInterval(function a() {
      if (count < 10) {
        elem.style.top = elem.style.left = count + 'px'
        count++
      } else {
        clearInterval(timer)
        timer = null
      }
    }, 300)
  }
  animate('box')
</script>

  a函数就是一个闭包,它可以访问animate函数中的elem和count

 

4、值缓存

      const cacheBox = (function() {
        const cache = {}
        return {
          search: function(id) {
            if (id in cache) {
              return '1结果' + cache[id]
            }
            const result = dealFn(id)
            cache[id] = result
            return '2结果' + result
          }
        }
      })()
      function dealFn(id) {
        console.log('这是一段比较耗时的操作')
        return id
      }
      // 第一次执行cacheBox.search(100),cache中没有100,走dealFn函数,比较耗时;第二次执行cacheBox.search(100),cache中已经存在100,直接输出
      let res = cacheBox.search(100)
      console.log(res) // 2结果100
      let res1 = cacheBox.search(100)
      console.log(res1) // 1结果100

 

5、for循环中的定时器

  

  打印结果都是3

  *原因:定时器中的函数(回调函数---本质是闭包)使用了本函数外层的i,导致这个i被缓存到内存中不被销毁,这里并不是因为异步而导致的

 

  如果需要打印对应的i:

    (1)使用闭包

      

     (2)【扩展】定时器中放函数的调用,不受定时器控制,直接执行打印

      

     (3)【扩展】使用es6的let,具有块级作用域

      

 

6、打印li对应的下标

  DOM:

    <ul id="ul">
      <li>第一个</li>
      <li>第二个</li>
      <li>第三个</li>
    </ul>

  js:

      const ul = document.getElementById('ul')
      const lis = ul.getElementsByTagName('li')
      for (var i = 0; i < lis.length; i++) {
        lis[i].onclick = function() {
          console.log(i)
        }
      }

  此时,每次点击li打印的下标都是3。循环的执行是一瞬间的,而事件的回调函数是点击后才触发,此时i已经是3了

  

  如果需要打印对应的i:

    (1)使用闭包(闭包将本函数外的变量i长期储存在内存中)

      

      或

      

    (2)【扩展】使用es6的let

      

    (3)【扩展】循环时保存i

      

    (4)【扩展】使用bind

      

    (5)【扩展】使用forEach代替for

      

 

7、定时器函数传参

      // 定时器中function无法直接实现传参
      setTimeout(params => {
        console.log(params) // undefined
      }, 100)

      // 通过闭包实现定时器传参
      function fn(params) {
        return function() {
          console.log(params) // 100
        }
      }
      var one = fn(100)
      setTimeout(one, 100)

  【扩展】利用定时器第三个参数传参

      // 定时器的第三个参数可以传参
      function one(params) {
        console.log(params) // 200
      }
      setTimeout(one, 100, 200)

 

8、computed传参

  computed中函数传参

 

posted @ 2021-10-11 11:51  吴小明-  阅读(433)  评论(0编辑  收藏  举报