简单前置
JS引擎在正式执行一个.js文件中的代码时会先执行两个步骤,再正式执行JS代码
步骤一: 语法检查
该步骤会检查代码中有没有书写错误,比如一些关键字没写对,var写成vra,return写成retrun,声明变量时未进行初始化但是多写了个等号等等语法问题,全都没有问题就会进入下一个步骤
步骤二: 预编译
预编译会把程序中声明变量/函数进行优先处理并放入一个对象中,这个对象称为执行上下文对象,预编译会分为全局预编译和函数预编译
全局预编译发生在.html文件被打开时,与其对应的是全局执行上下文对象
函数预编译发生在函数执行时,与其对应的是函数执行上下文对象
全局预编译
例子:
var name = '悟空';
var gender = 'male';
function getName(){
console.log(name);
}
function getGender(){
console.log(gender);
}
function getName(){
console.log('八戒');
}
预编译阶段
编译器会首先创建一个空的全局执行上下文对象(global object)简称GO对象
var GO = {}
然后优先收集所有的函数声明(会优先收集函数,我会在后面说明)并把函数名称放入到GO对象中,赋值为函数体,当遇到多个函数名相同的情况时,只会放入一个函数名称,并将函数体进行覆盖操作,只保留在物理位置上最后声明函数的函数体
var GO = {
getName: function (){ console.log(name) } 不好意思同名函数函数体被覆盖 ==> function (){ console.log('八戒')},
getGender: function (){ console.log(gender) }
}
之后收集所有的变量声明如果有多个重复变量名相同的情况,只会放入一个变量名并赋值undefined
var GO = {
getName: function (){ console.log(name) } 不好意思同名函数函数体被覆盖 ==> function (){ console.log('八戒')},
getGender: function (){ console.log(gender) },
name: undefined,
gender: undefined
}
此时全局预编译过程已经完成接下来就是正式执行阶段
正式执行阶段
按照顺序执行代码并对变量进行赋值,赋值时会覆盖预编译时预设的值,无论是变量还是函数
var GO = {
getName: function (){ console.log(name) } 不好意思同名函数函数体被覆盖 ==> function (){ console.log('八戒')},
getGender: function (){ console.log(gender) },
name: '悟空',
gender: '八戒'
}
我们运行并打印结果就可以得到
console.log(name);
// 结果 悟空
console.log(gender);
// 结果 male
getName();
// 结果 八戒
getGender()
// 结果 male
函数预编译
例子:
function fn(a, b){
var a = 1;
var b = 2;
var c = 3;
function handle1(){
console.log(a);
}
function handle1(){
console.log('10010');
}
function handle2(){
console.log(b);
}
}
fn('悟空', function(){ console.log(10086); });
编译器会首先创建一个空的函数执行上下文对象(activation object)简称AO对象
var AO = {}
在函数执行时会优先收集变量声明与形参,如果出现多个重复声明或者是形参名与变量名相同时只会放入一个变量/形参名,并将其赋对应的实参值,如果没有实参值则为undefined
var AO = {
a: '悟空',
b: function(){ console.log(10086); },
c: undefined
}
然后收集该函数内部声明的函数,当遇到多个函数名相同的情况时,只会放入一个函数名称,并将函数体进行覆盖操作,只保留在物理位置上最后声明函数的函数体
var AO = {
a: '悟空',
b: function(){ console.log(10086); },
c: undefined,
handle1: function() { console.log(a); } 不好意思同名函数函数体被覆盖 ==> function() { console.log('10086'); },
handle2: function() { console.log(b); }
}
此时函数预编译过程已经完成接下来就是正式执行阶段
正式执行阶段
按照顺序执行代码并对变量进行赋值,赋值时会覆盖预编译时预设的值,无论是变量还是函数
var AO = {
a: '悟空',
b: function(){ console.log(10086); },
c: 3,
handle1: function() { console.log(a); } 不好意思同名函数函数体被覆盖 ==> function() { console.log('10086'); },
handle2: function() { console.log(b); }
}
我们运行并打印结果就可以得到
handle1();
// 结果 10086
handle2();
// 结果 1
回收问题
在全局全局预编译下为什么会优先收集函数声明?
没有为什么,只能说这是一个值的记住点,可以尝试以下代码
console.log(fn);
var fn;
function fn(){
console.log(123);
}
fn = 1;
// 结果 ƒ fn(){ console.log(123); }
打印结果是一个函数,这就侧面印证了函数优先于变量进行提升的
注:
打印语句不要写在fn=1的后面,因为打印语句属于正式执行阶段的操作,正式执行阶段的赋值操作会覆盖预编译阶段为已声明变量/函数的预设值
解决一个问题
什么是提升?
我们可以在一个变量/函数未声明时对其进行访问(当然该变量/函数在后面的代码中必须进行声明)
如何做到的?
通过预编译
console.log(a);
var a = 1;
// 结果 undefined
???
掌握预编译并不是主要目的,而是执行上下文对象,这个会对理解this有很大帮助
注
该文章仅仅是学习过程的中理解,如果存在问题,欢迎提出与讨论
参考书籍
《YOU DON'T KNOW JavaScript》
文章详细解释了JavaScript引擎在执行.js文件时的语法检查和预编译步骤,包括全局预编译和函数预编译的过程,以及它们如何影响变量和函数的处理。此外,还提及了提升现象和执行上下文对象在编程中的作用。

1192

被折叠的 条评论
为什么被折叠?



