程序员步步为营之JavaScript

JS-闭包

2020-06-15  本文已影响0人  刘淘

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.块级作用域

image.png

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 // 解决内存泄露 ,在退出函数之前,将不使用的局部变量全部删除
})
上一篇 下一篇

猜你喜欢

热点阅读