ES6来简化代码,你都用过哪些
块级作用域
为什么需要块级作用域?
ES5 只有全局作用域和函数作用域,没有块级作用域,这导致很多场景不合理。
- 第一种场景,内层变量可能会覆盖外层变量。
1 | var tmp = new Date(); |
以上代码的原意是, if 代码块的外部使用外层的 tmp 变量,内部使用内层的 tmp 变量。但是,函数 fn
执行后,输出结果为 undefined
,原因在于变量提升导致内层的 tmp 变量覆盖了外层的 tmp 变量。
- 第二种场景,用来计数的循环变量泄露为全局变量。
1 | var s = "hello"; |
上面的代码中,变量 i
只用来控制循环,但是循环结束后,它并没有消失,而是泄露成了全局变量。
let
实际上为 JavaScript
新增了块级作用域。
1 | function fl() { |
上面的函数有两个代码块,都声明了变量 n
,运行后输出 5
。这表示外层代码块不受内层代码块的影响。如果使用 var
定义变量 ,最后输出的值就是 10
那么我们能利用块级作用域
做什么呢?
我们先来做道面试题
1 | for (var i = 0; i < 5; i++) { |
改成 ES6
中的 let
1 | for (let i = 0; i < 5; i++) { |
看到这,相信聪明的你已经理解块级作用域的好处了 O(∩_∩)O
那么 ES5
能不能实现 块级作用域
的效果呢? 可以的,我们可以利用闭包
1 | for (var i = 0; i < 5; i++) { |
解构
解构 :是将一个数据结构分解为更小的部分的过程。ES6 中,从数组和对象中提取值,对变量进行赋值。
那么解构有什么用处呢?
- 可以大大的简化变量声明操作。
1 | // ES5 |
- 变量交换:看起来如同镜像。赋值语句的左侧的解构模式,右侧是临时创建的数组字面量。x 被赋值为数组中的 y,y 被赋值为数组中的 x。
1 | let x = 1; |
- 对象解构
1 | var obj = { x: 1, y: 2, c: 1 }; |
- 字符串解构
1 | const [a, b, c, d, e] = "hello"; |
- 函数参数解构
1 | const xueyue = { |
箭头函数
ES6
允许使用箭头 =>
定义函数
1 | var f = (v) => v; |
如果箭头函数不需要参数或需要多个参数,就使用圆括号代表参数部分。
1 | var f = () => 5; |
箭头函数可以与解构结合使用。
1 | const full = ({ first, last }) => first + " " + last; |
箭头函数使得表达更加简洁
1 | const isEven = (n) => n % 2 === 0; |
上面代码只用了两行,就定义了两个简单的工具函数。如果不用箭头函数,可能就要占用多行,而且还不如现在这样写醒目。
箭头函数使用注意点
- 函数体内的
this
对象,就是定义时所在的对象,而不是使用时所在的对象。 - 不可以当作构造函数,也就是说,不可以使用
new
命令,否则会抛出一个错误。 - 不可以使用
arguments
对象,该对象在函数体内不存在。如果要用,可以用rest
参数代替。 - 不可以使用
yield
命令,因此箭头函数不能用作Generator
函数。
上面四点中,第一点尤其值得注意。this
对象的指向是可变的,但是在箭头函数中,它是固定的。
1 | // ES6 |
上面代码中,转换后的 ES5
版本清楚地说明了,箭头函数里面根本没有自己的 this
,而是引用外层的 this
。
模板字符串
模板字符串( template string )是增强版的字符串 ,用反引号
(``)
标识 。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。
1 | const { log } = console; |
剩余参数 / 展开语法
ES6 引入了 rest 参数(形式为...变量名
),用于获取函数的多余参数,这样就不需要使用 arguments
对象了。rest
参数搭配的变量是一个数组,该变量将多余的参数放入其中。
1 | function sortNumbers() { |
比较上面的两种写法可以发现, rest
参数的写法更自然也更简洁。
扩展运算符( spread
)是三个点(…) 如同 rest
参数的逆运算 将一个数组转为用逗号分隔的参数序列
1 | console.log(...[1, 2, 3]); |
下面是扩展运算符取代 apply
方法的一个实际例子 应用 Math.max
方法简化求出数组中的最大元素。
1 | // ESS 的写法 |
扩展运算符提供了数组合并的新写法。
1 | // ESS |
对象的扩展运算符(…)用于取出参数对象的所有可遍历属性,拷贝到当前对象之中。
1 | let z = { a: 3, b: "bb" }; |
特别注意: ...
扩展对象,只能做到当对象属性是 基本数据类型
才是 深拷贝
,如果是 引用数据类型
,那就是浅拷贝
。
1 | let z = { a: 3, b: "bb", c: { name: "ccc" } }; |
对象字面量简写语法
1 | const name = "雪月"; |
使用 vue
的同学是不是感到很熟悉
1 | new Vue({ |
数组实例的 includes()
Array.prototype.includes 方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的 includes 方法类似。ES2016 引入了该方法。
1 | [1, 2, 3].includes(2); // true |
没有该方法之前,我们通常使用数组的 indexOf 方法,检查是否包含某个值。
1 | // ES5 |
indexOf
方法有两个缺点,一是不够语义化,它的含义是找到参数值的第一个出现位置,所以要去比较是否不等于-1,表达起来不够直观。二是,它内部使用严格相等运算符(===)进行判断,这会导致对 NaN
的误判。
1 | [NaN].indexOf(NaN); |
includes
使用的是不一样的判断算法,就没有这个问题
1 | [NaN].includes(NaN); |
Async/await 异步语法
ES2017
标准引入了 async
函数,使得异步操作变得更加方便。
async
函数是什么?一句话,它就是 Generator
函数的语法糖。
1 | async function getTitle(url) { |
上面代码中,函数 getTitle
内部有三个操作:抓取网页
、取出文本
、匹配页面标题
。只有这三个操作全部完成,才会执行 then
方法里面的 console.log
结束(意犹未尽)
文章介绍了 ES6
常用的一些语法以及使用场景; 但是 ES6
内容远不止于此,感兴趣的同学可以去 阮一峰老师的
ES6 入门教程[1] 一书中查看详细内容。如果您认可这本书,也可以去正版渠道购买书籍。这样可以使出版社不因出版开源书籍而亏钱,进而鼓励更多的作者开源自己的书籍。
后记(列举 API)
还有很多 ES6
实用的 API
我就简单提及一下,朋友们看看平时是否有用到
1 | [1, 4, -5, 10].find((n) => n < 0); |