声明变量的6种方式

245 阅读7分钟

先说明下,我说的6种变量声明方式都是基于es6的环境下运行的结果。我18年入行,老的项目确实是接触的比较少,所以老的js环境了解不多。

几乎所有编程语言最基本的功能之一,就是能够储存变量当中的值,并且能在之后对这个值进行访问或修改。事实上,正是这种储存和访问变量的值的能力将状态带给了程序。若没有了状态这个概念,程序虽然也能够执行一些简单的任务,但它会受到高度限制,做不到非常有趣。

这是我之前看的一本书上第一章最开头的话,描述了变量对于一个编程语言的重要性,学习一个语言,需要先学习如何声明变量。

js的变量声明,我常用的基本上就是6种方法,如果有其他的声明方式还希望在评论区补充。

var

var是最经典的js变量声明方法了。var后面接变量名就可以声明一个函数作用域有效的变量。变量可以重复声明,变量值可修改。而且还有一个我不理解的逻辑,就是var声明的变量会把变量提升到函数的顶端,但是只声明,不赋值,等待代码执行到变量声明的位置的时候才会赋值。

image.png

以这个函数为例,在var a = 1运行之前,打印的a就已经是undefined了,证明这个a已经被声明了,但是此时变量只是被声明,未被赋值,等执行完var a = 1之后打印出来的a才是1。这个情况也经常会有面试题,大部分我都感觉挺讨厌的,毕竟日常谁写这种代码,直接弄死就好了。

image.png

这个只是我临时想起来的,就先将就着用了。有的面试官会问你fn函数执行之后__a的值是多少。

众所周知,如果声明变量不加关键字,那么这个变量就是一个全局变量。像是下图中所示。

image.png

但是之前的例子里面__a没有声明,因为两点

1: var是函数作用域,for循环或者if的判断语句之类的这种非函数的大括号是无法限制var声明的变量的。这也是另一道经典的for循环体打印和预期不同的原因。

image.png

可能计划中是要打印0,1,2,3,4的,但是打印出5个5,就是因为作用域的原因,这面试题都说烂了,我就不多赘述了。

2:var会将变量声明提升至函数的顶部。

这两个原因导致这个函数其实是等价于下图的函数的。

image.png

这里需要注意一点,就是if的判断语句无论执行与否,里面声明的变量都会提到函数的顶部,所以尽量不要再if判断里面声明变量,起码别用var声明。

function

function和var差不多是同期出现的变量声明方法,区别在于function后面可以接匿名函数,但是这个匿名函数如果无法直接执行或者为一个变量赋值的话也是会报错的。

image.png

使用function声明的变量,和var一样,都会将作用域提升到函数顶部,但是function会直接将变量赋值,而不是想var一样,只声明,不赋值。

image.png

所以function可以先调用,后声明,虽然比较方便,但是也有可能出bug,具体使用就见仁见智吧。

function的作用域算是比较奇怪的,作为和var一起出现的声明方式,理论上它也是函数作用域,但是实际上它是块级作用域。但是他这个块级作用域还与其他的不太一样。

image.png

根据刚才我的描述,如果function声明的函数是函数作用域,那么第一个打印出来的就应该是函数,而不是undefined。但是如果他单纯的是块级作用域,第一个打印应该报错,毕竟变量还没声明。这个在阮一峰的文章里面能找到答案,我就说一下结论,function是块级作用域,不过他会和var一样,先在函数作用域内声明变量,但不赋值,同时function会提升到块级作用域的顶部。

PS:不要在代码块中声明函数。

image.png

let和const

es6中引入的let和const,这两个我就一起说了,毕竟这两个除了一点之外都差不多。

let和const声明的变量都是块级作用域的,而且不能重复声明。let和const没有函数提升,但是有一个暂时性的死区,在函数顶部到变量声明之间,let和const声明的变量都会被放到一个暂时性死区里面,无法使用。

let声明的是变量,可修改的,const修改的常量,是不可修改的。当然如果有人说const声明的对象和数组可以修改,说的那真是一点都对,因为确实是可以修改,这就涉及到对象的一些特殊的属性,这个文章就不说了。

let声明的变量可修改,所以可以做到先声明,后赋值。但是const的值不可修改,所以直接const声明变量但是不赋值,就会报错的。

image.png

class

es6引入的class方法,用来声明一个构造函数的。这个其实可说的点还挺多的,不过这里我们只说他的作用域。

class声明的变量是块级作用域,不同于let和const,class声明的变量是可以重复声明,也可以修改的。

image.png

class声明的变量也存在暂时性死区。

image.png

这个我也想说的多点,但是确实是不知道如何说。复杂了吧,class自己就够说一篇,只说作用域的话我又只知道这些。

import

es6开始,引入了模块化的开发逻辑,import 和 export。其中import也是一个创建变量的方法。这个我只能更简单的说了,毕竟关于import的内容,声明变量其实对它而言是最简单的功能,更多的是用来模块化开发的。

import的使用方式和之前的几个都不一样,之前的是属于凭空创建的一个变量,import需要从其他的js文件里面引入里面导出的变量,具体方法有点复杂,我就不多说了,感兴趣的自行百度,说的都挺多的。我就说说作用域和变量的一些问题吧。

import和function一样,都会将变量提升到顶部,不同于function,import是无法在代码块中执行的,所以也就不用考虑import的块级作用域问题。(import()函数执行是另外的情况,这个不属于import创建变量)

import的作用域也是和之前的都不一样的,他属于模块作用域。

image.png

以图中为例,上面的模块引入的变量,即使在底部引入的也是可以打印和访问的,但是换到了另一个模块里面就无法访问了。同时import引入的变量和const的变量是一样的,无法直接的修改,但是对象和数组还是可以修改。所以打印出的内容如下图。

image.png

总结

也没什么好总结的,我常用的基本上就这几种,如果有遗漏希望评论区补充一下,先感谢了。