一个变量包含创建、初始化、赋值三个阶段
function的创建、初始化和赋值均会被提升。
var的创建和初始化被提升,赋值不会被提升。
let的创建被提升,初始化和赋值不会被提升。
变量提升
函数和变量声明部分都会提升到代码开头。
变量声明会设置初始值 undefined
函数则分成以下两种
JS 的执行流程
- 编译阶段
编译完后会生成 执行上下文 和 可执行代码
执行上下文里包含变量环境、词法环境、this、outer
其中变量环境保存了变量提升的内容 - 运行阶段
在变量环境中查找自定义的变量和函数
如果是同名的函数,JavaScript编译阶段会选择最后声明的那个。
如果变量和函数同名,那么在编译阶段,变量的声明会被忽略
何为执行上下文
JS 执行全局代码、调用函数或者调用 eval 函数时,会编译代码并创建执行上下文,里头包含了变量环境和词法环境变量、this、outer
调用栈
用来管理函数调用关系的一种数据结构。
管理执行上下文的栈就叫作执行上下文栈,也叫调用栈。
函数编译时,会创建执行上下文,并推入栈,函数执行完后就会出栈。
作用域
- 作用域就是变量与函数的可访问范围,即作用域控制着变量和函数的可见性和生命周期。
- es6 之前只有全局作用域跟函数作用域,es6 之后加入了函数作用域
- es6 采用的是词法作用域:即是由代码中函数声明的位置来决定的,而不是执行时才确定的
块级作用域
- 块级作用域是通过词法环境的栈结构来实现的,而变量提升是通过变量环境来实现,通过这两者的结合,JavaScript 引擎也就同时支持了变量提升和块级作用域了
- 管理执行上下文的叫调用栈。
- 变量提升是通过执行上下文里的变量环境来实现的。
- 块级作用域是通过执行上下文里的词法环境的栈结构来实现的。
作用域链
是按自己所在的执行上下文的词法环境=》自己所在执行上下文的变量环境=》变量环境的 outer 所指向的执行上下文的词法环境=》变量环境的 outer 所指向的执行上下文的变量环境
一直沿着 outer 查找,直到全局执行上下文,没找到就报错
闭包
内部函数引用了外部函数里的变量,并返回该内部函数,这样当外部执行完之后会出栈,但由于内部函数引用外部函数的变量仍然保存在内存中,将这些变量的集合称为闭包
产生闭包的核心有两步:第一步是需要预扫描内部函数;第二步是把内部函数引用的外部变量保存到堆中。
JS 机制
全局上下文中的 this: 非严格模式下 this 指向 window,严格模式下指向 undefined
函数执行上下文中的 this:默认指向 window
可以通过 call,apply,bind 更改 this 指向。
对象调用方法时,this 指向该对象
构造函数里 this 指向新对象
ES6 中的箭头函数并不会创建其自身的执行上下文,所以箭头函数中的 this 取决于它的外部函数