本文首发于我的网站: let关键字:增强版的var关键字
你好,今天大叔想和你唠扯唠扯 ES6 新增的关键字 —— let
。再说 let
的具体用法以前,大叔想先和你说说大叔本身对 let
的感觉 —— let
其实就是增强版的 var
。为啥这么说呢?别急,且听大叔慢慢道来。javascript
首先,let
和 var
的做用是同样同样滴,都是用来声明变量。看到这儿,你可能会有个问题啦,既然做用同样,为啥还要再搞个什么新特性出来?html
想要回答这个问题,就要说到 let
和 var
的不一样之处了。比方说 var
声明的全局变量会自动添加到顶级对象中做为属性,而 let
就不会。再比方说 var
容许声明提高或者重复声明,而 let
就不容许这样作。固然了,它们之间的不一样可不止这些,大叔也只是举个栗子而已。前端
若是你没了解过 ES6 的内容,看到这儿可能有点懵。不要紧啊~ 别往内心去,由于接下来大叔就是要和你唠扯唠扯 let
的具体用法。java
在整明白 let
和 var
第一点不一样以前,大叔要先和你唠扯唠扯 var
这个关键字的一些用法。为啥?!var
你要是都整不明白的话,你还想整明白 let
,那就是一个美丽的扯!安全
首先,我们都知道其实声明一个全局变量,是既可使用 var
进行声明,也能够不使用 var
进行声明的。比方说像下面这段代码同样:app
var a = 'a' console.log(a) b = 'b' console.log(b)
上面这段代码不用大叔多扯,想必你也知道打印的结果是个啥 —— 打印 a 和 b 嘛。别急,这才是个开始,咱不点慢慢来不是~ ecmascript
接下来呢,大叔要用 delete
这个运算符来作个骚操做了 —— 先用 delete
删除上面的两个变量 a
和 b
,而后呢再分别打印这两个变量的值。函数
你寻思一下这个时候应该打印的结果是啥呢?对啦!变量 a
的值会正常输出 a,但变量 b
会报错 b is not defined
。那为啥又是这样一个结果呐?学习
大叔以为你应该知道 delete
运算符的做用是用来删除对象的属性,可是 delete
是没法删除变量的。对啦!你想的没错,这就说明上面声明的 a
是变量但不是对象的属性,而是 b
是对象的属性但不是变量。网站
大叔这话说的有点绕,给你带入一个场景吧。好比上面这段代码是在一个 HTML 页面中定义的 JavaScript 代码,那 a
就是一个全局变量,b
就是向 window
对象添加了一个属性。因此,delete
运算符能够删除 b
,但不能删除 a
的缘由了。
那也就是说使用 var
关键字声明的是变量,不使用 var
关键字声明的是 window
对象的属性呗。话唠叨这儿,大叔还得来个骚操做。咱再看一段代码:
var a = 'a' console.log(window.a) var b = 'b' console.log(window.b)
这段代码若是按照上面的结论,打印的结果就应该是 undefined 和 b。可是~ 你真实运行一下这段代码,就应该知道实际上打印的结果是 a 和 b!
这咋和上面的结论不同呢?!是否是又有点懵?哈哈~ 别先急着懵逼,这个问题其实是 JavaScript 的做者 Brendan Eich 当年在设计 JavaScript 这门语言时的一个小失误:在全局做用域中声明的变量同时会被做为属性添加到顶级对象中。
可能唠扯到这儿,你会满屏的吐槽弹幕:这尼玛谁不知道?!但大叔真正想和你唠扯的就是这一点,这个小小的失误,就致使了使用 var
关键字声明的全局变量会污染全局对象的问题。
而 ES6 新增的 let
就很好滴弥补了这个问题!也就是说,使用 let
关键字声明的全局变量不会污染全局对象。不信咱能够来试试嘛~ 仍是刚才那个场景,在一个 HTML 页面中定义 JavaScript 代码,仅仅把 var
改为 let
:
let a = 'a' console.log(a) console.log(window.a)
这段代码实际的运行结果就是 a 和 undefined。事实证实 let
有效滴解决了 var
的问题,因此你知道为啥 ES6 要新增一个关键字来完成和 var
同样的事儿了吧?!
可是,但但是,可可是~ let
就这么一点点和 var
的区别吗?答案确定不是滴。我们仍是先来唠扯唠扯 var
关键字,使用 var
声明的变量是容许反复滴重复声明的,就像下面这段代码:
var a = 'a' var a = 'aa' console.log(a)
这段代码最终打印的结果是 aa,缘由就在于 var
声明的变量是容许重复声明的。可能这会儿你又会问了,这我也知道啊,有啥子问题吗?
问题确定是有滴,要是没有大叔花这么多口舌和你在这儿叨逼叨干啥啊~ 大叔仍是给你带入一个场景,比方说你定义了一个 JS 文件是须要被其余小伙伴导入使用滴,那你在这个文件里面声明的变量在人家那分分钟被从新声明了,你心里是个啥感觉?
固然了,大叔就是举个栗子,你也别太当真啦~ 总而言之,就是说我们在真实开发时对变量的命名确定是有规划的,不能随意就被从新声明使用,这样会让命名空间很乱很乱滴。
你可能有想问了,这个问题要怎么解决呢?答案其实很简单,就是使用 ES6 新增的这个 let
关键字。由于 let
关键字声明的变量是不容许被重复声明,不然会报错滴。不信你也能够看看嘛:
let a = 'a' let a = 'aa' console.log(a)
仅仅只是把 var
改为 let
,这个结果就是报错了,报错的内容是:SyntaxError: Identifier 'a' has already been declared
,大概的意思就是变量 a 已经被声明过了。
因此,你看,let
可不是仅仅那么一点点的区别呢!
这会儿你是否是又想问 let
和 var
之间还有没有其余区别啊?大叔也不藏着掖着了,干脆一口气都和你说了吧!你知道使用 var
关键字声明的变量是容许声明提早的吗?啥?不知道!没事儿,这个简单,啥叫声明提早,来看段代码:
console.log(a) var a = 'a'
你运行一下这段代码,看看打印的结果是啥?没错~ 结果就是 undefined。为啥不是报错呢?缘由就是使用 var
关键字声明的变量容许声明提早。仍是说人话吧,也就是说,上面这段代码和下面这段代码本质上是没区别的:
var a console.log(a) a = 'a'
这样婶儿写你可能就明白了为啥打印的结果是 undefined 而不是报错了吧!可是,嘿嘿~ 我们又得唠扯唠扯 let
了,由于 let
声明的变量就不容许声明提早。不信的话仍是给你看段代码先:
console.log(a) let a = 'a'
这段代码运行以后打印的结果就是报错,报错的内容是:ReferenceError: Cannot access 'c' before initialization
,大概的意思就是没法在声明变量 c
以前访问变量 c
。
let
是否是挺屌的吧?!那你想不想知道 let
声明的变量又为啥不容许声明提早呢?嘿嘿~ 这是由于使用 let
声明变量的过程当中存在一个叫作暂时性死区(Temporal dead zone,简称 TDZ)的概念。
是否是以为挺高深的?哈哈~ 其实没啥高深的,大叔就给你唠扯明白这个事儿。规矩不变,咱仍是先看段代码再说:
if (true) { console.log(a) let a; console.log(a) a = "a"; console.log(a) }
大叔想先问问你这段代码里面三处打印的结果分别是啥?你得认真的寻思寻思哈~ 这可都是大叔刚和你唠过的内容。
ReferenceError: Cannot access 'c' before initialization
对于这样的结果,大叔估计你应该会明白,毕竟都是刚唠过的内容。接下来,你得认真的看了,由于大叔要和你来唠扯有关暂时性死区的概念了~
所谓的暂时性死区,就是说使用 let
关键字声明的变量直到执行定义语句时才会被初始化。也就是说,从代码从顶部开始执行直到变量的定义语句执行,这个过程当中这个变量都是不能被访问的,而这个过程就被叫作暂时性死区。
具体到上面这段代码的话,实际上暂时性死区的开始和结束位置就像下面这段代码标注的同样婶儿:
if (true) { // 暂时性死区开始 console.log(a); // 报错,ReferenceError: Cannot access 'a' before initialization let a; // 暂时性死区结束 console.log(a); // 输出undefined a = "a"; console.log(a); // 输出a }
捞到这会儿,大叔相信你应该能够明白啥子是暂时性死区了。其实啊,一些新的概念也没啥难理解的,主要是你理解的角度和方式的问题。
typeof
运算符也再也不安全整体上来讲,let
关键字要比 var
关键字严格了许多,致使咱们开发时遇到的问题相应会减小许多。但 let
就没有任何问题了吗?答案显然不是滴,大叔一直信奉一句话:任何技术都没有最优,只有最适合。
ES6 新增的 let
关键字也是如此,就比方说刚才我们捞的暂时性死区的内容,其实就有问题。啥问题呢?你还记得 JS 里面有个运算符叫作 typeof
吧,就是用来判断原始数据类型的。这个运算符在 let
出现以前相对是比较安全的,说白了就是不容易报错。但在 let
出现以后就不必定了,比方说若是你把它用在刚才说的暂时性死区里面,它就会报错了:
if (true) { console.log(typeof c) let c; }
这段代码最终打印的结果一样是报错,报错内容一样是:ReferenceError: Cannot access 'c' before initialization
。
关于 let
关键字我们捞到这会儿,其实基本上已经唠完了。可是,但但是,可可是~ 嘿嘿~ let
还有一个最重要的特性大叔还没和你唠呢,这重量级的都得最后出场不是?!
那这个最重要的特性就是啥呢?叫作块级做用域。唠到做用域想必你应该知道在 ES5 中存在两个:全局做用域和函数做用域,但在 ES6 中又新增了一个块级做用域。
想唠明白什么是块级做用域,咱就得从为啥须要块级做用域唠起啊~ 规矩不变,仍是先看段代码:
var a = "a" function fn() { console.log(a) if (false) { var a = "b" } } fn()
你以为这段代码运行以后打印的结果应该是啥?是 a?是 b?仍是... ...?其实结果是 undefined。固然了,这个结果不可贵出,你运行一下就能看到。关键在于,为啥是这么个结果?!
由于就在于 ES5 只有全局做用域和函数做用域,而上面这段代码的结果产生的缘由就在于局部变量覆盖了全局变量。固然了,还有比这更麻烦的问题呢,比方说我们再看下面这段代码:
for (var i = 0; i < 5; i++) { console.log("循环内:" + i) } console.log("循环外:" + i)
是否是无比地熟悉吧?!不就是个 for
循环嘛!关键在哪?关键在于 for
循环结束以后,你会发现依旧能访问到变量 i
。这说明啥?说明变量 i
如今是一个全局变量。固然了,你可能会说这没啥问题,毕竟以前一直不都是这个样子的嘛。
可是,大叔要和你说的是,如今不同了啊,如今有块级做用域啦!啥是块级做用域?仍是看段代码先:
if (true) { let b = "b" } console.log(b)
这段代码运行以后打印的结果是报错,报错的内容是:SyntaxError: Lexical declaration cannot appear in a single-statement context
。
这说明啥?这就说明如今你使用 let
声明的变量在全局做用域中访问不到了,缘由就是由于使用 let
声明的变量具备块级做用域。
接下来你的问题可能就是这个块级做用域在哪呢吧?其实这个块级做用域就是在花括号({}
)里面。比方说,我们如今把上面那个 for
循环的代码用 let
改造一下再看看:
for (let i = 0; i < 5; i++) { console.log("循环内:" + i) } console.log("循环外:" + i)
改造完的这段代码运行以后的结果就是在循环结束后的打印结果是报错,报错内容大叔就不说了,由于都一个样。
整明白了啥是块级做用域,接下来大叔就得和你唠叨唠叨须要注意的事儿了。就是在使用 let
关键字声明块级做用域的变量时可必须在这对 {}
里面啊,否则一样也会报错滴。
比方说,我们常常在使用 if
语句时爱把 {}
省略,可是若是 if
语句里面是使用 let
声明变量的话就不行了。不信来看段代码吧:
if (true) let c = 'c'
这段代码的运行结果一样是报错,并且报错内容都是同样的。但是不能忘记哦~
好了,整明白啥是块级做用域了,也唠清楚须要注意的了,你是否是想问问这块级做用域有啥子用处啊?大叔都想你内心面去了,嘿嘿~
你知道匿名自调函数吧?还记得怎么写一个匿名自调函数吗?是否是这样婶儿的:
(function(){ var msg = 'this is IIFE.' console.log(msg) })()
还记得匿名自调函数的做用不?是否是就是为了定义的变量和函数不污染全局命名空间?!有了 let
,有了块级做用域,上面这段匿名自调函数就能够写成这样婶儿的:
{ let msg = 'this is IIFE.' console.log(msg) }
简化了很多吧?!
好了,整到这儿,ES6 新增的 let
关键字全部大叔想和你唠扯的内容都唠扯完了,也但愿能对你有所帮助。最后再说一句:我是不想成熟的大叔,为前端学习再也不枯燥、困难和迷茫而努力。你以为这样学习前端技术有趣吗?有什么感觉、想法,和好的建议能够在下面给大叔留言哦~