变量提升
作用域
预编译
在了解JavaScript的预编译之前,让我们先来了解了解跟预编译相辅相成的两个小玩意!!(变量提升和作用域),变量提升又和作用域有着密切的联系,对变量提升和作用域有了很好的认识后,再来看预编译,简直跟喝水似的!!!让我们先来了解了解变量提升吧!!
集中注意力看这里看这里!!!
变量提升(Hoisting)
我们先不谈变量提升,先来看看这个例子:(上例子!!)
var a = 112;
console.log(a);
复制代码
这不是so easy!!,闭着眼睛就知道了,console.log(a)输出2。
我们把上面的代码做一点小小的变化。
a =112;
console.log(a);
var a;
复制代码
这里又会有人直接说了,这还不简单嘛,有手就行,这不就啥也输出不来嘛,直接报错呗,这还需要问。
先不做过多解释,先上运行截图:(在浏览器的控制台上运行这一段代码)
小伙伴们都直接惊呆了!!怎会如此,活久见。
大家不要着急,这里就涉及到了我们要谈的变量提升问题。
在JavaScript中,函数及变量的声明都将被提升到函数的最顶部。变量可以在使用后声明,也就是变量可以先使用再声明。
我们知到JS中声明变量的关键字有两个(var 和 let),那么它们之间有什么区别呢??
上链接:JavaScript:var与let的区别 这里我们移步到另一篇文章了解了解var与let的区别。
上面提到函数声明也有提升,这里我们再上一个例子:
fun();
function fun(){
var a = 456;
console.log(a);
}
复制代码
到这里,大家就开始谨慎了起来!!类比前面的例子,瞬间自信心爆棚!!!console.log(a)输出456;就这呀,我还以为啥呢。
你以为这就完了嘛?这只是你以为,我们再来改改上面的例子:(阴险!)
fun();
console.log(b);
var b = function fun(){
var a = 456;
console.log(a);
}
订正: 这里评论区大佬指点的是,这一段代码如果换成这样:
console.log(b);
var b = function fun(){
var a = 456;
console.log(a);
}
fun();
也是会报错的,因为函数表达式的函数名不能在外面使用。这一部分就是让我们了解函数声明和函数表达式的区别
复制代码
大家看看这里又会得出什么结果?嘿嘿!!
先上运行截图!!
果然有诈!!!
见了鬼,为什么会报错?函数声明不是会变量提升嘛?
这里的var b = function fun(){},是一个赋值操作(也叫函数表达式),将fun(){}这个函数赋值给b,fun()并没有变量提升,这就是为什么会报错的原因了。这里也就是我们要搞清楚函数声明和函数表达式的区别了。
到这里相信大家已经初步了解了变量提升
现在我们来了解了解作用域
作用域
作用域是指在程序中定义变量的区域,该位置决定了变量的生命周期(函数作用域,块级作用域用完销毁),通俗的理解,作用域就是变量与函数的可访问范围,即作用域控制着变量和函数的可见性和生命周期
作用域分为:
- 全局作用域:直接编写在script标签中的JS的代码,都在全局作用域中,全局作用域中的变量都是全局变量,在页面的任意的部分都可以访问到。
- 函数作用域:调用函数时,创建函数作用域,函数执行完毕以后,函数作用域销毁
全局作用域
先来聊聊全局作用域吧。
全局作用域那不就是定义在全局!! 小 case,那还真是说对了。
但是还是得来解释解释(奸笑)
- 全局作用域在我们页面打开时创建,在页面关闭时销毁
- 在全局作用域中有一个全局对象window,它代表的是一个浏览器窗口,它由浏览器直接创建,我们可以直接使用
第一点就不需要解释了,直接来解释第二点。还是老样子,上例子:
<script>
var a = 123;
var b = 234;
function fun(){
}
</script>
复制代码
像这种直接定义在script标签下的,经过变量提升,将这些声明全都放在了全局作用域的最前面。
var a = 123;
console.log(window.a);
复制代码
这里我们可以通过window.a访问到a;
函数作用域
了解了全局作用域后,我们来聊聊函数作用域!!
相同理解简直不要太完美,顾名思义,函数内的区域。
我们再来做点其他的理解:
- 每调用一次函数就会创建一个新的函数作用域,他们之间是相互独立的
- 在函数作用域中可以访问全局作用域的变量,在全局作用域中无法访问到函数作用域的变量
- 当在函数作用域中操作一个变量时,会先在自身作用域中寻找,如果有就直接使用如果没有则向上一级作用域中寻找,直到找到全局作用域中,如果全局作用域中依然没找到,则会报错。
相信大家这个贼好理解,咱就不做过多解释了,就不上例子了,还是来个例子叭,显的更专业一点(嘿嘿!!)
var a = 123
function fun(){
console.log(a); //123
}
fun();
复制代码
结果毫无疑问 123!!!!!!!!
再来上个例子(提示:此处有诈)
var a = 123;
function fun(){
console.log(a);
a = 456;
}
fun();
console.log(a);
复制代码
别太着急回答,好好想想(奸笑)三分钟思考
…
…
…
三分钟到了。。。。。
又上截图:
疑惑头一连串!!!!
这里有什么问题呢??来想想,仔细看fun()函数中的a有问题,有极大问题。
没错就是这个a出轨了,因为在函数中没有a变量的声明,在函数中出现类似于a = 456,未定义直接赋值,这种的的属于定义在全局作用域中的。这就解释了fun()函数外面的console.log(a)为什么是输出456了,被覆盖了。(上面var与let的区别中提到了的)
上面两个小菜吃完了,现在上主菜!!!!
预编译
预编译分为函数体内的预编译和全局作用域内的预编译
函数体内预编译四部曲:
- 创建一个AO对象(activation object)
- 找形参和变量声明,找到之后将形参和变量声明作为AO对象的属性名,值为undefined
- 将实参和形参统一(将实参赋值给形参)
- 在函数体里找函数声明,将函数名作为AO对象的属性名,值赋予函数体
有了上面两个小菜开胃,这个主菜就有意思多了。
老规矩,上花生(例子):
function fun(a) {
console.log(a); //function(){}
var a = 123;
console.log(a);//123
function a() {};
console.log(a); //123
var b = function() {}//函数表达式
console.log(b); //function(){}
function d() {}
var d = a
console.log(d); //123
}
fun(1)
复制代码
开始用四部曲分析一波:
1:创建一个
AO:{
}
复制代码
2:找形参和变量声明:均赋值为undefined
a:undefined
b:undefined
d:undefined
复制代码
3:将实参和形参统一:(将1放到a中)
a:undefined---->1
b:undefined
d:undefined
复制代码
第三步结束。
关键步骤第四步:(考虑var关键字定义的变量会产生覆盖) a的一开始由实参传入的1被第二行var a = 123覆盖,a的值变为123
a:undefined----> 1 ---->function a() {}
b:undefined
d:function d() {}
复制代码
四部走完,开始常规操作(洒洒水啦!!)
只看第四步:
第一个console.log(a)输出function a() {} 毫无压力
由上往下执行赋值操作:
第二个console.log(a)输出123;
第三个console.log(a)还是输出123;
console.log(b)输出function b(){} //因为其是将一个函数赋值给b的
console.log(d)输出123
复制代码
不多说,上运行截图:
结果完美匹配,神奇。这里就涉及到了函数声明和函数表达式的区别(上面没弄懂的这里就会踩坑)
全局下预编译三部曲
- 创建一个GO对象(Global Object)
- 寻找变量声明,将变量声明作为GO对象的属性名,值赋予为undefined
- 在全局中找函数声明,将函数名作为GO对象的属性名,值赋予函数体。
整体上跟函数体内的预编译差不多。
还是一样的配方,还是一样的味道。先上例题:
global = 100
function fn() {
console.log(global); // undefined
global = 200;
console.log(global); // 200
var global = 300;
}
fn();
var global;
复制代码
根据步骤往下走:
第一步创建一个GO对象:
GO:{
}
复制代码
第二步寻找变量声明,值赋为undefined:
GO:{
global:undefined
fn:undefined
}
复制代码
第二步结束,第三步冲:
GO:{
global:undefined 100;
fn:function(){},
}
复制代码
这就是全局下的预编译,发现没,全局下还有一个函数,所以函数还需要来按照上面的四部曲分析一波:
照旧,咱就不过多赘述了,大家按照上面的四部曲来分析一波,咱就直接贴出来了
AO: {
global: undefined---->200---->300
}
复制代码
文章到此就完毕了,瞬间腰不疼腿不酸了,人也变得自信了!!!