JavaScript闭包

闭包

  • 使用chrome调试查看
  • 闭包是有权访问另一个函数作用域的变量的函数.
    简而言之,这些函数表达式定义在另一个函数的函数体内,它可以访问它们所在的外部函数中声明的所有局部变量、参数和声明的其他内部函数。当其中一个这样的内部函数在包含它们的外部函数之外被调用时,就会形成闭包,例如:
function fn1() {
    var num = 0
    function fn2() { //fn1函数内嵌套着内部函数fn2
        num++
        console.log(num)
    }
    return fn2 //将fn2返回
}

var result = fn1() //相当于result = function fn2() {num++;console.log(num);}
result() //1

在上面的例子中fn2就是闭包。

产生闭包的条件

  • 函数嵌套
  • 内部函数引用了外部函数的数据(变量/函数)
  • 执行外函数

闭包的生命周期

  • 产生: 在嵌套内部函数定义执行完时就产生了(不是调用)
  • 死亡: 在嵌套的内部函数成为垃圾对象时

闭包的用途

  • 可以读取函数内部的变量。

    • 根据作用域链定义可知子对象会一级一级地向上寻找所有父对象的变量,所以父对象的所有变量,对子对象都是可见的。上面的例子中,fn2函数就可以访问fn1函数内部的局部变量num
    • 平时我们在函数中的返回值经常是一个值,既然fn2函数能够获取fn1内部的变量,我们将fn2函数返回就能在外部获取num变量了
    • 将fn2返回后,相当于var result = function fn2() {num++;console.log(num);},即是外部的函数result也能获取局部变量num了
  • 让变量的值始终保存在内存中。

function fn1() {
    var num = 0
    function fn2() { 
        num++
        console.log('闭包函数' + num)
    }
    return fn2 
}

var result = fn1() 
result() //闭包函数1
result() //闭包函数2

function commonFn() {
    var num = 0
    num++
    console.log('普通函数' + num)
}
commonFn() //普通函数1
commonFn() //普通函数1

从上面的例子中可知,重复调用闭包函数时,每次输出的num值都会+1,而普通函数重复执行后num值仍然保持不变,这证明了函数fn1中的局部变量num一直保存在内存中,并没有在f1调用后被自动清除。
原因:fn1是fn2的父函数,fn2被赋值给全局变量result,这导致fn2始终在内存中,而fn2依赖于fn1,故fn1的变量值一直存在于内存中。

闭包的缺点

由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大而会导致内存泄漏,所以在调用结束之后要将不使用的局部变量删除掉。

内存溢出
  • 一种程序运行出现的错误
  • 当程序运行需要的内存超过了剩余的内存时,就抛出内存溢出的错误
内存泄漏
  • 占用的内存没有及时释放
  • 内存泄漏积累多了就容易导致内存溢出
  • 常见的内存泄漏
    • 意外的全局变量
    • 没有及时清理的计时器或回调函数
    • 闭包

闭包的this指向问题

let obj = {
    name: 'Joe',
    getName: function() {
        return function() {
            console.log(this.name)
        }
    }
}

obj.getName()() //<empty string>

返回的函数最终是在全局作用域中,故无法访问到obj对象中的name属性。

闭包的例子

  • 创建函数工厂
function addFunc(x) {
    return function(y) {
        return x + y
    }
}

var add5 = addFunc(5)
var add10 = addFunc(10)

console.log(add5(2)) //7
console.log(add10(2)) //12

以上例子是创建两个数值相加的函数工厂,var add5 = addFunc(5)这行代码先给x传值5,且这行代码相当于var add5 = function(y) {return 5 + y}在打印行再给y传值2.

  • 返回一个对象的写法
let counter = function() {
    let currentVal = 0
    function addVal(value) {
        currentVal += value
    }

    return {
        increment: function() {
            addVal(1)
        },
        decrement: function() {
            addVal(-1)
        },
        value: function() {
            return currentVal
        }
    }
}

let counter1 = counter()
let counter2 = counter()

console.log(counter1.value()) //0
counter1.increment()
counter1.increment()
console.log(counter1.value()) //2
counter1.decrement()
console.log(counter1.value()) //1

console.log(counter2.value()) //0

由这个例子可知counter1和counter2是独立的,互相不影响的,也就是它们的值是私有的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 前言 总括 :这篇文章使用有效的javascript代码向程序员们解释了闭包,大牛和功能型程序员请自行忽略。 译者...
    KX九五阅读 2,241评论 0 1
  • 本文摘录及参考自:1. 学习Javascript闭包(Closure)2. 闭包的秘密3. JavaScript ...
    chenhong_f1e2阅读 3,262评论 0 2
  • 老生常谈的问题,什么是闭包? 我觉得阮一峰老师说的特别好:闭包简单来说,就是在函数内部访问函数局部变量的内部函数。...
    巨龙在盯着你呐阅读 845评论 0 0
  • 本篇文章包懂 什么是闭包 总有人看到闭包就头疼,因为“闭包”二字实在让人搞不懂它的语义。但闭包其实是个很简单的概念...
    鹤仔z阅读 1,742评论 1 2
  • f2是一个闭包,fn1拥有一个局部变量i,fn1调用结束后,其变量i因为被fn2引用,所以并没有被垃圾回收。 从作...
    YucinChow阅读 1,176评论 0 0