js执行机制
编译-->执行
编译过程
showName()
console.log(myName)
var myName = "xxx"
function showName(){
console.log("hi")
}
- 第 1 行和第 2 行,由于这两行代码不是声明操作,所以 JavaScript 引擎不会做任何处理;
- 第 3 行,由于这行是经过 var 声明的,因此 JavaScript 引擎将在环境对象中创建一个名为 myname 的属性,并使用 undefined 对其初始化;
- 第 4 行,JavaScript 引擎发现了一个通过 function 定义的函数,所以它将函数定义存储到堆 (HEAP)中,并在环境对象中创建一个 showName 的属性,然后将该属性值指向堆中函数的位置
编译过程生成执行上下文和可执行代码
执行上下文又包括
-
变量对象
- 变量提升后的变量
- 函数参数
-
词法环境
let/const
-
this
-
作用域
变量对象
- 函数形参
- 函数声明
- 变量声明
function foo(a) {
var b = 2;
function c() {}
var d = function() {};
b = 3;
}
foo(1);
变量对象如下:
AO = {
arguments: {
0: 1,
length: 1
},
a: 1,
b: undefined,
c: reference to function c(){},
d: undefined
}
变量提升(hositing)
扫一遍代码,提升函数声明和变量声明
两个原则:
- var 声明提到全局作用域,并赋值为undefined
- 函数声明就是引用 // 一段代码如果定义了两个相同名字的函数,那么最终生效的是最后一个函数。
// ps let const 也会被提升,但是如果在未被赋值前引用,会报错,暂时性死区(TDZ)
词法环境
块级作用域
块级作用域,同一个执行上下文变量查找顺序:
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(
当作用域块执行结束之后,其内部定义的变量就会从词法环境的栈顶弹出
this
- new 指向实例
- call,apply指向传入的对象
- 对象的方法指向调用对象
- 全局指向window
作用域链
作用域链在函数定义时就确定,指向其定义时的环境
闭包
// 将外部函数变量的装入背包
在JS中,内部函数时可以访问外部函数的,当调用外部函数返回一个内部函数,外部函数已经执行结束,但是内部函数对外部函数的引用依然在内存中,我们将这些变量叫闭包
// 在 JavaScript 中,根据词法作用域的规则,内部函数总是可以访问其外部函数中声明的变量,当通过调用一个外部函数返回一个内部函数后,即使该外部函数已经执行结束了,但是内部函数引用外部函数的变量依然保存在内存中,我们就把这些变量的集合称为闭包。
// 闭包的定义很简单:函数 A 返回了一个函数 B,并且函数 B 中使用了函数 A 的变量,函数 B 就被称为闭包。
你是否会疑惑,为什么函数 A 已经弹出调用栈了,为什么函数 B 还能引用到函数 A 中的变量。因为函数 A 中的变量这时候是存储在堆上的。现在的 JS 引擎可以通过逃逸分析辨别出哪些变量需要存储在堆上,哪些需要存储在栈上。