您的位置:首页 > Web前端 > JavaScript

javascript运行过程中的“预编译阶段”和“执行阶段”

2017-08-11 10:43 363 查看
javascript相对于其它语言来说是一种弱类型的语言,在其它如java语言中,程序的执行需要有编译的阶段

而在javascript中也有类似的“预编译阶段”(javascript的预编译是以代码块为范围,即每遇到一个代码块都会进行 预编译>执行),

了解javascript引擎的执行机理,将有助于在写js代码过程中的思路总结

首先科普下javascript中的两种声明方式,var和function,前者声明的是变量,后者声明的是方法

在预编译中,javascript对这两种声明做出了两种处理方案

<script>

  var a = "1";    //声明变量a

function b(){    //声明方法b
  alert();
  }
  var c = function(){    //声明变量c
    alert();
  }

</script>


以上代码块中,a、c为变量赋值,b为函数声明,当执行以上的代码时,

首先会进入预编译阶段,

对与变量赋值a、c会在内存中开辟一块内存空间并指向变量名,且赋值为undefined

对于函数声明,则同样会进行开辟内存空间,但此时会直接将函数体进行处理,即用函数声明方式,则在预编译阶段便已完成了函数的创建工作

预编译阶段:(PS:不管代码中声明变量和声明函数的顺序如何,在预编译阶段会先声明变量,再声明函数,但是变量初始化过程发生在执行期,而不是预编译期

<script>
    var a = undefined;
    var c = undefined;
    
    var b = function(){
      alert();
    }
</script>


执行阶段:

<script>
    a = "1";
    c = function(){
      alert();
    }
</script>


整体执行步骤:

<script>
    var a = undefined;
    var c = undefined;
    
    var b = function(){
      alert();
    }
    a = "1";
    c = function(){
      alert();
    }
</script>


题目:

<script>
var a =1;
function test(){
alert(a); //a为undefined! 这个a并不是全局变量,这是因为在function scope里已经声明了(函数体倒数第4行)一个重名的局部变量,
//所以全局变量a被覆盖了,这说明了Javascript在执行前会对整个脚本文件的定义部分做完整分析,所以在函数test()执行前,
//函数体中的变量a就被指向内部的局部变量.而不是指向外部的全局变量. 但这时a只有声明,还没赋值,所以输出undefined。
a=4
alert(a);  //a为4,没悬念了吧? 这里的a还是局部变量哦!
var a;     //局部变量a在这行声明
alert(a);  //a还是为4,这是因为之前已把4赋给a了
}
test();
alert(a); //a为1,这里并不在function scope内,a的值为全局变量的值
</script>


PS:相对与window环境下的变量、函数声明,每一个作用域都会对其下的变量和函数进行先声明

<script>
function Hello() {
alert("Hello");
}
Hello();
function Hello() {
alert("Hello World");
}
Hello();
</script>


我们会看到这样的结果:连续输出了两次Hello World。而非我们想象中的Hello和Hello World。

我们会看到这样的结果:连续输出了两次Hello World。而非我们想象中的Hello和Hello World。

这是因为Javascript并非完全的按顺序解释执行,而是在解释之前会对Javascript进行一次“预编译”,在预编译的过程中,会把定义式的函数优先执行,也会把所有var变量创建,默认值为undefined,以提高程序的执行效率。也就是说上面的一段代码其实被JS引擎预编译为这样的形式

<script>
function Hello() {
alert("Hello");
}
function Hello() {
alert("Hello World");
}
Hello();
Hello();
</script>


我们可以通过上面的代码很清晰地看到,其实函数也是数据,也是变量,我们也可以对“函数“进行赋值(重赋值)。

当JavaScript引擎解析脚本时,它会在预编译期对所有声明的变量和函数进行处理。

做如下处理:

1. 在执行前会进行类似“预编译”的操作:首先会创建一个当前执行环境下的活动对象,并将那些用var申明的变量设置为活动对象的属性,但是此时这些变量的赋值都是undefined,并将那些以function定义的函数也添加为活动对象的属性,而且它们的值正是函数的定义

2. 在解释执行阶段,遇到变量需要解析时,会首先从当前执行环境的活动对象中查找,如果没有找到而且该执行环境的拥有者有prototype属性时则会从prototype链中查找,否则将会按照作用域链查找。遇到var a = …这样的语句时会给相应的变量进行赋值(注意:变量的赋值是在解释执行阶段完成的,如果在这之前使用变量,它的值会是undefined)

所以,就会出现当JavaScript解释器执行下面脚本时不会报错:

alert(a);    // 返回值undefined
var a =1;
alert(a);    // 返回值1


由于变量声明是在预编译期被处理的,所以在执行期间对于所有代码来说,都是可见的。但是,你也会看到,执行上面代码,提示的值是undefined,而不是1。这是因为,变量初始化过程发生在执行期,而不是预编译期。在执行期,JavaScript解释器是按着代码先后顺序进行解析的,如果在前面代码行中没有为变量赋值,则JavaScript解释器会使用默认值undefined。由于在第二行中为变量a赋值了,所以在第三行代码中会提示变量a的值为1,而不是undefined

3.从如下结果中我们知道先是连续两次输出Hello Wrold!,最后连续两次输出test,得出这样的结果是因为javascript并非是完全按照顺序执行的,而是在执行之前先进行一个预编译,预编译 时声明式函数被提取出来,优先执行,而且相同的函数会进行覆盖,再执行赋值式函数。

<script type='text/javascript'>
test();                    //输出Hello World!
function test(){      
alert('hello');     //声明式函数
}
test();                    //输出Hello World!

var test=function(){    //赋值式函数
alert('test');
}
test();                    //输出test
function test(){      //声明式函数
alert('Hello World!');
}
test();                    //输出test
</script>


实际执行顺序:

<script type='text/javascript'>
var test=undefined;
function test(){      
alert('hello');     //声明式函数
}
function test(){      //声明式函数
alert('Hello World!');
}
test();                    //输出Hello World!

test();                    //输出Hello World!

var test=function(){    //赋值式函数
alert('test');
}
test();                    //输出test
test();                    //输出test
</script>


4.下面代码显示显示hello,再显示hello world!,这是因为javascript中的给个代码块是相互独立的,当脚本遇到第一个script标签时,则javascript 解析器会等这个代码块加载完成后,先对它进行预编译,然后再执行之,然后javascript解析器准备解析下一个代码块,由于javascript是按 块执行的,所有一个javascript调用下一个块的函数或者变量时,会出现错误

<script type='text/javascript'>
function test(){
alert('hello');                //显示hello
}
test()
</script>
<script type='text/javascript'>
function test(){
alert('hello world!');        //显示hello world!
}
test()
</script>


5.虽然javascript是按块执行的,但不同的块却属于相同的全局作用域,不同的块的变量和函数式可以相互使用的,也就是某个块可以使用前面块的变量和函数,却不可以使用它之后的块的变量和函数

<script type='text/javascript'>
alert(name);                    //显示undefined
var name='Jude';
function test(){
alert('hello');
}
fun();                            //不能调用下一个块的函数
</script>
<script type='text/javascript'>
alert(name);           //可以调用上一个块的变量,显示Jude
test();                //可以调用上一个块的函数,显示hello
function fun(){
alert('fun');
}
</script>


  综上所述,javascript在执行时的步骤是:

    1、先读入第一段代码块

    2、对代码块进行语法分析,如果出现语法错误,直接执行第5步骤

    3、对var变量和function定义的函数进行“预编译处理”(赋值式函数是不会进行预编译处理的)

    4、执行代码块,有错则报错

    5、如果还有下一段代码块,则读入下一段代码块,重复步骤2

    6、结束

更多信息可查看博客:http://www.cnblogs.com/RunForLove/p/4629510.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: