学习目标:
梳理闭包
学习资料
《前端开发核心知识进阶》
基本知识
作用域
某种规则下的限定范围,该规则用于指导开发者如何查找变量。
函数作用域:函数内访问变量时,先查找函数作用域有没有声明,如果没有查到,会查找上级作用域,可能是函数作用域也可能是全局作用域。
全局作用域:全局声明的变量、window对象/global对象。
变量作用域的查找是一个扩散的过程,就像各个环节相扣的链条,逐次递进,这就是作用域链。
块级作用域,是指作用域范围限制在代码块中。(let const)
暂时性死区:函数开头到变量声明所在行,这个范围无法访问let const 声明的变量,访问会报错。
函数参数默认值也受暂时性死区影响。
null和undefined区别:null表示为空,null本身也是对象,undefined表示不存在,undefined时可以用默认值,null不可以。
引用思否的一个回答:
函数参数名出现在执行上下文/作用域会报错。
执行上下文、调用栈
执行上下文 就是当前代码的执行环境/作用域。
代码执行的两个阶段 :代码编译阶段、代码执行阶段。
预编译阶段:编译器将javascript代码编译成可执行代码(变量声明、提升、非表达式函数声明提升)。
执行阶段:执行代码逻辑,执行上下文会在这个阶段全部创建完成。
// var bar
// function bar (){arg1}
// bar = function(){arg2}
// bar()
// var bar
// function bar (){arg1}
// bar = function(){arg2}
// bar()
函数式声明会被提升。
作用域在预编译阶段确定,作用域链是在执行上下文的创建阶段完全生成,函数调用时才会开始创建对应的执行上下文。执行上下文包括作用域链、变量对象、this指向。
预编译阶段创建变量对象,只是创建,未进行赋值; 代码执行阶段变量对象转为激活对象(作用域链由当前执行环境的变量对象和所有外层已经完成的激活对象组成)。
调用栈:一个函数调用另一个函数,另一个函数又调用了其他函数,形成了一系列调用栈(后进先出)。
函数执行完毕出栈时,函数内的局部变量在下一个垃圾回收节点会被回收,该函数对应的执行上下文会被销毁,所以外界无法访问函数内定义的变量。
闭包
函数嵌套函数,内层函数引用了外层函数的变量,并且内层函数在全局环境下可以访问,进而形成闭包。
closure 闭包变量。
闭包的基本原理 通过函数返回的函数获取函数的变量值。
内存管理
内存管理是指对内存生命周期的管理,内存的生命周期:分配内存、读写内存、释放内存。
内存空间:
栈空间:由操作系统自动分配,存放函数的参数值、局部变量的值,操作方式类似于数据结构中的栈;
堆空间:一般由开发者释放,这部分空间要考虑垃圾回收。
基本数据类型:undefined、null、number、string、boolean,按照值大小保存在栈空间中,占有固定大小的内存空间。
引用类型:object、array、function...,保存在堆空间中,内存空间大小不固定,按引用访问。
var a = 10
var b = 11
var c = [1,2,3]
var d = {e:20}
内存泄漏:内存空间不再使用,却由于某种原因没有被释放。
内存泄露的危害:导致程序运行缓慢,甚至崩溃。
场景: 对dom元素的引用不用之后未置空、addEventListener 没有remove、setTimeout setInterval未清理。
垃圾回收:标记清除、引用计数。
内存泄漏风险排查:chrome Memory 标签,查看js Heap中 size较大的几项。
实战例题
例题1
返回10 是因为 v++ 是后置+,先返回值,再进行运算,如果改为++v,返回11。
例题2
innerFoo 被赋值给fn,foo函数空间不会被垃圾回收,所以可以拿到a变量。