一、作用域
作用域规定了变量能够被访问的范围,而离开变量作用域的变量则不能被访问(有时也叫变量的生命周期)。作用域又分为局部作用域和全局作用域。
1.局部作用域
在函数或代码块内部声明的变量只能在其内部被访问,在外部无法被访问,这里有几个注意点:
- 函数的形式参数也是函数内部的局部变量且不同函数内部声明的同名变量之间也不能互相访问;
- 函数执行完毕后,函数内部的变量会被释放;
- 变量在不进行声明时为全局变量,可以在代码块内部被外部访问到;
var i = 0;
function nihao(){
let numm = 0;
j = 0;
var num = 0;
console.log(i,num,numm,j);
num++;
numm++;
i++;
j++;
}
nihao();
nihao();
try{
console.log("j:"+j);
console.log("num:"+num);
}catch(err){
console.log(err);
console.log("变量找不到")
}
2.全局作用域
在最外层声明的变量就是全局作用域,这里有两个注意点:
- 为window对象动态添加的属性默认也是全局的;
- 函数中未使用任何关键字声明的变量为全局变量(本质上时给window对象添加了属性);
3.作用域链
作用域链本质上是底层的变量查找机制,浏览器变量查找机制如下:
- 在函数被执行时,会优先查找当前函数作用域中查找变量;
- 如果当前作用域查找不到则会依次逐级查找父级作用域直到全局作用域;
function first(){
let num = 1;
function second(){
let num = 2;
function three(){
console.log("three:"+num);
}
three();
console.log("second:"+num);
}
second();
console.log("first:"+num);
}
first();
console.log(num);
var num = 0;
console.log(num);
这里如果不用var声明最外层的num则会报错,但声明之后则不会报错,但值变为了underfined,这种情况叫变量提升,下面我们会讲到这种情况。
4.JS垃圾回收机制
JavaScript中的垃圾回收机制是自动进行的,也就是说开发者无需手动管理内存。垃圾回收的目的是检测和释放不再使用的内存,以便其他变量可以使用。下面是一些JS中常用的垃圾回收技术:
-
标记清除:垃圾回收器首先会标记所有的变量,然后从根变量开始遍历所有可访问的变量,并将其标记为“存活”。然后,垃圾回收器会遍历内存,清除未标记的变量。
-
引用计数:每个变量都有一个引用计数,表示有多少个引用指向它。当一个变量的引用数变为0时,说明它不再被使用,垃圾回收器会回收该变量的内存。
-
分代回收:根据变量的生命周期将其分为不同的代,通常是新生代和老生代。垃圾回收器会更频繁地检查新生代,因为这些变量的生命周期较短,而对老生代的检查则较少。这样可以减少垃圾回收的开销。
需要注意的是,垃圾回收器并不是实时执行的,而是在一些触发条件下才会执行,比如当内存达到一定限制时或者浏览器空闲时。这是为了避免影响JavaScript程序的性能。同时,开发者也可以手动触发垃圾回收,使用gc()
方法可以执行垃圾回收,这里就不进行展开了。
5.闭包操作
JavaScript的闭包是指函数能够记住并访问其词法作用域,即使该函数在其词法作用域外执行。简单来说,闭包就是函数和其词法环境的组合。
当一个函数内部定义了另一个函数,并且内部函数引用了外部函数的变量时,就会产生闭包。这样的闭包能够使变量的值在外部函数执行完后仍然保持。
闭包的特性有以下几点:
- 内部函数可以访问外部函数的变量,即使外部函数已经执行完毕。
- 闭包函数会拷贝外部函数的变量副本,而不是引用,这样即使外部函数的变量改变,闭包中的变量值也不会受到影响。
- 闭包可以让变量长期驻留在内存中。
闭包在实际开发中的应用非常广泛,例如:
- 封装私有变量:使用闭包可以创建私有变量,并通过返回函数的方式暴露出一些对外的接口,隐藏内部的实现细节。
- 延迟执行:通过使用闭包,可以实现函数的延迟执行,比如在定时器或事件回调中使用闭包,可以保持对外部变量的引用。
- 模块化开发:使用闭包可以创建模块化的代码结构,避免全局变量的污染。
需要注意的是,闭包会引用外部函数的变量,导致外部函数的变量不能被正常释放,可能导致内存泄漏问题。因此,在使用闭包时需要注意避免循环引用以及合理释放资源。
function outer() {
var outerVar = 'I am outer';
function inner() {
console.log(outerVar);
}
return inner;
}
var closure = outer();
closure(); // 输出: I am outer
6.变量提升
变量提升是JS中比较奇怪的现象,它允许在变量声明之前即被访问,但仅存在于var中,正因如此,es6中出现了let和const来声明变量,这两个关键字不会产生变量提升,变量提升的现象就和上述代码一样,先访问后声明,变量的值为underfined。