由于JS是弱类型语言,所以在运行的时候才能确定变量,但是在运行之前都会JS代码都是先编译再执行, 编译后会生成执行上下文和可执行的代码
调用栈call stack
用来管理函数调用关系的一种数据结构,先进后出,如果执行到当前函数,就把当前函数的执行上下文推入栈中,如果函数执行完毕,则推出销毁
执行上下文
类型
- 全局执行上下文,第一次载入JS代码的时候被创建,有且仅有一个全局执行上下文
- 函数局部执行上下文,每次执行函数的时候都会创建
组成部分
- 变量环境
- 词法环境
- outer-词法上外层执行上下文的指向
- this-执行阶段所绑定的执行上下文
创建流程
- 当遇到var,function等声明的变量或函数的时候,会把此类函数的变量添加到变量环境的变量对象上,等到后面执行阶段才会赋值-变量提升的原因
- 当遇到let/const等ES6的块级作用域的时候,会把他们依次推入到词法环境中,词法环境内部有一个小型栈用于维护词法变量,栈的底部是最外层的变量,进入下一个块域就会把内部的变量推入到此栈的顶部,执行结束后推出
- 根据词法判定指向outer
- 根据执行情况绑定this
变量查找顺序
词法栈顶向下 => 变量环境 => 闭包(如果存在) => outer指向的外部执行环境 => 全局执行环境
作用域/作用域链
- 变量和函数有权访问的范围,控制着他们的可见性和声明周期
- 词法决定的,也就是在编写的时候就已经确定
- 由于每个执行环境都有一个outer指向起词法决定的外部执行环境,所以基本词法嵌套的执行环境都可以通过outer链到全局执行环境,这个链路就叫做作用域链
- 变量也是通过作用域链来查找的
闭包
- 能够访问其他作用域中变量的函数叫闭包函数
- 如果一个函数返回了内部函数,内部函数用到了此函数的变量,则这些变量就会变成一个闭包,被一直存在作用域链上,以便内部函数能够访问到
this
- 简单来说为了能够使内部方法访问到内部属性,JS提出了this机制
- 只有当函数被调用的时候才能确定this绑定的执行上下文
- 每个执行上下文都有一个this
由于JS机制没有明确this的只想,所以根据函数调用的不同来判定this最终的绑定
- 函数直接的调用,默认存在一个隐式绑定到全局,也就是window,其他宿主可能是undefined
- 通过call/apply/bind来手动强制绑定this
- 构造函数中的this指向的是构造函数最终的实例
- 嵌套的函数中的this是不会继承的,最终还是看函数执行的绑定情况
- 箭头函数不存在this,所以内部this用的是父级的this
- setTtimout等回调this指向的是全局,如果是箭头函数,则需要根据词法分析决定