igCircle Blog

精选

深入理解JS闭包的9大使用场景

2025年5月11日

5分钟阅读

2 次浏览

0 条评论

前端三大件

标签

jsES6

深入理解 JS 闭包的 9 大使用场景,包括返回值、函数赋值、迭代器等

深入理解 JS 闭包的 9 大使用场景

1 返回值(最常用)

js
1//以闭包的形式将 name 返回。
2function fn() {
3 let name = 'hello'
4 return function() {
5  return name
6 }
7}
8let func = fn() //调用,得到函数
9console.log(func()) //再次调用,输出hello

2 函数赋值

在闭包里面给 fn2 函数设置值,闭包的形式把 name 属性记忆下来,执行会输出 hello

js
1let fn2
2function fn() {
3 let name = 'hello'
4 //将函数赋值为fn2
5 fn2 = function() {
6  return name
7 }
8}
9
10fn() // 要先执行进行赋值
11console.log(fn2()) //执行输出fn2,即hello

3 函数参数

用闭包返回一个函数,把此函数作为另一个函数的参数,在另一个函数里面执行这个函数,最终输出 hello

js
1function fn() {
2 let name = 'hello'
3 return function callback() {
4  return name
5 }
6}
7
8let fn1 = fn() //执行函数将返回值(callback函数)赋值为fn1
9
10function fn2(f) {
11 //将函数作为参数传入
12 console.log(f()) //执行函数并输出
13}
14
15fn2(fn1) //调用函数

4.IIFE(自执行函数)

直接在自执行函数里面将封装的函数 fn1 传给 fn2,作为参数调用同样可以获得结果 hello

js
1;(function() {
2 let name = 'hello'
3 let fn1 = function() {
4  return name
5 }
6 //直接在自执行函数里面调用fn2,将fn1作为参数传入
7 fn2(fn1)
8})()
9
10function fn2(f) {
11 //函数组为参数传入
12 console.log(f()) //执行函数,并输出
13}

5 循环赋值

js
1//每秒执行1次,分别输出1-10
2for (let i = 1; i <= 10; i++) {
3 ;(function(j) {
4  setTimeout(function() {
5   console.log(j)
6  }, j * 1000)
7 })(i) //i作为实参传入
8}

6 getter 和 setter

第一次输出 hello 用 setter 以后再输出 world ,这样做可以封装成公共方法,防止不想暴露的属性和函数暴露在外部。

js
1function fn() {
2 let name = 'hello'
3 setName = function(n) {
4  name = n
5 }
6 getName = function() {
7  return name
8 }
9
10 //将setName,getName作为对象的属性返回
11 return {
12  setName: setName,
13  getName: getName,
14  //简写
15  //setName,getName
16 }
17}
18let fn1 = fn() //返回对象,属性setName和getName是两个函数
19console.log(fn1.getName()) //getter
20fn1.setName('world') //setter修改闭包里面的name
21console.log(fn1.getName()) //getter

7.迭代器(执行一次函数往下取一个值)

js
1let arr = ['aa', 'bb', 'cc']
2function increment(arr) {
3 let i = 0
4 return function() {
5  //这个函数每次被执行都返回数组arr中 i下标对应的元素
6  return arr[i++] || '数组遍历完毕'
7 }
8}
9
10let next = increment(arr)
11console.log(next()) //aa
12console.log(next()) //bb
13console.log(next()) //cc
14console.log(next()) //数组值已经遍历完

8 首次区分(相同的参数,函数不会重复执行)

js
1let fn = (function() {
2 let arr = [] //用来缓存的数组
3 return function(val) {
4  if (arr.indexOf(val) == -1) {
5   //缓存中没有则表示需要执行
6   arr.push(val) //将参数push到缓存数组中
7   console.log('函数被执行了', arr)
8   //这里写想要执行的函数
9  } else {
10   console.log('此次函数不需要执行')
11  }
12  console.log('函数调用完打印一下,方便查看已缓存的数组:', arr)
13 }
14})()
15
16fn(10)
17fn(10)
18fn(1000)
19fn(200)
20fn(1000)

执行结果如下:

可以明显的看到首次执行的会被存起来,再次执行直接取。

9 缓存

比如求和操作,如果没有缓存,每次调用都要重复计算,采用缓存已经执行过的去查找,查找到了就直接返回,不需要重新计算

js
1let fn = (function() {
2 let cache = {}
3 let calc = function(arr) {
4  let sum = 0
5  for (let i = 0; i < arr.length; i++) {
6   sum += arr[i]
7  }
8  return sum
9 }
10 return function() {
11  let args = Array.prototype.slice.call(arguments, 0)
12  let key = args.join(',')
13  let result,
14   total = cache[key]
15  if (total) {
16   console.log('从缓存中取:', cache) //打印方便查看
17   result = total
18  } else {
19   //重新计算,并存入缓存同时赋值给result
20   result = cache[key] = calc(args)
21   console.log('存入缓存:', cache) //打印方便查看
22  }
23  return result
24 }
25})()
26fn(1, 2, 3, 4, 5)
27fn(1, 2, 3, 4, 5)
28fn(1, 2, 3, 4, 5, 6)
29fn(1, 2, 3, 4, 5, 8)
30fn(1, 2, 3, 4, 5, 6)

输出结果

参考文章

喜欢这篇文章吗?

加载中...

评论

0

登录后即可参与评论讨论

加载评论中...

相关文章

call bind apply的区别以及源码的手写

call方法的第一个参数也是this的指向,后面传入的是一个参数列表改变this指向后原函数会立即执行,且此方法只是临时改变this指向一次 当第一个参数为null、undefined的时候,默认指向window(在浏览器中) `js function fn(...args) { console.log(this的指向为${this}, args) } let obj = { ...

前端三大件
js手写

2025-08-06

2

0

目录