作用域链
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
内存管理
认识内存管理
- 不管什么样的编程语言,在代码的执行过程中都是需要给它分配内存的,不同的是某些编程语言需要我们自己手动 的管理内存,某些编程语言会可以自动帮助我们管理内存
- 不管以什么样的方式来管理内存,内存的管理都会有如下的生命周期:
-
第一步:分配申请你需要的内存(申请) -
第二步:使用分配的内存(存放一些东西,比如对象等); -
第三步:不需要使用时,对其进行释放;
- 不同的编程语言对于第一步和第三步会有不同的实现:
- 手动管理内存:比如C、C++,包括早期的OC,都是需要手动来管理内存的申请和释放的(malloc和free函 数)
- 自动管理内存:比如Java、JavaScript、Python、Swift、Dart等,它们有自动帮助我们管理内存;
JS内存管理
JavaScript会在定义变量时为我们分配内存
- JS对于基本数据类型内存的分配会在执行时,直接在栈空间进行分配
- JS对复杂数据类型内存的分配会在堆内存中开辟一块空间,并且将这块空间的指针返回值变量引用
JS垃圾回收
- 因为内存的大小是有限的,所以当内存不再需要的时候,我们需要对其进行释放,以便腾出更多的内存空间 。
- 在手动管理内存的语言中,我们需要通过一些方式自己来释放不再需要的内存,比如free函数 :
- 但是这种管理的方式其实非常的低效,影响我们编写逻辑的代码的效率
- 并且这种方式对开发者的要求也很高,并且一不小心就会产生内存泄露
- 以大部分现代的编程语言都是有自己的垃圾回收机制:
- 垃圾回收的英文是Garbage Collection,简称GC
- 对于那些不再使用的对象,我们都称之为是垃圾,它需要被回收,以释放更多的内存空间;
- 而我们的语言运行环境,比如Java的运行环境JVM,JavaScript的运行环境js引擎都会内存 垃圾回收 器
- 垃圾回收器我们也会简称为GC,所以在很多地方你看到GC其实指的是垃圾回收器;
- GC是如何知道哪些对象是不再使用的呢?
常见的GC算法
引用计数
当一个对象有一个引用指向它时,那么这个对象的引用就+1,当一个对象的引用为0时,这个对象就可以被销 毁掉;
这个算法有一个很大的弊端就是会产生循环引用;
标记清除
这个算法是设置一个根对象(root object),垃圾回收器会定期从这个根开始,找所有从根开始有引用到的对象,对 于哪些没有引用到的对象,就认为是不可用的对象,可以很好的解决循环引用的问题
JS引擎比较广泛的采用的就是标记清除算法,当然类似于V8引擎为了进行更好的优化,它在算法的实现细节上也会结合 一些其他的算法。