本文已参与「新人创作礼」活动,一起开启掘金创作之路。
Tips:如果你看完了整篇文章,务必看一看本文的拓展内容,知识点很多,不要错过,码字不易,点个赞鼓励一下
Tips:本文有所更新,之前五个知识点太长了,可能很多人看不下去,手机上看也很不方便,所以分为上中下三篇,可以去本人主页观看后续,谢谢大家支持!
作用域、作用域链、变量提升、预编译和闭包
拿上你专属的保温杯,一包瓜子,今天带你拿捏作用域、作用域链、预编译和闭包,让你以后遇见它不会像嗑瓜子一样上火。
作用域
作用域简单来说就是指,一段代码中所用到的的变量、函数和对象等的生效范围。
1. 全局作用域
全局作用域,是可以被整个程序访问到的作用域
var c = 'ccc'
function F() {
console.log(c)
}
F()
上面这段代码我们拿去执行,打印结果如下:
会发现,在函数F里面,并没有定义变量c,可是,在调用函数F的时候,他仍然打印出了c的值,这是因为内部作用域是可以访问到外部作用域的(原因会在后文作用域链中提到),而全局作用域就是整个程序最外层的作用域,那么函数F就是在全局作用域里面的内部作用域,也正因为全局作用域是最外层的作用域,所以,定义在全局作用域下的变量可以被整个程序访问到。
如果把第一行代码var关键字去掉呢
会发现结果还是一样,这是因为c='ccc'写在了全局作用域,就算没有var,let,const的声明,也会被挂在window对象上,等价于window.c='ccc'。
2. 函数作用域
函数作用域,就是定义在函数里的变量的作用范围在这个函数里。
function F() {
var c = 'ccc'
}
console.log(c)
将c定义在函数里,然后在全局下打印这个c,结果如下:
结果是报错了,报错内容是c没有被定义,这是因为,c是定义在函数里的,全局作用域是函数F的外部作用域,外部作用域是无法访问到内部作用域的(原因会在后文作用域链中提到),所以这里报了错。
3. 块级作用域
块级作用域,简单来说,{}(花括号)包裹的就是一个块级,花括号里面可以访问外面,外面无法访问里面,常见的if,for循环,while循环,let作用域等
function foo(){
var a = 1
let b = 2
{
let b = 3
var c = 4
let d = 5
console.log(a);//1
console.log(b);//3
}
console.log(b)//2
console.log(c)//4
console.log(d)//error 因为代码块执行后就销毁,故d被销毁了,不存在,所以是error,如果存在却未定义叫做undefined
}
foo()
上述例子有五个打印,结果分别是1,3,2,4,报错,这里涉及到一个花括号包裹的代码块,是个块级作用域,我们来分析一下:
-
第一个打印a,结果是1,是因为花括号是写在foo函数里面的,内部作用域可以访问外部作用域,因此,a能够顺利打印,结果为1,
-
紧接着花括号里面打印了b,b在花括号的外面和里面都有定义,那么从里面找到外面(具体会在后文作用域链中详细解释),先找花括号里面是否有b,如果没有就往foo里面找,一层层向外,那么这里的结果是有b,因此打印的是3
-
然后在花括号外面打印了b,因为,花括号包裹的是块级作用域,且花括号里面的b是关键词let声明的,所以根据外部作用域不能访问到内部作用域,是访问不到花括号里的b的,因此只能访问花括号外的b,故打印的是2
-
然后是打印c,c虽然是花括号里的,但是他是关键词var声明的,var不会有块级作用域的效果,所以c可以被访问到,打印是4
-
最后是d,d也是let声明的,且因为代码块执行后就销毁,故d被销毁了,不存在,所以访问不到,可是画括号的外部也没有定义d,故报未定义d的错。
变量提升和预编译
1. 变量提升
变量提升javaScript 代码在执行过程中,JavaScript引擎会把
变量声明部分和函数声明部分提升到代码的最前面的“行为”,根据提升的顺序,如果变量名相同,那么后者覆盖前者,且函数声明提升会在变量声明提升之后,当使用let,const等关键字时,是不会进行变量提升的。
首先我们了解一下代码是怎么运行的
- 在执行过程中,若使用未声明的变量,js执行会报错
- 在一个变量定义之前使用它,不会报错,但是该变量的值为undefined,而不是定义的值
- 在一个函数定义之前使用它,是不会报错,且函数能正确执行
其次是要分清函数声明和函数表达式(因为函数声明是会进行变量提升的,但是函数表达式不会):
-
函数声明:function 函数名(){}
-
函数表达式: var 函数名=function(){}
最后要分清变量的声明和赋值,一般我们写代码是这样写的,var a = 'aaa',但这其实是两部分,var a是变量声明,a = 'aaa'是赋值。
下面举一个变量提升的简单例子,看一下这段代码的打印结果:
console.log(a)
var a = 123
foo()
function foo (){
console.log(a)
}
var a;
function foo (){
console.log(a)
}
console.log(a)
var a = 123
foo()
这样一来,执行结果就相符了
这里给大家检测一下,函数声明会被提升而函数表达式是不会提升的,下面的这两张图,第一张是函数声明,打印的结果是123,而第二张是函数表达式,他的打印结果是报错了,这就说明了函数声明会被提升而函数表达式是不会提升的,所以,分清楚函数声明和函数表达式很重要。