JS-闭包

0.闭包

理解闭包的关键在于:外部函数调用之后其变量对象本应该被销毁,但闭包的存在使得我们仍旧可以访问外部函数的变量。
闭包的特性:
1. 函数内再嵌套函数
2. 内部函数可以引用外层的参数和变量
3. 参数和变量不会被垃圾回收机制回收


使用闭包主要是为了设计私有的方法和变量。
闭包的优点是可以避免全局变量的污染,
缺点是闭包会常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。


在js中,函数即闭包,只有函数才会产生作用域的概念。


闭包用处:
一个是可以读取函数内部的变量,
另一个就是让这些变量始终保持在内存中
闭包的另一个用处,是封装对象的私有属性和私有方法


由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用 闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露

解决方法是,在退出函数之前,将不使用的局部变量全部删除

function outer() {
    var a = 1;
    return function() {
        return a;
    };
}
var b = outer();
console.log(b()); //1
  • 闭包即在多级函数嵌套环境下,内部函数可以调用外部函数成员的一种代码组织形式,但外部却无法访问内部成员。
  • 认识闭包,首先认识作用域概念,内外函数都有自身存在的作用域范围,以便各自成员被其他函数调用或访问。
  • 函数作用域中的常量,只对当前函数以及子函数有效,在作用域外部无法访问。
  • 每次函数调用都会重新开辟一块内存空间,存储当前函数成员。


    image.png

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定义的循环变量仍然有效


    image.png

    在for循环外部,使用let定义的循环变量无效。因为let有块级作用域,超出了块级作用域


    image.png
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 // 解决内存泄露 ,在退出函数之前,将不使用的局部变量全部删除
})
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,922评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,591评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,546评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,467评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,553评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,580评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,588评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,334评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,780评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,092评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,270评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,925评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,573评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,194评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,437评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,154评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,127评论 2 352