声明和赋值:
有一段代码 var myname = '张三' 可以拆解为
var myname = undefined // 声明部分
myname = '张三' // 赋值部分
变量提升:
在JS代码执行过程中,JS引擎把变量的声明部分和函数的声明部分提升到代码开头的“行为”。变量被提升后,会给变量初始化设置默认值undefined
举个例子: 有代码如下
<script>
showName()
console.log(myname)
var myname = '张三'
function showName() {
console.log('函数showName被执行')
}
</script>
经过JS变量提升后,模拟提升后的代码
<script>
function showName() { // 函数声明部分被提升
console.log('函数showName被执行')
}
var myname = undefined // 变量申明部分被提升
showName() // 可执行部分
console.log(myname) // 可执行部分
myname = '张三' // 可执行部分 去掉var声明副本, 保留赋值部分
</script>
对比上方两段代码可以得出结论: 函数和变量 在执行之前都提升到了代码开头
JS代码的执行流程
JS代码执行之前需要被 JS引擎编译,编译完成后,才会进入执行阶段
一.编译阶段
输入一段JS代码,经过编译后,会生成两部分内容: 执行上下文 和 可执行代码
执行上下文: JS执行一段代码时的运行环境。确定该函数在执行期用到的 this,变量,函数,对象等
执行上下文中存在一个变量环境(variableEnvironmen)的对象, 该对象中保存了变量提升的内容
变量环境对象如何生成的:
<script>
showName()
console.log(myname)
var myname = '张三'
function showName() {
console.log('函数showName被执行')
}
</script>
- 执行到第二行,第三行代码时,不是声明操作,js引擎不做任何处理
- 执行到第四行代码,var myname = '张三' 时, 这行是经过 var 声明的,JS引擎会在环境对象中 创建一个名为myname的属性,并使用undefined对其初始化
- 执行到第五行代码的时候, js引擎发现一个通过function定义的函数, 所以它将函数定义存储到堆(HEAP)中,并在环境对象中创建一个showName的属性,然后将该属性值指向于堆中函数的位置
此时 变量环境对象可以简单理解如下格式为:
variableEnvironment:
myname -> undefined,
showName -> function: {console.log('函数showName被执行')}
到这就生成了变量环境对象, 接下来js引擎会把声明以外的的代码编译成字节码,可以简单理解为
<script> // 可执行代码
showName() // 可执行部分
console.log(myname) // 可执行部分
myname = '张三' // 可执行部分 去掉var声明副本, 保留赋值部分
</script>
此时, 编译阶段已经有执行上下文和可执行代码。接下来进入执行阶段
二.执行阶段
JS引擎开始执行 “可执行代码”(就是上面可执行部分的代码)
- 执行到第二行代码 showName() 时,js引擎开始在变量环境中查找该函数, 变量环境对象中存在该函数的引用,所以JS引擎开始执行该函数,并输出 '函数showName被执行'
- 执行到第三行代码时, 打印myname的信息, js引擎开始在变量环境中查找该变量,由于变量环境存在myname变量,并且其值为undefined,所以此时输出undefined
- 执行到第四行代码时, 把 '张三' 赋值给 myname变量,赋值后变量环境中的myname属性值改变为'张三', 此时变量环境如下
variableEnvironment:
myname -> '张三',
showName -> function: {console.log('函数showName被执行')}
总结: ---- 先编译,再执行
JS代码在执行之前需要先编译
在编译阶段,变量和函数会被存放到变量环境中,变量的默认值会被设置为undefined
在编译阶段,存在两个相同的函数,后定义的会覆盖掉之前定义的
1:如果是同名的函数,JS编译阶段会选择最后声明的那个。
2:如果变量和函数同名,那么在编译阶段,变量的声明会被忽略
在编译阶段, 函数提升要比变量提升的优先级要高一些
在代码执行阶段,JS引擎会从变量环境中去查找自定义的变量和函数
代码中出现相同的变量或者函数怎么办?
在执行一段js代码之前,会编译代码,并将代码中的变量和函数存储到执行上下文中的变量环境中
<script>
function showName() {
console.log('张三');
}
showName();
function showName() {
console.log('李四');
}
showName();
</script>
分析一下,首先编译阶段:
执行到第二行的showName声明函数,会将该函数体放到变量环境中,
执行到第四行的showName声明函数,继续存放到变量环境中,发现变量环境已有showName函数,第二个showName函数会将第一个showName函数覆盖掉
接下来是执行阶段:
JS引擎会从变量环境中去查找定义的showName函数,查找到showName函数并打印出'李四'
下面代码会输出啥?
<script>
showName()
function showName() {
console.log('1:', 1)
}
var showName = function() {
console.log('2:', 2)
}
</script>
分析一下,编译阶段
执行到第三行的时候,会在变量环境中定义showName属性,其值为showName函数体
当执行到第六行代码的时候, var定义变量showName, 此时变量环境中已有showName函数体,所以此变量声明会被忽略
执行阶段
showName()
showName = function() {
console.log('2:', 2)
}
所以打印的是 1