理解闭包

简介: 闭包:能够访问另一个函数作用域的变量的函数 function outer() { var a = 2; var inner = function() { console.log(a) } return inner // inner就是一个闭包,因为它能够访问outer函数的作用域的变量 } 很多人会搞不懂匿名函数与闭包的关系,实际上,闭包是站在作用域的角度上来定义的,因为inner访问到outer作用域的变量,所以inner就是一个闭包函数。

闭包:能够访问另一个函数作用域的变量的函数;是函数和声明该函数的词法环境的组合
闭包的形成与函数作用域链有关

function outer() {
  var a = 2;
  var inner = function() {
    console.log(a)
  }
  return inner  // inner就是一个闭包,因为它能够访问outer函数的作用域的变量
}

很多人会搞不懂匿名函数与闭包的关系,实际上,闭包是站在作用域的角度上来定义的,因为inner访问到outer作用域的变量,所以inner就是一个闭包函数。
闭包所需要注意的地方:this指向,变量的作用域,会造成内存泄露

思考:为什么闭包函数能够访问其他函数的作用域?

从堆栈的角度看待js函数
基本变量的值一般都是存在栈内存中,对象类型的变量的值存储在堆内存中,栈内存存储对应空间地址。基本类型的数据:Number、Boolean、Undefined、String、Null

var a = 1;
var b = {m: 20}

对应内存存储
_2019_08_06_10_45_00
当我们执行 b={m: 30} 时,堆内存就用新对象{m:30},栈内存的b指向新的空间地址,而堆内存中原来的{m: 20}就会把程序引擎垃圾回收掉

var a = 3;
function fn() {
  var b = 4;
  function fn1() {
    console.log(b)
  }
  fn1()
}
fn()

栈:一种先进后出的数据结构

  • 在执行fn前,此时我们在全局执行环境,全局作用域里有变量a,对象fn
  • 进入fn,此时栈内存就会push一个fn的执行环境,这个环境里有变量b和对象fn1,在这里可以访问到自身执行环境和全局执行环境
  • 进入fn1,会push一个fn1的执行环境到执行栈,这里没有定义其他变量,但是这里也同样可以访问到fn的执行环境和全局执行环境,因为当程序在访问变量时,会向底层一个个栈查找,如果在全局执行环境里找不到,则会报undefined错误
  • 随着fn1()执行完毕,fn1的执行环境被销毁,接着执行完fn(),fn的执行环境也会被销毁,只剩下全局执行环境。现在没有变量b和fn1函数对象了,只有a和fn

当程序调用某个函数时,做了以下工作:准备执行环境,初始化函数作用域链和arguments参数对象

现在我们会看最初的例子outer与inner

function outer() {
  var a = '1'
  var inner = function() {
    console.log(a)
  }
  return inner;
}
var inner = outer()
inner()

当程序执行完var inner = outer(),其实outer的执行环境并没有被销毁,因为他里面的变量a仍然被被inner的函数作用域链所引用,当程序执行完inner(), 这时候,inner和outer的执行环境才会被销毁调;


坑点1:应用的变量可能发生变化

function outer() {
  var result = [];
  for(var i = 0; i < 10; i++) {
    result.[i] = function() {
      console.log(i)
    }
  } 
  return result  // 只有打印的结果全部是10
}
改成如下
function outer() {
  var result = []
  for(var i = 0; i < 10; i++) {
    result.[i] = function(num) {
      return function() {
        console.log(num)
      }
    }(i)
  }
}

坑点2:this指向问题

var object = {
  name: 'object',
  getName: function() {
    return function() {
      console.log(this.name)
    }
  }
}
object.getName()() // undefined
// 因为里面的闭包函数是在window作用域下执行的,也就是说,this指向windows

坑点3:内存泄露问题

function showId() {
  var el = document.getElementById('id')
  el.onclick = function() {
    console.log(el.id) // 这样会导致闭包引用外层的el,当执行完showId后,el无法释放
  }
}
改成下面
fucntion showId() {
  var el = document.getElementById('id')
  var id = el.id
  el.onclick = function() {
    console.log(id)
  }
  el = null // 主动释放
}

用闭包模仿块级作用域

(function() {
  for(var i = 0; i< 10; i++) {
    console.log(i)
  }
})()
console.log(i) // undefined 因为i随着闭包函数的退出,执行环境销毁,变量回收

有意思的示例

function makeAdd(x) {
  return function(y) {
    return x + y
  }
}
var add5 = makeAdd(5)
var add10 = makeAdd(10)
console.log(add5(2)) // 7
console.log(add10(2)) // 12

add5 和 add10 都是闭包。它们共享相同的函数定义,但是保存了不同的词法环境。在 add5 的环境中,x 为 5。而在 add10 中,x 则为 10
var Counter = (function() {
    var privateCounter = 0;
    function changeBy(val) {
        privateCounter += val
    }
    return {
        increment: function() {
            changeBy(1)
        },
         decrement: function() {
            changeBy(-1);
        },
        value: function() {
            return privateCounter;
        }
    }
})()
Counter.increment()
Counter.increment()
console.log(Counter.value())

常见闭包形式

function v() {
    setTimeout(function() {
        // 闭包函数
    }, 0)
    document.getElementById('btn').addEventListener('click', function() {
        // 闭包函数
    })
    $.ajax({
        url: '',
        type: 'post',
        success: function() {
            // 闭包函数
        }
    })
    return {
        n: function() {
            // 闭包函数
        }
        n: function() {
            // 闭包函数
        }
    }
}
v()
相关文章
|
1天前
|
云安全 人工智能 安全
AI被攻击怎么办?
阿里云提供 AI 全栈安全能力,其中对网络攻击的主动识别、智能阻断与快速响应构成其核心防线,依托原生安全防护为客户筑牢免疫屏障。
|
11天前
|
域名解析 人工智能
【实操攻略】手把手教学,免费领取.CN域名
即日起至2025年12月31日,购买万小智AI建站或云·企业官网,每单可免费领1个.CN域名首年!跟我了解领取攻略吧~
|
5天前
|
安全 Java Android开发
深度解析 Android 崩溃捕获原理及从崩溃到归因的闭环实践
崩溃堆栈全是 a.b.c?Native 错误查不到行号?本文详解 Android 崩溃采集全链路原理,教你如何把“天书”变“说明书”。RUM SDK 已支持一键接入。
439 191
|
3天前
|
数据采集 消息中间件 人工智能
跨系统数据搬运的全方位解析,包括定义、痛点、技术、方法及智能体解决方案
跨系统数据搬运打通企业数据孤岛,实现CRM、ERP等系统高效互通。伴随数字化转型,全球市场规模超150亿美元,中国年增速达30%。本文详解其定义、痛点、技术原理、主流方法及智能体新范式,结合实在Agent等案例,揭示从数据割裂到智能流通的实践路径,助力企业降本增效,释放数据价值。
|
9天前
|
人工智能 自然语言处理 安全
国内主流Agent工具功能全维度对比:从技术内核到场景落地,一篇读懂所有选择
2024年全球AI Agent市场规模达52.9亿美元,预计2030年将增长至471亿美元,亚太地区增速领先。国内Agent工具呈现“百花齐放”格局,涵盖政务、金融、电商等多场景。本文深入解析实在智能实在Agent等主流产品,在技术架构、任务规划、多模态交互、工具集成等方面进行全维度对比,结合市场反馈与行业趋势,为企业及个人用户提供科学选型指南,助力高效落地AI智能体应用。
|
5天前
|
消息中间件 安全 NoSQL
阿里云通过中国信通院首批安全可信中间件评估
近日,由中国信通院主办的 2025(第五届)数字化转型发展大会在京举行。会上,“阿里云应用服务器软件 AliEE”、“消息队列软件 RocketMQ”、“云数据库 Tair”三款产品成功通过中国信通院“安全可信中间件”系列评估,成为首批获此认证的中间件产品。此次评估覆盖安全可信要求、功能完备性、安全防护能力、性能表现、可靠性与可维护性等核心指标,标志着阿里云中间件产品在多架构适配与安全能力上达到行业领先水平。
315 196