【原创】本文阐述某些情况下内存泄漏的根本原因,深入分析函数变量取值底层原理;
示例****start
//【JS预解析】 经典题
function fn(i) {
var i;
alert(i);
};
fn(10); // 10
解释:函数fn内的行参,相当于var i = i;紧随其后又声明了变量i,但未赋值;根据var声明提升原理,在重复声明后才会完成复制操作(此代码不在函数内部依然表现如此);
示例****1
function fn(j){
j = 2323
};
var h = 23;
fn(h);//h依旧是23
解释:函数内的行参,相当于var j = j;所以函数内部重赋值h不受影响
示例****2
var data = [];
for (var k = 0; k < 3; k++) {
data[k] = function () {
alert(k);
};
}
//输出结果
data[0](); // 3
data[1](); // 3
data[2](); // 3
解释:函数内的内的变量是在函数创建的时候保存了函数的作用域链,而不是变量。所以在调用的时候,就会顺着作用域链找到k的值,此时的k已经为3了!
示例****3
var data = [];
for (var k = 0; k < 3; k++) {
data[k] = (function _helper(k) {
return function () {
alert(k);
};
})(k); //传入"k"值
}
//现在结果是正确的了
data[0](); // 0
data[1](); // 1
data[2](); // 2
解释:data[k]为自执行函数_helper执行之后的函数,自执行函数_helper接收参数k,相当于var k = k; 执行了三次,就相当于每次var k = k;创建了三个局部变量k,那么这个k会不会被释放呢?
答案是不会!因为自执行函数_helper的返回函数 使用到了局部变量k,会以[[scope]]的方式引用,只要data[k]不会被释放(注意:data[k]中的k下标和自执行函数_helper执行之后的函数,使用到的k局部变量不是同一个),那么_helper执行之后的函数,通过[[scope]]的方式引用的局部变量k将永远存在于内存之中;
函数内的内的变量是在函数创建的时候保存了函数的作用域链,而不是变量。所以在调用的时候,就会顺着作用域链找到k的值,此时的k找到的是被data引用所以无法释放的三块内存中,分别为0/1/2 这就是柯里函数的意义所在
示例****4
var x = 10;
function foo() {
alert(x);
}
(function () {
var x = 20;
foo(); // 10, but not 20
})();
解释:我们再次看到,在标识符解析过程中,使用函数创建时定义的词法作用域--变量解析为10,而不是20。此外,这个例子也清晰的表明,一个函数(这个例子中为从函数“foo”返回的匿名函数)的[[scope]]持续存在,即使是在函数创建的作用域已经完成之后。
换句通俗易懂的说法:函数内的变量在函数激活(调用)的时候,会顺着 作用域链 向父级递归查找,这个 作用域链 在函数创建的时候,是以函数的一个属性[[scope]]保存的;
闭包内变量会保存在内存中,且不会被释放,就是因为被未释放的函数引用。
汤姆大叔的博客原文地址:https://www.cnblogs.com/TomXu/archive/2012/01/18/2312463.html
网友评论