ES6,全称ECMAScript 6,是ECMA委员会在 2015年6月正式发布的新ECMAScript标准,所以又称ECMAScript 2015。ES6是一次重大的版本升级,它提供了许多新的语法和编程特性以提高 JavaScript 的开发效率和体验。
下面将开始浏览ES6的常用特性:
let and const
let和const命令可以用来声明块级作用域的变量。块级作用域作用于代码块,代码块就指一对花括号中的任何东西。
let声明的变量可重复赋值,const声明时就要赋值,不可重复赋值。
// 熟悉的例子:
for (var i = 1; i < 5; i++) {
setTimeout(function () {
console.log(i)
}, 1000)
}
因为var是函数作用域的,延时输出的肯定都是5,当用let声明i时,上面代码块就能正常工作了。
块级作用域优势
- 作用域更加清晰
- 作用域更容易控制
- 防止变量重复声明
第一点我们看下面这个例子:
var tmp = new Date();
function f() {
console.log(tmp);
if (false) {
var tmp = 'hello world';
}
}
函数执行后会输出undefined,因为var自动提升了。正常来讲,我们希望的是输出时间。if代码块中使用let后,变量就属于if代码块,不影响外层作用域。
第二点作用域更容易控制是指我们创建局部变量时,可以直接使用花括号包裹起来,不用再像以前一样写个立即执行函数。
第三点就更容易理解了,let变量是不可以重复声明的。
箭头函数
箭头函数=>通常是用来简写匿名函数。具体写法会根据函数参数个数和是否隐式返回而改变。
const max = arr => Math.max(...arr)
const sum = (arr) => { return arr.reduce((a, b) => (a + b), 0)}
讲到箭头函数,肯定要提到lexical this这个概念。
lexical this是指箭头函数里面的this绑定定义时所在的作用域,而不是指向运行时所在的作用域。同时this值不会被.call和.apply等方法修改
const obj = {
a: 1,
funcA () {
setTimeout(() => console.log(this.a), 1000)
},
funcB: () => {
console.log(this.a)
}
}
obj.funcA() // 1
obj.funcA.call(null) // undefined
obj.funcB() // undefined
obj.funcB.call(obj) //undefined
funcA中有个setTimeout函数,传入一个匿名的箭头函数, 函数里面的this值是引用funcA的this值,所以执行obj.funcA时,this就指向了obj;obj.funcA.call(null)时就指向window。
funcB方法用箭头函数来声明,所以this值就指向外层的window
默认参数
默认参数就是在定义函数时,可以为函数参数指定一个默认值。
这边就提两个知识点:
- 默认参数是可以接受表达式的
- 默认参数的判定是
undefined
// 表达式
function bar( x = 2, y = x + 4, z = foo(x)) {
console.log([ x, y, z ]);
}
bar(3, undefined, null)
解构
解构语法允许我们快速地从数组和对象中提取值。
let [a, b, c] = [1, 2, 3]
let { foo } = { foo: 'aaa', bar: 'bbb'}
技巧:
// 交换值
[a, b] = [b, a]
// 配合默认参数
function bar({ a=1, b=300 }) {}
function foo ({ a=1, b=2 } = {}) {}
foo和bar函数的不同点就是foo为解构的对象还提供了一个默认值,这样foo可以不用传任何参数调用foo(),而bar不行。
常用场景:
// 函数返回值解构
let {x, y} = getCoords()
// 配合正则使用
function getName (name) {
let magic = /^(\w+)\s(\w+)$/
return magic.exec(name)
}
let parts = getName ('Michael Jackson')
let [, firstName, lastName] = parts
剩余参数语法和扩展运算符
// 哪个是扩展运算符?
function foo(...args) {
console.log(...args)
}
之前还以为...就是指扩展运算符,只是可以“正着用”和“反着用”。其实另外一个用法是称为剩余参数。
技巧:
// 数组合并
let one = ['a', 'b', 'c']
let two = ['d', 'e', 'f']
[...one, ...two]
// 反转字符串
[...str].reverse().join('')
常用场景:
// 调用方法可以不用apply
Math.max(...[14, 3, 77])
// 不确定参数个数情况下,获得剩余参数
function sum (multiplier, base, ...numbers){}
对象字面量扩展语法
这个语法就比较清晰,一共有三点:
- 属性名简写
- 方法简写
- 动态属性名
// 属性名简写
const foo = 1
const bar = 2
const obj = {foo, bar}
// 方法简写
const obj = {
bar () {}
}
// 动态属性名
const prefix = "pre-"
const obj2= {
[prefix + "test"]: "dynamic"
}
模板字符串
这个也是比较清晰,就两点:
- 定义多行字符串
- 在字符串中嵌入变量、表达式
// 多行字符串
// ES5
let text = [ 'foo', 'bar', 'baz'].join('\n')
// ES6
text = ( `foo
bar
baz`)
// 在字符串中嵌入变量、表达式
let host = 'ponyfoo.com'
let text = `this blog lives at ${host}`
let result = `2 * 3 = ${2 * 3}`
模板字符串还有一种用法,叫标签模板。用的比较少,就不提了。
Set 和 Map
Set技巧和常用场景:
//去重
const arr1 = [1, 2, 3]
const arr2 = [2, 3, 4]
Array.from(new Set([...arr1, ...arr2]]))
// DOM增量更新
var set = new Set(divs)
otherdivs.forEach(div => set.add(div))
Map常用场景:
- 当用户提供的key值可能是__proto__, toString的时候。
- 当你需要用其他数据类型做key值的时候,像DOM元素。
Promise
讲Promise前,先提下传统的处理异步的两种方式:
// callback
fetch('foo', (err, res) => { if (err) { // handle error } // handle response })
// events
fetch('foo') .on('error', err => {
// handle error
}) .on('data', res => { // handle response })
Promise原理:初始pending状态,resolve()后状态成fulfilled,reject()后状态成rejected
推荐使用:
var p = fetch('foo')
p.then(res => {
// handle response
}).then(res => {
// handle response
}).catch(error => {
// handle error
}).finally(() => {···});
一般then中只处理成功的情况,失败的情况统一在后面catch处理,别忘了还有finally来做一些统一处理。
Class
关于Class的三个点:
- 在语法上更加贴合面向对象的写法
- 实现继承更加易读,易理解,对初学者更加友好
- 本质还是语法糖,使用prototype
想到之前学ES5的类和继承,各种实现方式,头都大了。现在有class的话可以快速上手。
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return `(${this.x}, $(this.y))`
}
}
这边提两个问题你们看看:
- new 操作都做了什么?
- toString方法是在实例还是原型上?
第一个问题可以从我们为什么使用new来解释:
- 执行构造函数,这个显而易见
- 创建一个对象并返回,一般我们使用new操作符,肯定是要获取实例对象的。
- this值绑定到对象,我们写类的时候,都会使用this来绑定值
- 原型处理,类的特性就有继承,所有肯定有处理原型方面的工作
第二个问题的话因为class本质还是使用prototype的,所有方法是会挂在原型上的。
其他新方法和语法
Object.assign() // 对象合并和浅复制
String.repeat() // 字符串重复
String.includes() // 判断是否包含某个字符串
String.startsWith(), String.endsWith() // 以某个字符串开头或结尾
Math.sign() // 判断数值的正负,返回1或-1
Array.fill() // 数组填充
for...of // 遍历有迭代器的数据:数组、字符串...
总结
除了上面介绍的点,还有一个很重要的部分,就是JS的模块化。这边就不介绍了,看看相关材料就可以很快掌握基础用法,提一个点:ES6模块化是引用传递。
ES6的新特性大家应该都有在使用,本文主要是快速回顾下常用的一些特性和特性的技巧和常用场景。当然文中介绍的并不是非常完整,如果你们有更好的技巧和使用场景,欢迎分享:)