什么是作用域链
在讲作用域链之前,先说明一下作用域
作用域分为全局作用域和函数作用域两种
- 全局作用域:变量在函数外定义,即为全局变量,如果变量在函数内没有声明(没有使用 var 关键字),该变量为全局变量。 网页中所有脚本和函数均可使用。
2.函数作用域:函数参数只在函数内起作用,是局部变量。
作用域有‘父子’关系,‘父子’关系的确定就看函数是在哪个作用域下创建的。如下,f1作用域下创建了f2函数,那么“f1作用域”就是“f2作用域”的‘爸爸’。
作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突,结果如下图。
变量取值:到创建 这个变量 的函数的作用域中取值。
一般情况下,变量取值到 创建 这个变量 的函数的作用域中取值。但是如果在当前作用域中没有查到值,就会向‘父级’作用域去查,直到查到全局作用域,这么一个查找过程形成的链条就叫做作用域链。
举个例子
大致效果就是如上图。
预编译
什么是预编译
首先我们要了解,js的运行三部曲。
1.语法分析(引擎通篇进行检查看有没有语法错误)
2.预编译
3.解释执行
那么要搞清楚预编译我们得先搞清楚,函数声明和变量赋值的区别。
举个例子
function jimu(){}
这个叫函数声明
var q = function(){}
这个叫变量赋值,函数在js中也是一种数据,匿名函数作为变量赋值给定义的变量。这种形式的写法,在编译阶段也会做处理,但是!只会给变量q分配一个内存空间,初始化为undefined了,具体值的初始化是在程序执行阶段。
我们来分析如下代码
function jimu(){
document.write('积木')
}
jimu();
function jimu(){
document.write('积木2')
}
jimu();
结果会是什么呢
很显然,第二个函数把第一个函数覆盖了。
var heng = function (){document.write('恒星')};
heng();
var heng = function (){document.write('恒星2')};
heng();
分析上面代码,结果是什么呢
分析代码,首先判断两个都属于是变量赋值,而且两个变量名一样,所以在编译阶段只会分配一个内存空间存放变量heng的内容,当代码执行时候按照顺序执行和赋值,会先后弹出恒星 和恒星2。
function haha(){
document.write('haha');
}
haha();
var haha = function(){
document.write('haha2');
}
haha();
分析如上代码,预编译后
var haha // undefined
function haha() {
document.write('haha');
}
haha() // 'haha'
xiaoyu = function() {
document.write('haha2');
}
haha(); // 'haha2'
总结可以得出,函数声明和变量声明会在预编译阶段被’提升‘并且变量的提升是被最优先的提升的,也就是说如果一个函数声明一个和一个变量同名了比如例子三中的那么变量名会被优先提升到最高 var haha // 不赋值 为undefined 然后提升函数声明 function haha() {document.write(‘haha’);} // 此时因为函数声明在后所以它覆盖了haha变量给它复制为一个函数。当js引擎做完所有的这些提升的工作后js才会按照代码顺序来执行。
另外需要注意的是 js不是全文编译完成再执行,而是块编译,即一个script块中预编译然后执行,再按顺序预编译下一个script块再执行 但是此时上一个script快中的数据都是可用的了,而下一个块中的函数和变量则是不可用的。