共计 5504 个字符,预计需要花费 14 分钟才能阅读完成。
本文首发于集体网站:let 关键字:加强版的 var 关键字
你好,明天大叔想和你唠扯唠扯 ES6 新增的关键字 —— let
。再说 let
的具体用法之前,大叔想先和你说说大叔本人对 let
的感触 —— let
其实就是加强版的 var
。为啥这么说呢?别急,且听大叔缓缓道来。
首先,let
和 var
的作用是一样一样滴,都是用来申明变量。看到这儿,你可能会有个问题啦,既然作用一样,为啥还要再搞个什么新个性进去?
想要答复这个问题,就要说到 let
和 var
的不同之处了。比方说 var
申明的全局变量会主动增加到顶级对象中作为属性,而 let
就不会。再比方说 var
容许申明晋升或者反复申明,而 let
就不容许这样做。当然了,它们之间的不同可不止这些,大叔也只是举个栗子而已。
如果你没理解过 ES6 的内容,看到这儿可能有点懵。没关系啊~ 别往心里去,因为接下来大叔就是要和你唠扯唠扯 let
的具体用法。
申明的全局变量不是顶级对象的属性
在整明确 let
和 var
第一点不同之前,大叔要先和你唠扯唠扯 var
这个关键字的一些用法。为啥?!var
你要是都整不明确的话,你还想整明确 let
,那就是一个漂亮的扯!
首先,咱们都晓得其实申明一个全局变量,是既能够应用 var
进行申明,也能够不应用 var
进行申明的。比方说像上面这段代码一样:
var a = 'a'
console.log(a)
b = 'b'
console.log(b)
下面这段代码不必大叔多扯,想必你也晓得打印的后果是个啥 —— 打印 a 和 b 嘛。别急,这才是个开始,咱不点慢慢来不是~
接下来呢,大叔要用 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
。
暂时性死区(TDZ)
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
- 第二处打印的后果是 undefined
- 第三处打印的后果是 b
对于这样的后果,大叔预计你应该会明确,毕竟都是刚唠过的内容。接下来,你得认真的看了,因为大叔要和你来唠扯无关暂时性死区的概念了~
所谓的暂时性死区,就是说应用 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
关键字所有大叔想和你唠扯的内容都唠扯完了,也心愿能对你有所帮忙。最初再说一句:我是不想成熟的大叔,为前端学习不再干燥、艰难和迷茫而致力。你感觉这样学习前端技术乏味吗?有什么感触、想法,和好的倡议能够在上面给大叔留言哦~