作用域和闭包、垃圾回收机制

2023/11/19 jses6

# 作用域和闭包、垃圾回收机制

了解作用域链的查找机制,使用闭包创建隔离作用域,避免全局变量污染。

  • 作用域规定了变量能访问的范围,离开范围就不能访问
    • 局部作用域
    • 全局作用域

# 1、局部作用域

# 1.1函数作用域

只能在函数内部访问,外部无法直接访问

  • 函数内部声明的变量,在函数外部无法被访问
  • 函数的参数也是函数内部的局部变量
  • 不同函数内部声明的变量无法互相访问
  • 函数执行完毕后,函数内部的变量实际被清空

# 1.2 块作用域

js中用{}包裹的代码称为代码块,代码块内部的变量外部 有可能 无法访问

  • 1.let声明的变量会产生块作用域,var 不会产生块作用域
  • 2.const声明的常量也会产生块作用域
  • 3.不同代码块之间的变量无法互相访问
  • 4.推荐使用 letconst

在for循环里声明var是可以被外部访问的,表面上看这是个块级作用域,但是因为使用var声明,所以不会产生块级作用域,大部分时候也只有这种情况会导致能访问

# 2、全局作用域

script标签和.js文件中最外层的就是所谓全局作用域,在此声明的变量在函数内部也能访问

  • 1.为 window 对象动态添加的属性默认也是全局的,不推荐!
  • 2.函数中未使用任何关键字声明的变量为全局变量,不推荐!!!
  • 3.尽可能少的声明全局变量,防止全局变量被污染

# 3、作用域链

本质上是底层的变量查找机制

  • 函数执行时,会优先从当前函数作用域中查找变量
  • 如果当前作用域查找不到会依次逐级查找父级作用域,直到全局作用域
  • 1.套关系的作用域串联起来形成了作用域链
  • 2.相同作用域链中按着从小到大的规则查找变量
  • 3.子作用域能够访问父作用域,父级作用域无法访问子级作用域

# 4、垃圾回收机制(Garbage Collection)

JS中内存的分配和回收都是自动完成的,内存在不使用时会被垃圾回收器自动回收。

# 4.1 内存的生命周期

JS中分配的内存,一般有如下生命周期:

  • 内存分配:当我们声明变量、函数、对象的时候,系统会自动为他们分配内存
  • 内存使用:即读写内存,也就是使用变量、函数等
  • 内存回收:使用完毕,由垃圾回收器自动回收不再使用的内存

全局变量不会被回收

大部分时候,局部变量不用了就会被自动回收掉

内存泄漏:因为某种原因,内存无法释放或未释放叫做内存泄漏

# 4.2 算法说明

堆栈分配:

  • 栈:由操作系统自动分配释放函数的参数、局部变量等,基本数据类型放到栈里
  • 堆:一般由程序员分配释放,若程序员不释放,由垃圾回收机制回收。复杂数据类型放在堆里。

# 4.2.1 引用计数法

IE采用的引用计数法,"内存不再使用",就是看一个对象是否有指向它的引用,没有了就回收对象

算法:

  • 1.跟踪记录被引用的次数
  • 2.如果被引用了一次,那么就记录次数1,多次引用会累加 ++
  • 3.如果减少一个引用就减1 --
  • 4.如果引用次数是0,则释放内存

问题:嵌套引用的情况下,尽管他们不再使用,也不会回收

# 4.2.2 标记清除法

是引用计数法的改进

核心:

  • 1.标记清除算法将“不再使用的对象”定义为“无法达到的对象
  • 2.就是从根部(在JS中就是全局对象)出发定时扫描内存中的对象。凡是能从根部到达的对象,都是还需要使用的。
  • 3.那些无法由根部出发触及到的对象被标记为不再使用,稍后进行回收。

# 5、闭包

一个函数对周围状态的引用捆绑在一起(也就是这个函数里面用了外层函数的变量之类的值),内层函数中访问到其外层函数的作用域

闭包= 内层函数+外层函数的变量

作用:封闭数据,提供操作,让外部也能访问内部数据

 function outer(){
            let a =10;
            function fn(){
                console.log(a);
            }
            return fn;// 这里别加括号(),因为这返回的是fn调用后的返回值,而不是fn本身
        }
    let fn01 = outer();
    fn01();
1
2
3
4
5
6
7
8
9

或者简约写法,直接将return写到函数名上

function outer(){
  let a =10;
  return function fn(){
           console.log(a);
          }
      }
  let fn01 = outer();
  fn01();
1
2
3
4
5
6
7
8

可能会引起的问题:内存泄漏

# 6、变量提升

即变量在被声明前被访问,仅存在于var声明的变量中

因为JS会把所有var声明的变量提升到整个作用域的最前面,然后在运行到var这一行再赋值

Last Updated: 2024/8/29 07:25:14