什么是闭包?
看下面代码:
const fn = ()=>{
let arr = [1,2]
setTimeout(() => {
console.log(arr);
}, 1000);
return arr
}
fn()[1] = 1
结果:一秒后打印[1,1]
分析:
fn函数执行完返回的是一个存放arr的地址,故fu()[1]是可以对函数中的arr进行修改和访问的,将arr[1]修改,一秒后执行延时器自然打印的是[1,1]
结论:
由此观之,当一个函数的返回值是引用值(数组,对象,函数),并且其返回值被外界所引用时,即可形成一个闭包
在行成闭包时,内存发生了什么?
内存图:
图解:
函数正常释放: 划一块内存,执行代码,将函数内局部变量进行赋值,最终释放内存
闭包函数无法释放: 由于外部还在引用自身返回的引用值的地址,无法进行释放内存操作
闭包的优缺点:
优点:加强封装性,便于管理,可以将一个变量存在内存中(变量私有化),并不会污染全局环境
缺点:因为内存无法得到释放,内存开销大,而且容易造成内存泄漏
应用: 学生成绩的管理
function student() {
let mark = {
'math':null,
'english':null,
'chinese':null
}
return {
// 修改成绩
set(subject,score){
mark[subject]=score
},
// 求平均分的方法
mean(){
let allScore = 0
for (const key in mark) {
allScore+=mark[key]
}
return allScore/Object.keys(mark).length
},
get(){
return mark
}
}
}
let student1 = student()
student1.set('math',92)
student1.set('english',91)
student1.set('chinese',96)
let student2 = student()
student2.set('math',88)
student2.set('english',94)
student2.set('chinese',100)
console.log(student1.get()); // {math: 92, english: 91, chinese: 96}
console.log(student2.get()); // {math: 88, english: 94, chinese: 100}
console.log(student1.mean()); // 93
console.log(student2.mean()); // 94
// 将student1(2)指向null 不再引用函数返回的地址,让内存得到释放
student1=null
student2=null
函数式编程
函数式编程的概念不能一言蔽之,大体上来说就是巧妙的利用函数的特点来编程,比如高阶函数中,我们会让一个函数入参一个函数,又或者返回一个函数这样的谜之操作
// 脏函数
let bar = 1
function add(a) {
return a+bar
// 当外部变量bar改变或没声明时,该函数就无法实现自己的功能
}
// 纯函数
function add(a) {
return a+1
// 不依赖外部变量,特定参数有唯一一个返回值与之对应
}
封装一个只能调用一次的函数(利用闭包)
const add = (a, b) => a + b
function once(fn) {
let isOne = true
// 利用闭包将isOne存储起来
return function o(...args) {
// 返回一个调用一次就会修改isOne的【函数】
if(isOne){
isOne = false
return fn(...args)
// 回调传入的fn 将参数也传入
}
console.log('该函数已不是第一次调用了');
return
}
}
const newAdd = once(add) // function o(){...}
console.log(newAdd(1,2)); // 3
console.log(newAdd(1,2)); //该函数已不是第一次调用了 undefined