闭包的实际应用
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传参