闭包懂?
之前自认为熟悉了闭包,结果发现自己也是半桶水,一顿找资料,综合之前的各种积累,今天就来认真梳理一下闭包的知识啦~
首先得懂作用域
当某个函数被调用的时候,会创建一个执行环境及相应的作用域链。然后,使用arguments和其他命名参数的值来初始化函数的活动对象。在作用域链中,外部函数的活动对象位于第二位,再外部位于第三位,直至作用域链终点的全局执行环境。
作用域链就是为了保证对执行环境能访问到的数据的有序访问。比如要找变量a,从最前端找,直至全局执行环境。
那什么是闭包?
闭包就是一个有权访问另一个函数作用域的函数,常见方法就是在一个函数里创建另一个函数。
for example:
function fun1() {
var i = 4;
function fun2() {
console.log(i);
}
return fun2;
}
var temp = fun1();
temp();//生成闭包,结果为4上面就是一个简单的闭包的栗子啦~
深入一步?
关于setTimeout与闭包的坑
for (var i = 1; i <= 5; i++) {
setTimeout( function timer(){
console.log(i);
},i*1000);
}以上这段代码的结果会是以每秒一次的频率输出五次6。
为什么显示都是6呢?
闭包对函数类型的值进行传递时,保留对它被声明位置所处的作用的引用。
setTimeout函数里的闭包的执行,需要1秒,所以for循环在setTimeout函数将timer放入执行栈(这里说法可能不正规,意思就是timer函数执行之前)前,已经执行完5次循环了。而timer里的i保存的是外层for循环i的引用,所以只能得到最终for循环执行完的结果,i=5。
如果我们想要得到每隔一秒输出递增的数列呢?
且看下面代码:
for (var i=1; i<=5; i++) {
(function(i) {
setTimeout( function timer() {
console.log(i);
}, i*1000 );
})(j)
}再深入一步?
闭包与模块
使用闭包可以很好的将模块的公有属性和方法暴露出来:
var moudel1 = (function () {
var _count = 0;
var m1 = function () {
_count++;
};
var m2 = function () {
_count--;
}
var m3 = function () {
console.log(_count);
}
return {
m1 : m1,
m2 : m2,
m3 : m3
}
})();
moudel1.m1();
moudel1.m3();//1
moudel1.m2();
moudel1.m3();//0万物总不是十全十美的
闭包的用处很多,但是它的缺点就是可能会导致内存泄露;
js是有自己自动垃圾回收机制的,而闭包
。
因此当我们不需要使用变量对象的时候,要记得手动将其释放哦~
function fun1() {
var i = 4;
function fun2() {
console.log(i);
}
return fun2;
}
var temp = fun1();
temp();//生成闭包
temp = null;//释放对象彩蛋
js内存的堆与栈
这个问题我一直想了解,刚好看到一篇博文,里面有一篇文章,里面有一段:
而JavaScript 是高级语言,底层依旧依靠 C/C++ 来编译实现,其变量划分为基本数据类型和引用类型。
基本数据类型包括:undefined、null、boolean、number、string;
这些类型在内存中分别占有固定大小的空间,他们的值保存在栈空间,通过按值访问、拷贝和比较。
引用类型包括:object、array、function、error、date;这些类型的值大小不固定,栈内存中存放地址指向堆内存中的对象,是按引用访问的,说白了就跟 C 语言的指针一样的道理。
对于引用类型变量,栈内存中存放的知识该对象的访问地址,在堆内存中为该值分配空间,由于这种值的大小不固定,因此不能把他们保存到栈内存中;但内存地址大小是固定的,因此可以将堆内存地址保存到栈内存中。这样,当查询引用类型的变量时,就先从栈中读取堆内存地址,然后再根据该地址取出对应的值。
很显而易见的一点就是,JavaScript 中所有引用类型创建实例时,都是显式或隐式地 new 出对应类型的实例,实际上就是对应 C 语言的
malloc分配内存函数。
Last
欢迎大家关注公粽号:CSandCatti
日常推送英语精读,算法题,前端知识~