JavaScript解析过程分为两个阶段:
一是:编译阶段.就是JavaScrip预解析阶段,在这个阶段JavaScript解析器将完成把JavaScript脚本代码转换到字节码;
二是:执行阶段.在编译阶段JavaScript解析器借助执行环境把字节码生成机械码,并顺序执行;
预解析:在当前的作用域中,js代码执行之前浏览器会默认把所有带var 和function的进行提前的声明或者定义;
eg:var num=1;
sum();
function sum(){console.log(num)};//1
执行过程:
1.预解析:查找var,找到num变量,按照与解析的规则,变量的预解析是只声明不赋值.
相当于把num变量放在了全局变量仓库里,但是此时的num并没有赋值,值为undefined.
查找function关键字,发现了函数素描,按照与解析的规则,声明并定义,相当于把sum函数放在全局的仓库,并且赋值了.
2.执行:代码从上到下执行,预解析过的直接略过.
*定义函数的方式有函数声明和函数表达式两种,通过对预解析机制的简单了解我们可以知道只有函数声明的方式才可以将函数执行写在函数声明之前,而使用函数表达式定义的函数则不能先调用后声明
handel1(); //1 函数声明的方式,函数整体被预解析
function handel1(){
alert(1);
}
handel2(); //Uncaught TypeError: undefined is not a function 函数表达式仅仅将变量名进行了预解析
var handel2 = function(){
alert(1);
}
*简单了解我们可以知道只有函数声明的方式才可以将函数执行写在函数声明之前,而使用函数表达式定义的函数则不能先调用后声明,
alert(a); //function a(){alert(4)}
var a = 1;
alert(a); //1
function a(){
alert(2);
}
alert(a); //1
var a = 3;
alert(a); //3
function a(){
alert(4);
}
alert(a); //3
*由于JS是自上而下的执行顺序,单个script标签内为一个完整的执行域,所以会逐个script标签进行以上两步操作
* 当JS解析器执行代码到fn()执行的时候重新执行预解析过程,如果在执行函数里面的代码的时候没有在执行环境中找到相应变量则会在其父级作用域中寻找,这种方式叫做作用域链.
eg:var a=1;
function fn(){
alert(a);//1
a=2;
}
alert(a);//1
fn();
alert(a);//2
*除了var和function会被预解析之外,函数的参数也会被执行环境预解析:
var a = 1;
function fn(a){
alert(a); //undefined
a = 2; //这里修改的是函数作用域内的局部变量a
}
fn();
alert(a); //1
作用域:
全局作用域:在代码中任何地方都能访问到的对象,
(1)最外层函数和在最外层函数外面定义的变量拥有全局作用域.
eg:var num=1;
function sum(){console.log(num)};
(2)所有未定义直接复制的变量自动声明为拥有全局作用域,
eg:function sum(){
var num1=10;//局部变量
num2=20;//全局变量
}
(3)所有window对象的属性拥有全局作用域
一般情况下,window对性的内置属性都拥有全局作用域.
局部作用域:一般只在固定的代码片段内可以访问到,最常见的就是函数内部,所以在一些地方也会看到有人把这种作用域称为是函数作用域.
eg:var num=1;//全局作用域
function sum(number){//number是局部变量
var num2=2;//局部变量
console.log(num);//num为全局变量
num=3;//全局变量
}
函数有一个内部属性[[Scope]],该内部属性包含了函数被创建的作用域中对象的集合,这个集合被称为函数的作用域链,他决定了哪些数据能被函数访问.