深入JavaScript高级语法-学习笔记-02-作用域链-内存管理-垃圾回收

140 阅读5分钟

作用域链

var name = "why"

foo(123)
function foo(num) {
  console.log(m)
  var m = 10
  var n = 20
  function bar() {
    console.log(name) // why
  }

  bar()
}

函数内部在执行时是有一个作用域链的,在函数创建之初就在开辟的存储函数空间中存储了它的父级作用域,在FEC中除了VO同时还有它的作用域链scope chain:AO+ ParentScope,函数内部在进行查找时寻找它内部的VO对应的AO中有没有这个属性,若无则会去它的父级作用域进行查找,若父级找不到再继续向上,最后找到GO中,若GO中也没有,则会报错未定义。

函数的父级作用域在编译时就已经确定,跟执行的位置没有关系!

var message = "Hello Global"

function foo() {
  console.log(message)
}

function bar() {
  var message = "Hello Bar"
  foo()
}

bar() // Hello Global

这段代码中在编译解析阶段时,foo函数的父级作用域就已经确定,为GO,所以在作用域链查找过程中,函数内部查找不到就直接到了GO中进行查询,所以输出为Hello Global。

变量环境和记录

其实我们上面的讲解都是基于早期ECMA的版本规范 在最新的ECMA的版本规范中,对于一些词汇进行了修改 不再规定说执行上下文中一定是一个对象,VO现在称为VE(VariableEnvionment),其中的声明叫环境记录。

作用域提升面试题

function foo() {
  console.log(n) // undefined
  var n = 200
  console.log(n) // 200
}
var n = 100
foo()
var n = 100
function foo() {
	n = 200
}
foo()
console.log(n) // 200
var n = 100

function foo1() {
  console.log(n) // 100
}

function foo2() {
  var n = 200
  console.log(n) // 200
  foo1()
}

foo2()
console.log(n) // 100
var a = 100

function foo() {
  console.log(a) // undefined
  return 
  var a = 100
}

foo()
function foo() {
  var a = b = 100 // 可转化为 => 
  // var a = 100
  // b = 100
} 

foo()

console.log(a) // 报错,找不到a
console.log(b) // 100

内存管理

认识内存管理

  1. 不管什么样的编程语言,在代码的执行过程中都是需要给它分配内存的,不同的是某些编程语言需要我们自己手动 的管理内存,某些编程语言会可以自动帮助我们管理内存
  2. 不管以什么样的方式来管理内存,内存的管理都会有如下的生命周期:
  • 第一步:分配申请你需要的内存(申请)
    
  • 第二步:使用分配的内存(存放一些东西,比如对象等);
    
  • 第三步:不需要使用时,对其进行释放;
    
  1. 不同的编程语言对于第一步和第三步会有不同的实现:
  • 手动管理内存:比如C、C++,包括早期的OC,都是需要手动来管理内存的申请和释放的(malloc和free函 数)
  • 自动管理内存:比如Java、JavaScript、Python、Swift、Dart等,它们有自动帮助我们管理内存;

JS内存管理

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

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

JS垃圾回收

  1. 因为内存的大小是有限的,所以当内存不再需要的时候,我们需要对其进行释放,以便腾出更多的内存空间 。
  2. 在手动管理内存的语言中,我们需要通过一些方式自己来释放不再需要的内存,比如free函数 :
    • 但是这种管理的方式其实非常的低效,影响我们编写逻辑的代码的效率
    • 并且这种方式对开发者的要求也很高,并且一不小心就会产生内存泄露
  3. 以大部分现代的编程语言都是有自己的垃圾回收机制:
    • 垃圾回收的英文是Garbage Collection,简称GC
    • 对于那些不再使用的对象,我们都称之为是垃圾,它需要被回收,以释放更多的内存空间;
    • 而我们的语言运行环境,比如Java的运行环境JVM,JavaScript的运行环境js引擎都会内存 垃圾回收 器
    • 垃圾回收器我们也会简称为GC,所以在很多地方你看到GC其实指的是垃圾回收器;
  4. GC是如何知道哪些对象是不再使用的呢?

常见的GC算法

引用计数

当一个对象有一个引用指向它时,那么这个对象的引用就+1,当一个对象的引用为0时,这个对象就可以被销 毁掉; 这个算法有一个很大的弊端就是会产生循环引用;

标记清除

这个算法是设置一个根对象(root object),垃圾回收器会定期从这个根开始,找所有从根开始有引用到的对象,对 于哪些没有引用到的对象,就认为是不可用的对象,可以很好的解决循环引用的问题 JS引擎比较广泛的采用的就是标记清除算法,当然类似于V8引擎为了进行更好的优化,它在算法的实现细节上也会结合 一些其他的算法。