闭包应用
封装一个具有特定功能的JS模块,将所有数据和功能封装在函数内部,只向外暴露指定的几个方法或者对象,使用者只需通过约定的方法来实现对应的功能。
下面来看代码
function NumOperation(params) {
let num = 10
function readNum() {
return num
}
function addNum(a) {
num += a
}
function subNum(b) {
num -= b
}
return {readNum,addNum,subNum}
}
let temp = NumOperation()
console.log(temp.readNum()) //10
temp.addNum(2)
console.log(temp.readNum()) //12
temp.subNum(3)
console.log(temp.readNum()) //9
temp = null
这里外面无法直接访问到num,只能通过暴露的几个接口去访问。代码应该很好理解
再看另一种做法
(function NumOperation(window) {
let num = 10
function readNum() {
return num
}
function addNum(a) {
num += a
}
function subNum(b) {
num -= b
}
window.NumModel = {readNum,addNum,subNum}
})(window)
console.log(NumModel.readNum()) //10
NumModel.addNum(2)
console.log(NumModel.readNum()) //12
NumModel.subNum(3)
console.log(NumModel.readNum()) //9
这种方法不用像第一种那样去调用函数,这是一个立即执行函数,在引入js代码的时候就会立即执行。
函数的柯里化
什么是柯里化:将一次性传入多个参数的函数转换成一次传入一个参数的函数。就比如下面的验证邮箱,一般函数直接传入正则表达式和要匹配的字符,可是这样做每次都得写一遍正则表达式,这时候就可以用到柯里化,设置分步传参,看下面代码。
//需求:实现验证手机号,和邮箱
function Viv(temple) {
return function (readViv) {
return temple.test(readViv)
}
}
//验证手机号
let template1 = /^1[0-9]{10}$/
let phoneViv = Viv(template1)
let phone1 = '14747231234'
let phone2 = '1475678120'
let phone3 = '01212345678'
console.log(phoneViv(phone1))
console.log(phoneViv(phone2))
console.log(phoneViv(phone3))
let template2 = /^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/
let email1 = '2101055795@qq.com'
let email2 = '12.edu.12@qq.com'
let email3 = '23qq.com'
let emailViv = Viv(template2)
console.log(emailViv(email1))
console.log(emailViv(email2))
console.log(emailViv(email3))
求和
// 这个是优化的版本
function add() {
let sum = 0
let fn = function () {
Array.prototype.forEach.call(arguments, item => (sum += item))
console.log(sum)
return fn
}
fn(...arguments)
return fn
}
let a = add(1, 2)(3, 4)(8)(4, 5, 6, 7)
function add() {
let arg = Array.prototype.slice.call(arguments)
function _add() {
if(arguments.length>0){
arg.push(...arguments)
return _add
}else {
return arg.reduce((pre,cur)=>{
return pre+cur
},0)
}
return _add
}
return _add
}
console.log(add(6)(7)(1,2)())
可见本质上还是用到闭包的特性。
闭包优缺点
函数执行完之后,函数内部的局部变量还没有被释放,占用内存时间会延长,这就容易导致内存泄漏问题,所以要在使用完之后及时的释放内存。如果内存泄漏积累多了就导致了内存溢出。
引申一个知识点this
var name = 'lalal'
let obj = {
name :'heihei',
getName : function () {
return function () {
console.log(this.name)
}
}
}
obj.getName()() // lalal
var name = 'lalal'
let obj1 = {
name :'heihei',
getName : function () {
let that = this
return function () {
console.log(that.name)
}
}
}
obj1.getName()() // heihei
第一个和第二个输出明显是不同的,这是因为执行上下文是不同的,第一个调用getName返回函数,再执行返回函数,返回函数的执行是在window环境下执行的,它处于的上下文是window,所以this.name是window中的name, 第二个再调用getName的时候函数this是指向obj对象,此时用闭包的原理用that存储了当前的上下文obj1,再执行返回函数的时候,返回函数的执行虽然再window上下文,可是里面的that指向的是obj1的上下文。
这里相关的可以看我的博客