ES6常用特性

976 阅读4分钟

ES6,全称ECMAScript 6,是ECMA委员会在 2015年6月正式发布的新ECMAScript标准,所以又称ECMAScript 2015。ES6是一次重大的版本升级,它提供了许多新的语法和编程特性以提高 JavaScript 的开发效率和体验。

下面将开始浏览ES6的常用特性:

let and const

letconst命令可以用来声明块级作用域的变量。块级作用域作用于代码块,代码块就指一对花括号中的任何东西。

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值是引用funcAthis值,所以执行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 } = {}) {}

foobar函数的不同点就是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的新特性大家应该都有在使用,本文主要是快速回顾下常用的一些特性和特性的技巧和常用场景。当然文中介绍的并不是非常完整,如果你们有更好的技巧和使用场景,欢迎分享:)

相关材料