JS的回收机制和闭包
本篇内容:
作用域及声明
JS作用域:全局、块、函数作用域。
在函数外,使用 var
声明的变量为global variable
在函数内,使用 var
声明的变量为local variable
而 let
和 const
声明的变量为块作用域变量。
隐式声明的变量:是全局对象的属性。
js赋值操作的实质
1. 作用域链
JavaScript代码在执行时,会形成一个作用域链,该链包含当前执行代码的所有作用域。
每个作用域都是一个对象,包含该作用域内所有变量和函数的声明。
2. 赋值操作
当执行赋值操作时,JavaScript会在作用域链逐级查找要赋值的变量。
3. 全局对象
全局对象是JavaScript中的一个特殊对象,它是所有作用域链的顶层对象。在浏览器环境中,全局对象是window对象。
4. 创建全局属性
如果在所有作用域链中都找不到要赋值的变量,则JavaScript会在全局对象中创建一个新的属性并赋值。
所以注意:
访问一个没有声明的变量时,JS会报错;
给一个没有声明的变量赋值时,JS不会报错,会认为我们是要隐式声明一个全局变量。
5.显式声明与隐式声明
变量声明自带不可删除属性1
2
3var name = 'muzidigbig'
name = 'muzidigbig'
前者是变量声明,因此无法被删除;后者为全局变量的一个属性,因此可以从全局变量中删除。
当使用var声明一个变量时,创建的这个属性是不可配置的,也就是说无法通过delete运算符删除。
函数体内变量回收机制
在函数执行后是否会被回收取决于变量是否被引用 。
如果变量在函数执行后 仍然被引用 ,那么 不会被回收 。
情况一:返回变量给函数外部 (最好理解)
1
2
3
4
5
6
7
8
9
10
11function foo() {
var x = 1;
return x;
}
var y = foo();
//console.log(x)
//>>Uncaught ReferenceError: x is not defined
//无法在外部使用函数内局部变量
console.log(y); // 1
//可以引用返回的变量情况二:内部函数引用外部函数的局部变量(封装变量),内部函数在执行时,可以访问该变量。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24function foo() {
var x = 1;
function bar() {
console.log(x); // 1
}
return bar;
}
var baz = foo();
baz(); // 1
//返回的内部函数bar,其引用了x变量,因此其未被销毁。
//在执行时,可以访问
//下面这是一个延迟执行的函数,内部嵌套的子函数引用了局部变量a,在延迟执行时,依旧可以访问该变量。
function foo() {
let a=1
setTimeout(function() {
alert(a);
}, 1000);
}
foo();//打印输出1情况三:事件处理程序函数中的引用
1
2
3
4
5
6
7
8
9
10
11
12
13var button = document.getElementById("button");
// 在母元素中移除按钮元素,但此时仍有变量引用该button
button.parentNode.removeChild(button);
alert(button.innerHTML+"1")//打印出文字节点+1
// 点击页面空白区域
document.body.addEventListener("click", function() {
alert(button.innerHTML+"2"); //在10s内点击body,打印出文字节点+2
});
setInterval(function(){
button=null
},10000)
//10s后,引用的button变量的值被回收,此时点击会报错了。
//因此仅在事件处理函数中引用,也会被回收
JS的undefined和null
参考资料: