个人对JavaScript预编译的理解
什么是js的预编译
马上要找工作了,之前学习JS都是很基础的皮毛,作为当前最火热的语言,本人一定是要研究的,然而刚接触到预编译我就快疯了,对于一个脑子不好使的人来说真的太绕了,饶了好久也不知道有没有绕明白,所以先记载下来,以后发现有啥不对的再纠正。
首先,JS解释器在执行一段script脚本时,首先会进行预编译,将代码中声明的变量和函数进行处理,然后才会按代码顺序翻译执行,那么JS在预编译和执行阶段分别进行了些什么操作呢?网上有很多大神的帖子给了说明:
1. 在执行代码前会进行类似“预编译”的操作:首先会创建一个当前执行环境下的活动对象,并将那些用var申明的变量设置为活动对象的属性,但是此时这些变量的赋值都undefined,并将那些以function定义的函数也添加为活动对象的属性,而且它们的值正是函数的定义
2. 在解释执行阶段,遇到变量需要解析时,会首先从当前执行环境的活动对象中查找,如果没有找到而且该执行环境的拥有者有prototype属性时则会从prototype链中查找,否则将会按照作用域链查找;遇到var a = ...这样的语句时会给相应的变量进行赋值(注意:变量的赋值是在解释执行阶段完成的,如果在这之前使用变量,它的值会是undefined)
看完之后我的表情是懵逼的,内心是崩溃的,,,活动对象啊,prototype啊都是什么鬼,(原谅本人是一个小白,这些东西都不懂),最后经过自己尝试,总结了一下
1.预编译阶段,首先会将所有以var显示声明的变量,或者"变量式"函数提到最前面创建,不管他们在声明时同时进行赋值与否,预编译创建时都给他们赋值undefined;对于"定义式"函数声明,即用function声明的函数,其函数也会被提出来创建,但是创建后赋值的内容是该函数定义本身;另外,var显示声明的变量会先于定义式函数被创建
2.上述预编译结束后,js代码按照代码顺序解释执行,当遇到赋值语句时,再更改之前预编译过的变量的值,如var a = 11;在预编译阶段,a变量会被提出来创建,赋值undefined,在执行阶段,当执行到这条语句,将11赋给a
FOR INSTANCE
呐,基本就是这样,下面用几个例子详细说明一下,答案会在后边给出,可以自己先想一下结果是什么~
<script type="text/javascript"> console.log(a); var a = 1; console.log(fa); fa(); function fa(){ console.log("方法fa()"); } console.log(fb); fb(); var fb = function(){ console.log("方法fb()"); }
fb();
</script>
像Java如果在一个变量被创建前使用这个变量,一定会报错,但是js在执行前经过了预编译事先创建了变量,所以不会报错;
首先进行预编译阶段,先将所有的变量和函数提到前面创建,并赋相应的初始值,上述的代码进过预编译处理等价成下面的代码形式:
<script type="text/javascript"> var a = undefined; var fb = undefined; function fa(){ console.log("方法fa()"); } console.log(a); a = 1; console.log(fa); fa(); console.log(fb); fb(); fb = function(){ console.log("方法fb()"); }
fb(); </script>
首先,被var声明的变量首先被前置,创建,赋值,其次是定义式函数的预编译处理;当预编译结束,进入执行阶段,从上往下顺序执行:
1)遇到了第一个console.log(a),由于事先经过了预编译创建了a变量,所以输出undefined;
2)再向下顺序执行,遇到了a=1赋值语句,此时将1赋值给了a;
3)继续,console.log(fa),fa是定义式函数,预编译赋值内容为函数的定义,输出
function fa(){
console.log("方法fa()");
}
4)执行fa(),即调用了fa函数,输出 方法fa()
5)执行console.log(fb),输出undefined
6)执行fb(),报错!Uncaught TypeError: fb is not a function,因为fb当前仍然是变量,还没有执行将函数赋给fb的操作;
7)将上行代码注释,继续执行,此时才将一个函数赋值给了fb
8)再次执行fb(),输出 方法fb()
另一个例子
<script type="text/javascript"> console.log(aNumber); var aNumber = 100; tweak(); function tweak() { var newThing = 0; console.log(newThing); newThing = aNumber; aNumber = 42; if (false) { var aNumber; aNumber = 123; } console.log(aNumber); console.log(newThing); } console.log(aNumber); </script>
首先还是要预编译,注意,此处有两个aNumber,一个是全局的,一个是在if代码段中的局部变量,全局变量的aNumber被预编译处理是毋容置疑的,然而,即使if语句不会被执行,但是预编译发现了if代码段中声明的aNumber局部变量,也会进行预编译!也就是说,预编译君照吃不误,不管你是全局变量、局部变量、执行时这个变量用得着还是用不着,只要显示的用var声明了,都会进行预编译,不过此处的两个aNumber作用域是不同的;
其次是tweak()定义式函数也会被预编译,预编译结束,进入执行阶段,下面公布作业答案~
undefined 0 42 undefined 100
可以在试一下,将代码var aNumber = 100;注释掉又会发生什么?
在执行第一句console.log(aNumber)就会报错!这是为啥?
因为全局变量被注释掉了,现在只有if代码块的aNumber被预处理创建,在作用域外调用局部变量当然不行了
SUMMARY
1)首先,JS执行脚本前会将所有的变量、函数预编译,且变量先于函数预编译
<script type="text/javascript"> console.log(fc);//输出fc方法的定义部分,如果变量和函数同名,则先预编译变量,后预编译函数,函数的声明就覆盖了变量 function fc(){ console.log("fc方法"); } var fc = "fc变量"; console.log(fc);//执行阶段,对fc赋值为"fc变量" </script>
可以看到,即使fc变量的定义语句在fc函数定义语句之后,但是预编译时还是会先预编译fc变量,同名情况下,后预编译的覆盖了前面的内容;再拿同名方法举例说明:
<script type="text/javascript"> //先编译,被后面的同名方法覆盖了方法体 function func(){ console.log("hello"); } func();//输出world function func(){ console.log("world"); } func();//输出world </script>
2)注意,JS中变量可以隐式创建,但是这类变量应该不会被预编译,类似下面的形式会报错;
<script type="text/javascript"> console.log(c);//Uncaught ReferenceError: c is not defined c =5; </script>
还有就是,在局部代码块中隐式创建的变量当做全局变量使用
3)最后,JS是按<script>代码块分块执行的,且上一个script块中的变量在下一个script中仍然适用;反之则不行,如
<script type="text/javascript"> var a = 2; console.log(c);//Uncaught ReferenceError: c is not defined </script> <script type="text/javascript"> var c = 5; console.log(a);//2 </script>
个人理解的感觉还是有些出入,有什么错误还望各位dalao指正