一、我们先来看下ES5的作用域:
1、全局作用域
一个script标签就是一个全局作用域(也是一个宏任务),如果一个页面有多个script标签则会从上到下执行。
2、函数作用域(局部作用域)
一个函数内就是局部作用域。
二、JavaScript的预解析执行每遇到一个作用域就会进行两步骤:
1、(找出一些关键字初始赋值,代码从上到下查找)如:var、函数声明
如果找到的是var的变量,则统一先把他们存为未定义(undefined);
如果找到的是函数声明,则存为不变的整个函数块 (函数参数的话其实也是一个var变量)
如果遇到变量和函数同名,函数声明会被提升至作用于最上面。
// 函数
function a(y){
console.log(y+2);
}
// 变量
var a ;
console.log(a); // ƒ a(y){ console.log(y+2); }
如果遇到多个同名的函数,那么就会考虑顺序问题,后者覆盖前者。
console.log(a); // ƒ a(y){ console.log(y+3); }
function a(y){
console.log(y+2);
}
function a(y){
console.log(y+3);
}
a(1); // 4
预解析第一步:
a = undefined;
所有使用var声明的变量,在正式运行代码之前都提前赋值为未定义(undefined),变量提升。
function fn1() { alert(2); }
所有函数声明,在正式运行代码之前都是整个函数块,整体提升。
2、再逐行解读代码运行
1)、如果遇到的是表达式(+、-、*、/、%、++、–)、赋值(=)等这些能够改变变量的值的,就把相应的变量值修改掉。
2)、如果遇到函数的调用,则又要进行预解析的两个步骤!(因为又遇到了函数作用域)
此时的预解析是在对应函数内部进行预解析的。
i 、(找出一些关键字初始赋值,在函数体内从上到下查找)如:函数参数、var、函数声明、
在函数的参数中如果形参没有显式使用var声明参数,那么默认也是使用了var。函数的参数就是本函数内部变量。
ii、再逐行解读运行
(这里和外层的逐行解读代码是一样的。)
如果在函数内部使用了没有使用var声明的变量(当前作用域内没有定义的变量即自由变量
),那么内部函数就会往函数外去寻找这个变量(所谓的由内向外查找)。
在逐行解读代码的时候(如果遇到函数的定义(也就是函数声明)就直接忽略了,因为又不是表达式,又不是函数调用的)
三、例子解析
上面就是讲解了JavaScript语言预解析和执行的流程了,那么我们来看些例子来理解实践一下。
例1:(先思考下为什么这样输出,结合上面的解析)
console.log(a); // ƒ a(){ console.log("函数2") }
var a = 1;
console.log(a); // 1
function a(){ console.log("函数1") }
console.log(a); // 1
var a = 2;
console.log(a); // 2
function a(){ console.log("函数2") }
console.log(a); // 2
解析:
1、(找出一些关键字初始赋值,代码从上到下查找)如:var、函数、函数参数
找到第2行的 var a,此时a = undefined;
遇到第4行的函数声明a (pk同名函数胜),此时a = function a(){ console.log(“函数1”) };
遇到第6行的var a (pk同名函数胜),此时a = function a(){ console.log(“函数1”) };
遇到第8行的函数声明a (pk后来者同名函数胜),此时a = function a(){ console.log(“函数2”) };
2、逐行解读代码
(在这步时遇到函数的声明就直接忽略了,因为又不是表达式,又不是函数调用的)
读到第1行console.log(a); ,此时打印出 f a(){ console.log(“函数2”) }; (f代表function)
读到第2行var a = 1; ,把a重新赋值为1;
读到第3行console.log(a); ,此时打印出 1;
读到第4行为函数的声明,直接忽略;
读到第5行console.log(a); ,此时打印出 1;
读到第6行var a = 2; ,把a重新赋值为2;
读到第7行console.log(a); ,此时打印出 2;
读到第8行为函数的声明,直接忽略;
读到第9行console.log(a); ,此时打印出 2;
从上面分两部份分析,就很清晰了。更多复杂的例子,就按照这两大步骤去分析就容易理解了。
例2:自由变量
var a = 100;
function fn(){
var b = 200;
// 变量a在当前函数作用域没有定义,即“自由变量”
console.log(a); // 100
console.log(b); // 200
}
fn();
自由变量(当前作用域内没有定义的变量即自由变量
)在当前作用域内找不到时,就会依次顺着作用域链往父辈作用域找。
函数的父级作用域就看函数是在哪个作用域内定义的。