详解变量提升和闭包的应用

堆栈内存

栈内存:作用域

  • 提供一个供js代码自上而下执行的环境(代码都是在栈内存中执行)
  • 存储基本数据类型
  • 释放:当栈内存被销毁,存储的那些基本值也跟着被销毁了(一般函数执行完成所形成的私有作用域会自动释放,除非某些内容被栈内存以外的变量占用了此时栈内存不能释放)全局栈内存只有在页面关闭的时候被释放

堆内存:引用值对应的空间

  • 存储引用类型值(对象:键值对 函数:代码字符串)
  • 当前堆内存释放销毁,那么这个引用值彻底没了
  • 释放:当对内存没有被任何的变量或其他东西所占用,浏览器会在空闲的时候自主进行内存回收,把所有不被占用堆的内存销毁掉。比如我让ary1=null,null是空对象指针,让原始变量谁都不指,原有被占用的对内存就没有被东西占用了,浏览器在空闲时候就把该堆内存销毁了。

举例:
var a=10经历三个过程,声明变量a,没有赋值默认是undefined;在当前作用域开辟一个位置存储12(栈);让变量a和12关联在一起(定义:赋值)
var a=[1,2],声明变量a;开辟一个新的内存空间存储[1,2](堆);让变量a和该对内存地址关联起来
如果是函数的话,会开辟一个新的内存空间存储代码字符串,执行的时候开辟一个私有作用域(栈)把之前创建函数时存储的字符串执行,执行完毕,该栈内存销毁。

变量提升

js是先预编译再执行,预编译的过程就是扫一遍把变量绑定作用域。所以才有变量提升。
带var的只声明未定义,带function的把声明定义都完成了

console.log(a)
console.log(sum)
var a = 1
function sum () {
console.log('hello world')
}

执行结果是
在这里插入图片描述
【注意】
私有作用域,先形参赋值再变量提升再代码执行

带var和不带var的区别

在全局作用域下声明一个变量,也相当于给window对象设置了一个属性,属性的值就是属性值(私有作用域声明的私有变量和window没关系)

console.log(a)//undefined 变量提升不报错
conosle.log(window.a)//undefined 给window设置一个对象
console.log('a' in window)//true window存在a属性
var a = 12
console.log(a) //12
conosle.log(window.a)//12

如果不带var,看以下代码

a = 12
conosle.log(a)//12
console.log(window.a)//12
console.log(a)//报错 不执行
a = 12
conosle.log(a)
console.log(window.a)
console.log(window.a)//undefined
console.log('a' in window)//false
a = 12//=>window.a = 12
conosle.log(a)//12
console.log(window.a)//12

【总结】
在全局作用域

  • 加var是全局变量,由于window对象和全局有映射关系,也就设置了window的属性
  • 不加var的本质是window下的属性,自然没有变量提升

【拓展】
在私有作用域中

  • 带var的在私有作用域有变量提升,都声明为死于变量,和外界没有任何关系
  • 不带var不是私有变量,它会向它的上级作用域查找,看是否为上级变量,不是则继续向上查找,一直查找到window位置,这种查找机制叫做私有作用域链

作用域链的拓展

function fn () {
//变量提升:无
b = 13
console.log('b' in window)//true
console.log(b)
}
fn();//13
console.log(b)//13

在作用域链查找过程中,如果找到win也没有这个变量,相当于给win设置了一个属性b(window.b=13)

闭包

什么是闭包

  • ·存在没被释放的内存
  • 当前作用域使用其他作用域的变量
    比如
function b () {
var a = 1
return function c () {
console.log(a)
}
}
var e = b()
e()//输出1

这就是个闭包,b执行完了栈内存也没有被释放,里面的a被b栈内存以外的变量引用。再看e,执行的时候器作用域是全局作用域,但使用到了b的私有作用域下的变量a

闭包的应用

  1. 为了保证js 的性能(堆栈内存的性能优化),应该尽可能减少闭包的使用(不被销毁的堆栈内存是耗性能的)
  2. 闭包具有保护作用,保护私有变量不受外界干扰

在项目中,尤其是团队写作开发的时候,应尽可能减少全局变量的使用,以防止相互之间的冲突,那么此时哦我们可以把自己的一部分内容封装到一个闭包里,让全局变量变为私有变量。基于return把需要供外面使用的方法暴露出去。

var Zepto = (function () {
//...
	return {
		xxx:function () {
		}
	} 
})

或者,当想改变一个对象的值,比如user,之前用user.name,但这个方法不够好,当程序过于复杂,会一不小心改动一些东西,变化的轨迹就非常难以追踪。虽然有效但非常危险。所以想把修改过程管控起来,就用到了闭包。
可以定义一个function user(name)。在里面var一个age,sex。这时候返回的不是一个确切的数值,是一个对象,对象里包含不同的方法,比如getName,setName。方法就是定义在对象里面的函数。
当做的项目内部数据流足够复杂,那我们此处必须得找个办法来归纳,不能让对象里面的值在里面被随便修改,必须得找一个像一个接口一样的东西,必须得按我的方法才能修改和设置某个值,这样严谨很多。闭包可以通过走子函数来获取母函数的函数作用域,然后去存取里面的值。这就是闭包的好处和特点。
在这里插入图片描述
3. 闭包具有保存作用,形成不销毁的栈内存,把一些值保存下来,方便后面的调取使用
可以基于闭包解决用var for循环给多个按钮绑定事件,无论点击第几个i都是最后那个。(这个问题可以直接直接用let解决,此处说用闭包解决的办法)
假如有五个按钮

for (var i=0; i<buttonList.length;i++) {
	buttonList[i].onclick = function () {
		console.log(i)
	}
}//此时无论点击第几个按钮打印的都是4,因为js在es6之前没有块级作用域。当我们点击的时候,外层循环已经结束(可以点击的时候页面已经加载完成,页面加载完成预示着js代码都已经执行完成,也就是循环也都执行完成,外层循环结束已经让全局下的i为4)

用闭包解决如下:

for (var i=0; i<buttonList.length;i++) {
	buttonList[i].onclick = (function () {
		//让自执行函数执行,把执行的返回值(return)赋值给onclick,此时onclick绑定的是返回的小函数,点击的时候执行的是小函数,自执行函数在给事件赋值的时候已经执行了
		return function () {
			console.log(i)//上级作用域,自执行函数形成的作用域
		}
	})()
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值