0.闭包
理解闭包的关键在于:外部函数调用之后其变量对象本应该被销毁,但闭包的存在使得我们仍旧可以访问外部函数的变量。
闭包的特性:
1. 函数内再嵌套函数
2. 内部函数可以引用外层的参数和变量
3. 参数和变量不会被垃圾回收机制回收
使用闭包主要是为了设计私有的方法和变量。
闭包的优点是可以避免全局变量的污染,
缺点是闭包会常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。
在js中,函数即闭包,只有函数才会产生作用域的概念。
闭包用处:
一个是可以读取函数内部的变量,
另一个就是让这些变量始终保持在内存中
闭包的另一个用处,是封装对象的私有属性和私有方法
由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用 闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露
解决方法是,在退出函数之前,将不使用的局部变量全部删除
function outer() {
var a = 1;
return function() {
return a;
};
}
var b = outer();
console.log(b()); //1
- 闭包即在多级函数嵌套环境下,内部函数可以调用外部函数成员的一种代码组织形式,但外部却无法访问内部成员。
- 认识闭包,首先认识作用域概念,内外函数都有自身存在的作用域范围,以便各自成员被其他函数调用或访问。
- 函数作用域中的常量,只对当前函数以及子函数有效,在作用域外部无法访问。
-
每次函数调用都会重新开辟一块内存空间,存储当前函数成员。
1.函数作用域
延长作用域的生命周期
之前提到过,每次函数调用读会开辟一块新的内存空间,产生一套新的运行环境,那如何才能保存上一次的运行结果呢?
当然可以,在主函数内部返回新函数,让新函数来保存主函数的运行结果。
//主函数
function sum() {
let i = 1
//新函数
//主函数内部返回新函数(匿名),让新函数来保存主函数的运行的结果
return function () {
//多次执行返回的新函数不会开辟新的内存空间 ,但主函数再次调用又产生一个新的函数,
//此时新的函数读取的i 是从老内存中读取的,而老内存上一步中已经将i变成了2
let j = 1
function show() {
++i;
++j;
console.log(`i:${i} j:${j}`)
}
show()
}
}
const sum1 = sum()
sum1() //i:2 j:2
sum1() //i:3 j:2
//主函数
function sum2() {
let x = 1
//新函数
//主函数内部返回新函数(匿名),让新函数来保存主函数的运行的结果
return function () {
let y = 1
return function () {
//新函数保存主函数的运行结果,每次调用主函数将再次产生一个新函数
//由于返回的新函数不被重新开辟内存空间,新函数读取到的x y 是上次操作过的值
++x;
++y;
console.log(`x:${x} y:${y}`)
}
}
}
const sum2x = sum2()() //两个return函数 ,两个括号才可以获取到最终的函数
sum2x() //x:2 y:2
sum2x() //x:3 y:3
2.块级作用域
- 块级作用域,类似函数作用域,let const是有块级作用域的,var没有块级作用域,var是全局作用域,挂载在window上
- 每个块级作用域内的成员都彼此独立的,因此多个块作用域可定义同名成员
-
var 虽然没有块级作用域,但有函数作用域,因此也就能达到‘伪块级作用域’的效果
在Java等其他语言中,当循环完毕,变量将被销毁,但是在js中
在for循环外部,使用var定义的循环变量仍然有效
在for循环外部,使用let定义的循环变量无效。因为let有块级作用域,超出了块级作用域
3.应用场景示例
闭包就是在多级函数嵌套下,内部函数可以调用外部函数成员的一种代码组织形式。
/**
* 获取指定范围的数字
*/
var array = [1, 2, 3, 4, 5, 6]
//知道需要 首先想到数组的过滤filter方法,可以根据条件过滤
const filterData = array.filter(function (item) { return item > 2 && item < 6 })
console.log("=="+filterData)
//现在条件是写死的,我要怎么才能让条件 2 6 变成动态的呢?
//这个时候就应该想到闭包,闭包:嵌套函数 内部函数可以访问外部函数的变量
function between(a, b) {
return function (item) {
return item > 2 && item < 6
}
}
console.log("闭包真是秒呀秒呀"+array.filter(between(2,6)))
/**
* 筛选出成绩在60-70之间的学生
*/
const students = [
{ name: 'a', score: 61, age: 10 },
{ name: 'b', score: 62, age: 19 },
{ name: 'c', score: 30, age: 20 },
{ name: 'd', score: 80, age: 22 },
{ name: 'e', score: 70, age: 22 },
{ name: 'f', score: 66, age: 11 },
]
students.filter(function (item) { return item.score > 60 && item.score < 70 });
const scoreRange = function scoreRange(a, b) {
return function (item) {
return item.score > a && item.score < b
}
}
console.log(JSON.stringify(students.filter(scoreRange(60, 70))))
//现在问题来了 我突然想按照年龄 能不能更灵活一些呢?
const proertyRange = function scoreRange(a, b,property) {
return function (item) {
return item[property] > a && item[property] < b
}
}
console.log("age:"+JSON.stringify(students.filter(proertyRange(18, 70,'age'))))
4.内存泄露
/**
*由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用 闭包,
*否则会造成网页的性能问题,在IE中可能导致内存泄露
*解决方法是,在退出函数之前,将不使用的局部变量全部删除
*
*/
const buttons = document.querySelectorAll('button')
buttons.forEach(function (item) {
const name = item.getAttribute('name')
item.addEventListener('click', function () {
console.log(item.getAttribute('name'))
console.log(name)
})
item = null // 解决内存泄露 ,在退出函数之前,将不使用的局部变量全部删除
})