写在前面
本系列为在我读完阮一峰老师的《ES6标准入门》第二版之后,所做的阅读笔记的整理。
许多初学ES6, 同时和我一样初次阅读阮老师的这本书的时候, 读第一遍会越发的困惑, 因为阮老师上面说的很多的定义,方法,之前都是没有见过的, 读到后面才发现, 哦~原来是这样.
有一句话叫做”大神的世界我们不懂”, 所以我在初读第一遍《ES6标准入门》这本书的时候,也是踩了不少坑,读书的时候查阅了不少的资料.
所以,在这一系列的笔记教程中,我会从一个初学者的角度,向您讲述ES6的相关知识,在后面介绍的知识我会尽量不提前用,即使提前使用,也会同时做好标注,避免了阅读时各种查阅资料的烦恼.
系列博客将采用一个一个的样例,来说明书中的精华部分(当然,这只是我认为的),同时引导新手,快速入门ES6,并逐步将其投入到生产实践中。
同时,在阅读前也提醒您, 为了系统连贯性的学习ES6的基础知识,建议您从我的博客第一章开始阅读,当然,如果您对对应的知识已经有所了解,那么可以跳转到任意章节阅读,每一篇博客名中均有介绍该博客中涉及到的ES6的内容.
阮一峰老师的这本书是开源的,在其官方博客就可以下载到,但是我强烈建议大家去购买一本书, 一是方便自己查阅ES6中新增的众多API, 二也是表达一下对大神的敬仰.
函数的作用域
要知道,在ES6以前,JS一直都是只有函数作用域,而没有块级作用域的。在这样的情况下,我们大多会使用IIFE(自执行函数表达式)来模拟块级作用域,达到避免全局作用域污染等目的。
那么ES6有新定义了两种变量的定义方式,let和const,两种变量的定义方式均会产生块级作用域,也就是我们使用大括号包起来的区域,都是块级作用域。
let和const两者之间的区别就是,const定义的变量,之后是无法更改的。
1 | { |
我们在大括号外面去打印a会报错,说明let其实是有块级作用域的,当我们使用循环语句的时候,let来声明变量是一个不错的选择.
1 | var a = []; |
当我们使用let来进行声明的时候,才能得到想要的值 也就是6
原因是因为,变量i是let声明的,当前的i只在本轮循环有效,所以相当于每一次循环的i都是一个新的变量
let 不和var一样,存在变量的提升,所以我们在没有声明之前调用let声明的变量,会报错
1 | console.log(b) // 报错 |
还有一点要注意的是,我们在第一行使用var声明了b,在这里再声明,就会报错,因为let和const是不允许变量被重复声明的.
暂时性死区
只要块级作用域内存在let命令,那么它所声明的变量就绑定了这个区域,不会受外部的影响
es6明确规定,只要区块中存在let和const命令,则这个区块对这些命令声明的变量从一开始就形成封闭作用域.只要在声明之前就使用这些变量,就会报错
其实上面那个例子,已经很好地说明了暂时性死区这个概念了.
1 | var c = 3; |
不仅如此,坑爹的是有些死区,你不仔细看真的很难发现
另外补充一点,在函数中进行形式参数的预定义,也就是设置函数的参数默认值,也是ES6中新定义的函数写法,关于此的内容,会在之后在函数篇中详细介绍.
1 | function bar(x = y, y = 2) { |
当然,我们把上面的代码稍微改一下就不会报错了
1 | function bac(y = 2, x = y) { |
结合上面的介绍,还有一点,当我们在块级作用域的内部再定义块级作用域的时候,就不会报错了.
1 | function f1() { |
讲到这里,我再分享一个坑,关于函数的声明提升问题,当然, 下面这一题我没有直接在题上写答案,大家可以把解析先遮住,思考一下这题的结果是什么.既然是坑, 我觉得新手还是老老实实往上踩几脚比较好.
1 | let f = function() { |
万万没想到啊没想到, aaa函数中f()函数的执行居然会报错,而且打印f的值居然是undefined!
这其实是一个函数的作用域提升问题,预编译阶段内部函数f会跨过if判断而提升至函数aaa的作用域顶端
那么可能大家会问,我在aaa函数内部打印f函数,是undefined啊.
因为函数的声明赋值时在if判断语句之内的,所以在此之前仅有一个var f,也就是定义了一个f,但并未对其赋值,所以f打印出来就是undefined.
const和let的具体区别
我们来看下面这几行代码
1 | let f; |
其实上面的代码很好地说明了let的作用域的问题,而且需要注意的是,let的变量值,是可以随时更改的.
而const,就没有这么自由了
1 | const PI = 3.14; |
需要注意的是const定义复合类型值的时候,因为复合类型的变量不直接指向数据,而是指向数据所存储的相应内存空间
所以这个时候当我们对相应的内存空间中的值进行更改,只要内存空间不变,则都可以对值进行更改
1 | const foo = {}; |
但是呢,,如果我们更改相应的foo指向的内存空间,则就会报错
1 | foo = {} // 报错(接上面的代码实例) |
如果在声明引用类型变量后,我们不想让里面的值改变怎么办呢?
这个时候可以使用Object.freeze()方法,冻结这个对象,包括其内存空间的值
1 | const foo1 = Object.freeze({}); |
这个时候为该对象定义属性和方法,都无效了,但是不会抛出异常
最后需要注意的一点是,即使我们在全局使用let或是const定义变量,变量都不会挂载到全局的window对象上.
1 | var a = 1 |
(完)