浏览器中JS的执行机制
变量提升
指在JavaScript代码执行过程中,JavaScript引擎把变量的声明部分和函数的声明部分提升到代码开头的“行为”。变量被提升后,会给变量设置默认值 undefined
showName()
console.log(myName)
getName()
var myName = '阿斯顿'
function showName() {
console.log('showName')
}
var getName = function () {
return 'getName'
}
结果 'showName' => '阿斯顿' => throw getName is not a function
模拟变量提升
/* 变量提升 */
var myName = undefined
function showName() {
console.log('showName')
}
var getName = undefined
/* 代码执行 */
showName()
console.log(myName)
getName()
myName = '阿斯顿'
getName = function () {
return 'getName'
}
并非物理层面移动代码,而是在编译阶段被 js 引擎放入内存中
执行上下文是JavaScript执行一段代码时的运行环境
调用栈
- 每调用一个函数,JavaScript引擎会为其创建执行上下文,并把该执行上下文压入调用栈,然后JavaScript引擎开始执行函数代码。
- 如果在一个函数A中调用了另外一个函数B,那么JavaScript引擎会为B函数创建执行上下文,并将B函数的执行上下文压入栈顶。
- 当前函数执行完毕后,JavaScript引擎会将该函数的执行上下文弹出栈。
- 当分配的调用栈空间被占满时,会引发“堆栈溢出”问题。
作用域
作用域是指在程序中定义变量的区域,该位置决定了变量的生命周期。通俗地理解,作用域就是变量与函数的可访问范围,即作用域控制着变量和函数的可见性和生命周期。
全局作用域、函数作用域
ES5 之前js 只有
- 全局作用域,在代码任何地方都能访问,生命周期伴随页面的生命周期
- 函数作用域,在函数中定义的变量或函数,只能在函数内部访问,函数执行完后就会被销毁
块级作用域
示例
function foo(){
var a = 1
let b = 2
{
let b = 3
var c = 4
let d = 5
console.log(a)
console.log(b)
}
console.log(b)
console.log(c)
console.log(d)
}
foo()
编译并创建执行上下文
==================================================
变量环境:a = undefined; c = undefined
词法环境(栈):【b=undefined】
==================================================
var 声明的会放到变量环境,let,const 声明的会放到词法环境(lexical environment)。
词法环境维护了一个栈,进入一个作用域后,就会把变量压到栈顶,执行完后弹出。
执行代码
执行到代码块
==================================================
变量环境:a = 1; c = undefined
词法环境(栈):【b = 2】【b = undefined; d = undefined】
==================================================
执行到
console.log(a)
,a 先从词法环境向下找,然后在变量环境找执行到
console.log(a)
==================================================
变量环境:a = 1; c = 4
词法环境(栈):【b = 2】【b = 3; d = 5】
==================================================
执行到
console.log(a)
,a 先从词法环境向下找,然后在变量环境找块级执行完
==================================================
变量环境:a = 1; c = 4
词法环境(栈):【b = 2】
==================================================
词法环境弹出。后面继续执行。。。
作用域链与闭包
作用域链
作用域链是由词法作用域决定的,而词法作用域是由代码结构来确定的。
词法作用域
词法作用域就是指作用域是由代码中函数声明的位置来决定的,所以词法作用域是静态的作用域,通过它就能够预测代码在执行过程中如何查找标识符。
词法作用域是代码编译阶段就决定好的,和函数是怎么调用的没有关系。
块级作用域的变量查找
function bar() {
var myName = "极客世界"
let test1 = 100
if (1) {
let myName = "Chrome浏览器"
console.log(test)
}
}
function foo() {
var myName = "极客邦"
let test = 2
{
let test = 3
bar()
}
}
var myName = "极客时间"
let myAge = 10
let test = 1
foo()
1 - 5 表示查找顺序
闭包
function foo() {
var myName = "极客时间"
let test1 = 1
const test2 = 2
var innerBar = {
getName:function(){
console.log(test1)
return myName
},
setName:function(newName){
myName = newName
}
}
return innerBar
}
var bar = foo()
bar.setName("极客邦")
bar.getName()
console.log(bar.getName())
this
- 当函数作为对象方法调用的时候,this 指向该对象
- 当函数正常调用时,this 指向全局对象 window ,严格模式是 undefined
- 嵌套函数中的 this 不会继承外层函数的 this