作用域链
-
当我们查找一个变量的时候,真实的查找顺序是按照作用域链来进行查找的
-
变量的父级作用域是静态作用域不是动态作用域,这也就意味着变量的父级作用域取决于定义位置的上一层,是在函数定义(编译)的时候就已经被确定,而不是在函数运行的时候,才被确定下来
let name = 'coderwxf'
function foo() {
function baz () {
console.log(name)
}
baz()
}
foo()
在执行过程中,每一个函数作用域内部都有一个scope chain,表示的就是当前当前函数的作用域链
此时baz函数在执行的时候,打印name的值,
- 去自己的AO中查找
- 自己的AO中不存在,去其父级AO中查找
- 父级AO中不存在,去父级的父级的AO中查找
- 。。。。
- 直到查找到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对于复杂数据类型内存的分配会在堆内存中开辟一块空间(堆),并且将这块空间中创建对应的数据,并将对于的引用返回,在栈中使用对应的变量指向
垃圾回收
因为内存的大小是有限的,所以当内存不再需要的时候,我们需要对其进行释放,以便腾出更多的内存空间
在手动管理内存的语言中,我们需要通过一些方式自己来释放不再需要的内存,比如free函数:
- 但是这种管理的方式其实非常的低效,我们需要时刻注意内存的管理,影响我们编写逻辑的代码的效率
- 如果我们对内存管理不善,如忘记释放内存,一不小心就会产生内存泄露
所以大部分现代的编程语言都是有自己的垃圾回收机制:
- 垃圾回收的英文是Garbage Collection,简称GC
- 对于那些不再使用的对象,我们都称之为是垃圾,它需要被回收,以释放更多的内存空间
- 我们的语言运行环境在内存中会自己提供垃圾回收器,比如Java的运行环境JVM,JavaScript的运行环境js引擎都会自动内置内存回收器
- GC并不会再变量变成垃圾变量的时候直接回收,而是会在合适的时机统一进行回收
常见的垃圾回收算法
引用计数
-
当一个对象有一个引用指向它时,那么这个对象的引用就+1
-
当一个对象的引用为0时,这个对象就可以被销毁掉
这个算法有一个很大的弊端就是无法处理循环引用,如果遇到循环引用,需要手动销毁
标记清除
这个算法是设置一个根对象(root object){在JS中,所对应的那个根对象就是GO对象},垃圾回收器会定期从这个根开始,找所有从根开始有引用到的对象,对 于哪些没有引用到的对象,就认为是不可用的对象,并进行相应的清除操作
这个算法可以很好的解决循环引用的问题
JS引擎比较广泛的采用的就是标记清除算法,当然类似于V8引擎为了进行更好的优化,它在算法的实现细节上也会结合 一些其他的算法。