js 代码在执行时, js 引擎会先进行编译处理,并创建执行上下文;编译完成之后进入执行阶段。
变量提升
在编译阶段,会优先对变量和函数的声明进行处理,存储到执行上下文中,等待执行阶段调用。
同名的函数和变量,在声明提升时,函数声明优先于变量声明,展示为函数;在变量赋值之后,展示为变量值。
showName()
var showName = function() {
console.log(1);
}
function showName() {
console.log(2);
}
showName();
// 2
// 1
变量提升的问题
- 容易被误覆盖掉。
- 本应被销毁的值没有被销毁。例如 for 循环中的声明变量 i
解决方案
使用 let / const 声明
执行上下文
执行上下文是执行代码时的运行环境信息。调用一个函数时,就会进入这个函数的执行上下文,确定函数在执行期间用到的变量,this,对象以及函数等。
- 在执行全局代码时,会编译全局代码并创建全局执行上下文。在整个生命周期内,全局执行上下文有且只有一个
- 在执行函数时,会编译函数并创建函数执行上下文。::函数只有被调用的时候才会被编译::。
- 使用 eveal 时,也会创建执行上下文
对执行上下文的管理通过调用栈来完成。
- 栈是一种后进先出的数据结构,在其他编程语言中,执行过程也是通过栈来管理函数间的调用关系的。
- 在代码中可以通过
console.trace输出当前函数的调用关系。
入栈的执行上下文超过一定数目的时候就会触发栈溢出的错误。
作用域
作用域控制着变量和函数的可见性和生命周期。ES6 的作用域分为 3 种:
- 全局作用域
- 函数作用域
- 块级作用域
js 是如何做到即支持块级作用域,又支持变量提升的?
- 在执行上下文中,有着 变量环境 和 词法环境的区分
- var 声明的变量,会被存放到 变量环境 中
- let/const 声明的变量,会被存放到 词法环境中
- 词法环境中,每个块级作用域也是独立存在的。通过调用栈的方式进行管理
作用域链
词法作用域
词法作用域是静态作用域,是由代码中函数声明的位置来决定的,通过它就能够预测代码在执行过程中如何查找标识符。
作用域链是由词法作用域决定的。
闭包
根据词法作用域的规则,内部函数 getName 和 setName 总是可以访问它们的外部函数 foo 中的变量。
function foo() {
var myName = 'b';
let test1 = 1;
var innerBar = {
getName:function(){
console.log(test1)
return myName
},
setName:function(newName){
myName = newName
}
}
return innerBar
}
var bar = foo()
bar.setName('a')
bar.getName()
console.log(bar.getName())
当调用 bar.getName 时,函数foo 已经执行结束了,但是依旧可以使用 foo 中的变量。
当通过调用外部函数返回一个内部函数后,外部函数已经执行结束,但是内部函数中引用的外部函数变量依旧保存在内存中,我们就把这些变量的集合称为闭包。
闭包的回收
- 如果引用闭包的函数是一个全局变量,那么闭包会一直存在直到页面关闭时。
- 如果引用闭包的函数是个局部变量,等函数销毁后,在下次 js 引擎执行垃圾回收时,判断闭包这个内容如果不再被使用,那么 js 的垃圾回收器就会回收这块内存
this
在对象内部的方法中使用自有的属性是一个很普遍的需求,这就得通过 this 机制来实现
作用域链和 this 是两种不同的机制。this 跟调用的方式有关,作用域链跟函数声明所在的位置有关。
设置函数上下文中this
- 通过 call / bind / apply 方法设置
- 通过修改调用方法设置
- 在全局环境中调用一个函数,函数内部的 this 指向的是全局变量 window
- 通过对象来调用其内部的方法,该方法的执行上下文中的 this 指向对象本身。
- 通过构造函数中设置。
this 中的缺陷
嵌套函数中的 this 不会从外层函数中继承
```javascript
var myObj = {
name : "name",
showThis: function(){
console.log(this)
function bar(){console.log(this)}
bar()
}
}
myObj.showThis()
```
1. 通过声明中间变量来保存 this 改为作用域链的方式调用外层函数中的this。
2. 使用箭头函数来解决这个问题, 箭头函数没有自己的执行上下文,会继承调用函数中的 this。
总结
执行上下文中包含
- 变量环境
- 词法环境
- 作用域链
- this
这些都是在编译阶段完成后就确认的了。