栈堆内存释放
我们就拿上一篇文章的最后那一题来说
例题1:
var n=10;
function fn() {
var n=20;
function f() {
n++;
console.log(n);
}
f() ;
return f;
}
var x= fn();
x();
x();
console.log(n);
堆销毁
上面图中有2个堆5个栈,而且这两个堆都不能销毁,因为一个被fn占用,一个被f和x占用
如果要让它们销毁,很简单,让fn=null,f=null,x=null。
栈销毁
思考:全局作用域什么时候销毁?
当页面在浏览器里面刷新(形成新的全局作用域)/关闭
一个函数执行的时候也形成了栈内存,它什么时候销毁??
而f和另外两个x的私有作用域(栈内存),一般情况下只要它们(函数)中代码一执行完就会销毁,因为它们的存在就是提供代码执行环境,代码执行完,就没它们什么事了,浏览器自动销毁它们
但是有一种情况,销毁不了
比如fn形成的栈内存销毁不了,因为aaafff222是在它里面开辟的堆,这个堆没有销毁,所以这个栈也不能销毁!!
详细解说:
例题2:
分析:
var f=fn(2) ;//=>先把FN执行(传递实参2),把FN执行的返回结果(RETURN后面
的值)赋值给F
f() ;//=>把返回的结果执行
fn(2) () ;//=>和上面两步骤类似,都是先把FN执行,把FN执行的返回结果再次执行
临时不销毁
从上面我们看到了一个新的情况,那就是,临时不销毁,也就是当我们执行到
在这里虽然fn执行的返回结果没有赋给f,没有被外部变量占用,但是它的结果(小函数)还是要执行,所以要开辟堆,暂时占用fn私有作用域,等小函数执行完了就可以一块销毁(堆和栈)。
return ## 对return右边的东西是不做提升的,相当于值,我们不对值提升,只对变量提升
一定记住一个东西就是,你跑上级作用域找了变量,修改了之后,上级作用域那个变量也要修改!
假如一个a函数执行返回一个b函数,那如果这个函数执行100次,也就会返回100次b函数,而这100个b函数都不一样,每次都返回一个全新的b函数(地址不同)
两种闭包函数
[闭包]
=>函数执形成-一个私有的作用域,保护里面的私有变量不受外界的干扰,
这种保护机制称之,为“闭包”
=>市面上的开发者认为的闭包是:形成-一个不销毁的私有作用域(私有栈
内存)才是闭包
柯理化函数
把返回的函数赋给f
惰性函数
同理,把返回的函数赋给utils
他们的原理都是形成一个不销毁的私有作用域(私有栈内存)
闭包作用和项目实战应用(面试必问)
其实项目中要尽量少使用闭包
//==>真实项目中为了保证Js的性能(堆栈内存的性能优化),应该尽可能的减
少闭包的使用(不销毁的堆栈内存是耗性能的)
//1.闭包具有保护作用:保护私有变量不受外界的干扰
//2.闭包具有保存作用:形成不销毁的栈内存,把一些值保存下来,方便后面的
调取使用
应用无非分为下面两大类
闭包作用之保护
在真实项目中,尤其是团队协作开发的时候,应当尽可能的减少全局变量的 使用,以防止相互之前的冲突(“全局变量污染”),那么此时我们完全可以把自己这一部分内容封装到一个闭包中, 让全局变量转换为私有变量
假如你已经写完了才发现,也没关系,你可以自定义一个函数,把所有代码包进去,闭包,就像下面这样。
不仅如此,我们封装类库插件的时候, 也会把自己的程序都存放到闭包中 保护起来,防止和用户的程序冲突,但是我们又需要暴露一些方法给客户使 用,这样我们如何处理呢?
1.JQ这种方式:把需要暴露的方法拋到全局,通过window.XX
- Zepto这种方式: 基于RETURN把需要共外面使用的方法暴露出去,通过return:{XX:}
闭包作用之保存
传统的es5规范当中,只有全局作用域和函数执行形成的私有作用域,判断和循环不会产生作用域
同步编程,事情只能一件一件做
异步编程,这件事没有彻底完成,不再等待,继续执行下面的任务
所有的事件绑定都是异步编程 (同步编程: 一件事 一件事的做,当前这件事没完成,下一个任务不能处理/异步编程:当前这件事件没有彻底完成,不在等待,继续执行下面的任务),绑定事件后,不需要等待执行,继续执行下一个循环任务
举个例子:(tablist长度默认是3)(下图末尾少了个扩折号)
当我们发生点击事件执行方法的时候,循环早已结束(让全局的i等于循环最后的结果3)
那怎么办呢,这样点击事件就无法正确完成。
解决方法1:自定义属性
在这里我们新加了一个myIndex属性(自定义)
该方法涉及this,后几篇补充
解决方法2:闭包
把函数用另一个函数包起来,这样里面的变量的上级作用域就指的的包它的函数,不是window。(仅限本例)
总结:形成了**不销毁(保存)**的3个私有作用域
循环三次,形成三个不销毁的私有作用域(自执行函数执行),而每一个不销毁的栈内存中都存储了一个私有变量I,
而这个值分别是每一次执行传递进来的全局I的值(也就是:第一个不销毁的作用域存储的是0,第二个是1,第三个是2);
当点击的时候,执行返回的小函数,遇到变量I,向它自己的上级作用域查找,找到的I值分别是: 0/1/2, 达到了我们想要的效果
但是这么写非常消耗性能,开发中一般不这样做
另一种写法:
解析在图里!
解决方法3:基于ES6解决(用let)
基于Es6中的LET来创建变量,是存在块级作用域的(类似于私有作用域)
作用域: (栈内存)
1.全局作用域
2.私有作用域(函数执行)
3.块级作用域(一般用大括号包起来的都是快级作用域,前提是ES6语法规范)
4.注意var obj={};//对象的大括号不是块级作用域
ES6中的块级作用域
- 循环体
- 判断体
- 普通括号型
举个例子(普通括号型)
{
let a=12;
console.log(a) ;//=>12
}
console.log(a) ;//=>Uncaught ReferenceError: a is not defined
为什么报错呢?因为a是上面的块级作用域私有的,我们用不了它
再来个例子(循环体)
形成了五个块级作用域(每次循环形成一个),每个块级作用域中都有一个私有变量i,这5个i互不干扰!变量值就是每一次循环I的值
看看下面这个例子,ES6也翻车了
为啥报错呢,ES6虽然没有变量提升,but代码执行前有词法解析,在执行前就知道这个块里有哪些变量,它知道有a,但是你就是从上到下执行,规则如此还是会报错滴~~
补充一个小知识点:
itar:回车速成下面的结构666
结束啦,明天继续