JavaSCript
javascript通常被归类为“动态”或者“解释执行”语言,其引擎进行编译的步骤与传统的编译编译语言相似,但是 也有不同的地方。传统编译语言的一段源代码执行之前会经历三部曲:分词/词法分析、解析/语法分析、代码生 成。而javascript相比之下,要略微复杂一些。javascript在代码执行之前的很短时间内,javascript引擎会 为后面的代码执行创造最优的条件,来保证性能最佳,这就是预编译环节
代码是怎么运行的?
- 在执行过程中,若使用未声明的变量,js执行会报错
- 在一个变量定义之前使用它,不会报错,但是该变量的值为undefined,而不是定义的值
- 在一个函数定义之前使用它,是不会报错,且函数能正确执行
//直接打印没有声明的变量
console.log(Name)//报错
console.log(myName);//undefined
var myName = 'abc'
showName() //hello
function showName(){
console.log('hello');
}
变量提升
javaScript 代码在执行过程中,JavaScript引擎会把变量声明部分和函数声明部分提升到代码的最前面的“行为”
就比如上述代码中的var myName = ‘abc’ 并且打印myName。实际上javaCript在其执行前,会将其编译成如下所示:先声明变量,打印操作,最后是赋值,所以显然由于找不到值,打印结果为undefined
var myName;
console.log(myName);//undefined
myName = 'abc'
我们再看一个函数实例:在函数声明之前调用它也不会报错或者也不会找不到值是为什么。这个时候根据变量提升的规则就明白了,在函数执行前,JavaScript引擎将其编译成如下面代码展示的一样,将函数声明提前到代码打印操作执行之前,就跟正常写在前面一样,这就是JavaScript的编译机制。
function showName(){
console.log('hello');
}
showName() //hello
预编译
函数预编译
预编译发生在函数执行的前一刻(四部曲)(函数体)
-
创建AO对象(Activation Object)
-
找形参和变量声明,将变量声明和形参作为AO的属性名,值为undefined
-
将实参和形参值统一
-
在函数体里找函数声明,将函数名作为AO对象的属性名,值赋予函数体
function fn(a) {
console.log(a);//[Function:a]
var a = 123
console.log(a);//123
function a() {}
console.log(a);//123
var b =function(){}
console.log(b);//[Function:b]
function d(){}
}
// AO:{
// a: undefined 1 function a(){} 123,
// b: undefined function(){},
// d: function d(){}
// }
fn(1)
根据四部曲,我们自己来模仿一下javaScript引擎的预编译机制: 首先,在函数被执行时,生成一个上下文对象AO:{ },找形参a,以及变量声明b,将变量声明和形参作为AO的属性名,值为undefined。而后我们将实参与形参统一,按照其顺序,首先a作为形参1,之后声明为function,最后赋值为了123,所以最终a = 123,同样b也是如此,直接声明为函数,所以打印之后就是[Function:b].在寻找函数声明,将函数名d作为AO对象的属性名,值赋予函数体,也就是d: function d(){}。而一步步按照顺序执行,便可以得到正确的结果
function test(a,b){
console.log(a)//1
c = 0
var c;
a = 3
b = 2
console.log(b)//2
function b(){}
function d(){}
console.log(b)//2
}
// AO:{
// a:undefined 1 3,
// b:undefined function b(){} 2,
// c:undefined 0,
// d:function b(){}
// }
test(1)
上述代码也是类似的,可以按照函数预编译四部曲,自己试着推导一下。
全局预编译
预编译也发生在全局(三部曲)
-
创建GO对象
-
找形参和变量声明,将变量声明和形参作为GO的属性名,值为undefined
-
在全局里找函数声明,将函数名作为GO对象的属性名,值赋予函数体
// GO:{
// global: undefined 100,
// fn:function (){}
// }
var global = 100
function fn(){
console.log(global);
}
// AO:{
// }
fn()
上述代码便是在全局进行预编译,在首先全局上下文GO:{ }中,有全局变量global,先赋值为undefined,在此没有形参,所以统一之后global就被赋值为100,最后再是函数function fn().不过在此注意的是,fn也会生成一个执行上下文对象AO:{ },再根据函数预编译四部曲,很显然里面为空,所以global上述结果为100.
结合
在js中,在任意的代码块中添加包装函数(如下例中的function foo()就是包装函数),可以将内部的变量和函数定义“隐藏”起来,私有化函数和变量,也就是说,函数访问外部作用域是单向通道,外部作用域无法访问包装函数内部的任何内容,
var = 2;
function foo() {
var a = 3;
console.log(a); //3
}
foo()
console.log(a); //2
下面的代码也是一样,由于fn中又声明了一个变量global,内部的global打印事关函数fn().预编译环节,函数体中声明变量global,值赋为undefined,此时打印global,结果就是undefined、之后赋值为200,那么打印结果自然为200.
global = 100
function fn(){
console.log(global);//undefined
global = 200
console.log(global);//200
var global = 300
}
fn()
var global
希望这些对学习js的小伙伴有用,谢谢!