JS Advance --- 作用域链 和 内存管理

163 阅读5分钟

作用域链

  1. 当我们查找一个变量的时候,真实的查找顺序是按照作用域链来进行查找的

  2. 变量的父级作用域是静态作用域不是动态作用域,这也就意味着变量的父级作用域取决于定义位置的上一层,是在函数定义(编译)的时候就已经被确定,而不是在函数运行的时候,才被确定下来

let name = 'coderwxf'

function foo() {
  function baz () {
    console.log(name)
  }

  baz()
}

foo()

ItRL1W.png

在执行过程中,每一个函数作用域内部都有一个scope chain,表示的就是当前当前函数的作用域链

此时baz函数在执行的时候,打印name的值,

  1. 去自己的AO中查找
  2. 自己的AO中不存在,去其父级AO中查找
  3. 父级AO中不存在,去父级的父级的AO中查找
  4. 。。。。
  5. 直到查找到GO,如果GO中依旧不存在,那么就会报错

作用域练习

var n = 100

function foo() {
  // 函数的作用域链式在编译的时候就已经被确定的
  // JS中的作用域链式静态作用域
  console.log(n) // => 100
}

function baz() {
 var n = 200

 foo()
}

baz()
function foo() {
  var a = b = 100
  /*
   上面的代码会实际编译为
   var a = 100
   b = 100
  */
}

foo()

console.log(b) // => 100

console.log(a) // => error
var x = 0

// 如果函数参数有默认赋值的时候,默认赋值的函数会单独形成一个参数作用域
function fun(x, y = function() {x=3; console.log(x);}) {
  console.log(x)
  var x = 2
  y()
  console.log(x)
}

fun()
console.log(x)

/*
  =>
    undefined
    3
    2
    0
*/
var x = 0

function foo() {
  x=3;
  console.log(x);
}

// 但如果只是函数引用的时候
// 并不会形成特殊的参数作用域
function fun(x, y = foo) {
  console.log(x)
  var x = 2
  y()
  console.log(x)
}

fun()
console.log(x)

/*
  =>
   undefined
   3
   2
   3
*/

内存管理

不管什么样的编程语言,在代码的执行过程中都是需要给它分配内存的,

不同的是某些编程语言需要我们自己手动管理内存某些编程语言也会自动帮助我们管理内存

  • 手动管理内存:比如C、C++,包括早期的OC,都是需要手动来管理内存的申请和释放的(malloc和free函数)

  • 自动管理内存:比如Java、JavaScript、Python、Swift、Dart等,它们有自动帮助我们管理内存

不管以什么样的方式来管理内存,内存的管理都会有如下的生命周期:

  • 第一步:分配申请你需要的内存(申请)
  • 第二步:使用分配的内存(存放一些东西,比如对象等)
  • 第三步:不需要使用时,对其进行释放

所以 JS是自动进行内存管理的,会自动为我们进行内存的分配和释放

内存管理

JavaScript会在定义变量时为我们分配内存

  • JS对于基本数据类型内存的分配会在执行时,直接在栈空间进行分配
  • JS对于复杂数据类型内存的分配会在堆内存中开辟一块空间(堆),并且将这块空间中创建对应的数据,并将对于的引用返回,在栈中使用对应的变量指向

Itty8H.png

垃圾回收

因为内存的大小是有限的,所以当内存不再需要的时候,我们需要对其进行释放,以便腾出更多的内存空间

手动管理内存的语言中,我们需要通过一些方式自己来释放不再需要的内存,比如free函数:

  • 但是这种管理的方式其实非常的低效,我们需要时刻注意内存的管理,影响我们编写逻辑的代码的效率
  • 如果我们对内存管理不善,如忘记释放内存,一不小心就会产生内存泄露

所以大部分现代的编程语言都是有自己的垃圾回收机制:

  • 垃圾回收的英文是Garbage Collection,简称GC
  • 对于那些不再使用的对象,我们都称之为是垃圾,它需要被回收,以释放更多的内存空间
  • 我们的语言运行环境在内存中会自己提供垃圾回收器,比如Java的运行环境JVM,JavaScript的运行环境js引擎都会自动内置内存回收器
  • GC并不会再变量变成垃圾变量的时候直接回收,而是会在合适的时机统一进行回收
常见的垃圾回收算法

引用计数

  • 当一个对象有一个引用指向它时,那么这个对象的引用就+1

  • 当一个对象的引用为0时,这个对象就可以被销毁掉

这个算法有一个很大的弊端就是无法处理循环引用,如果遇到循环引用,需要手动销毁

ItN5Yt.png

标记清除

这个算法是设置一个根对象(root object){在JS中,所对应的那个根对象就是GO对象},垃圾回收器会定期从这个根开始,找所有从根开始有引用到的对象,对 于哪些没有引用到的对象,就认为是不可用的对象,并进行相应的清除操作

这个算法可以很好的解决循环引用的问题

JS引擎比较广泛的采用的就是标记清除算法,当然类似于V8引擎为了进行更好的优化,它在算法的实现细节上也会结合 一些其他的算法。

ItNXTp.png