闭包及其应用

52次阅读

共计 1732 个字符,预计需要花费 5 分钟才能阅读完成。

什么是闭包?

“在函数内使用其它函数内的变量,是为闭包”

【例 1】function a() {
    let num = 100;
    return function b() {console.log(num)
    }
}
var c = a()
c()   // 100

在例 1 中,a 为外层函数,a 中返回了一个内部函数 b, 函数 b 中使用了 a 中的 num 变量 。此时,函数 b 就形成了一个闭包。

闭包的用处是什么?

在 JavaScript 中,作用域链使得子作用域能够读取到父作用域的变量【见例 2】,而反过来,父作用域无法直接获取子作用域的变量【见例 3】。

【例 2】let num = 100
function a() {console.log(num)
}
a()   // 100【例 3】function a() {let num = 100}
console.log(num)   // error: not defined

当我们想在父作用域使用子作用域的值时该怎么办呢?

答:那就在函数内部再定一个函数,将其作为返回值即可【例 4】。

【例 4】function a() {
    let num = 100
    return function b() {return num}
}
var x = a()
console.log(x())   // 100

例 4 中,函数 b 作为函数 a 的内部函数,可以访问 a 中的变量 num,于是我们将函数 b 作为返回值,我们通过操作 b 来访问 a 的变量。

上述利用闭包的特性来实现外部对内部的访问,便是闭包最基本的应用。

闭包的应用场景

1. 封装变量以控制权限

一般来说,我们只想让外界使用我们暴露出去的方法,而方法涉及到的变量计算想要封装在函数内部,以避免被有意 / 无意地修改变量值,此时,便可利用闭包来进行变量封装以控制权限【例 5】。

【例 5】// 判断输入的变量是否是第一次输入
function isFirstVisit() {let _list = []
    return function (id) {if (_list.indexOf(id) >= 0) {console.log("Exist!")
        } else {_list.push(id)
            console.log("Added~")
        }
    }
}
let firstVisit = isFirstVisit()
firstVisit(5)   // Added~
firstVisit(5)   // Exist!
firstVisit(10)   // Added~

将 isFirstVisit 的返回值赋给 firstVisit,然后通过使用 firstVisit 进行判断,因为暴露给外界使用的是 firstVisit 函数,所以只能使用该函数定义的方法,而无法对 _list 进行任何修改,保证了数据的私密性。

2. 函数柯里化

函数柯里化就是把一个函数接收多个参数进行处理,转变为接收单一参数,然后返回一个新函数接收下一个参数【见例 6】。

【例 6】// 普通函数
function sum(x, y) {return x + y}
// 函数柯里化
function curryingSum(x) {return function(y) {return x + y}
}

这里函数柯里化后的内部函数所使用的 x,就是其父作用域的变量,此时的内部函数就是一个闭包。

函数柯里化应用之 参数复用

【例 7】function check(reg, txt) {return reg.test(txt)
}

// 检验是否含有数字
check(/\d+/g, 'test')   // false
check(/\d+/g, 'abc')   // false
check(/\d+/g, '2dd')   // true

// 柯里化后
function curryingCheck(reg) {return function(txt) {return reg.test(txt)
    }
}

let hasNumber = curryingCheck(/\d+/g)

hasNumber('test')      // false
hasNumber('abc')   // false
hasNumber('2dd')   // true

通过上例可以看到,直接使用 check 函数,第一个参数“正则判断语句”在每次使用时都需要输入,既不美观也不方便。对其进行函数柯里化后,只需输入一次正则判断语句,便可对判断函数进行复用。

闭包带来的问题

内存占用

由于闭包使用其它函数内的变量,所以垃圾回收机制不会对闭包相关的函数进行回收,占用内存空间。

正文完
 0