ECMAScript 新特性
前言:
ECMAScript是JavaScript的语言本身,通常看作JavaScript的标准化规范,但其实JavaScript是ECMAScript的扩展语言,ECMAScript只提供了最基本的语法,下图分别为浏览器中的JavaScript与Node.js中的JavaScript组成:
所以,JavaScript的语言本身指的就是ECMAScript。 ECMAScript从2015年开始实现每年一个版本的更新,并以年份命名,而ES2015也就是我们俗称的ES6,在经历了五年的停滞后,在ES2015的版本中增加了许多的新特性,而之后的版本都是比较小变化的版本,因此我们俗称的ES6当中通常不仅包含ES2015的内容,通常也包含之后版本迭代的内容,统称为ES6。下图为ECMAScript的版本迭代:
因为ES6内容比较多,以下只是对重要的内容进行简单介绍,有需要的小伙伴可以参考最下方官方链接。
1.let与块级作用域:
ES2015之前只有全局作用域与函数作用域,而在ES2015中增加了块级作用域。而let声明的变量只可以在所在的作用域内访问:
- let有自己作用域,而var的作用域为全局
if (true) {
let foo = 'zce'
console.log(foo)
}
console.log(foo) //输出undefined,let 声明的成员只会在所声明的块中生效
- let声明的变量不会进行变量提升,必须先声明后才可使用
console.log(foo) //zce
var foo = 'zce'
console.log(foo) //报错 修复了变量声明提升现象
let foo = 'zce'
2.const声明恒量:
- 恒量声明过后不允许重新赋值
- 恒量要求声明同时赋值
- 恒量只是要求内层指向不允许被修改,对于数据成员的修改是没有问题的
3.数组与对象的解构:
运用解构的方式,方便于我们对数组及对象内的成员进行获取并操作,简化代码,以下为具体示例
数组的解构:
定义一个数组:
const arr = [100, 200, 300]
方式一:原先方式
const foo = arr[0]
const bar = arr[1]
const baz = arr[2]
console.log(foo, bar, baz)//输出为:100 200 300
方式二:解构方式获取
const [foo, bar, baz] = arr
console.log(foo, bar, baz)//输出为:100 200 300
方式三:获取指定位置成员
const [, , baz] = arr
console.log(baz)//输出为:300
方式四:展开运算符获取剩余全部成员,需放置最后
const [foo, ...rest] = arr
console.log(rest)//输出为:200 300
方式五:提取未定义成员
const [foo, bar, baz, more] = arr
console.log(more)//输出为:undefined
方式六:给提取成员设置默认值
const [foo, bar, baz = 123, more = 'default value'] = arr
console.log(bar, more)//输出为:200 default value
对象的解构:解构console对象
const { log } = console
log('foo')//输出为:foo
log('bar')//输出为:bar
附:展开运算符…的函数参数运用:
function foo (...args) {
console.log(args)
}
foo(1, 2, 3, 4) //[ 1, 2, 3, 4 ]
3.模板字符串:
- 字符串使用反引号包裹``
- 字符串允许换行
- 可以通过 ${} 插入表达式,表达式的执行结果将会输出到对应位置(比拼接符 + 更简洁更清晰)
const name = 'tom'
const msg = `hey, ${name} --- ${1 + 2} ---- ${Math.random()}`
console.log(msg) //输出为:hey, tom --- 3 ---- 0.4402420599967165
4.字符串的扩展方法:
const message = 'Error: foo is not defined.'
console.log(message.startsWith('Error'))//字符串开始位置
console.log(message.endsWith('.'))//字符串结束位置
console.log(message.includes('foo'))//字符串内部位置
//以上输出均为true
console.log(message.includes('false'))//输出为:false
5.箭头函数:
- 语法:
function inc (number) {
return number + 1
}
// 最简方式
const inc = n => n + 1
// 完整参数列表,函数体多条语句,返回值仍需 return
const inc = (n, m) => {
console.log('inc invoked')
return n + 1
}
- 箭头函数与 this:箭头函数不会改变 this 指向,箭头函数中的this指向它的执行上下文环境中的this,也就是它的上层作用域的this指向谁,箭头函数中的this就指向谁。
const person = {
name: 'tom',
sayHi: function(){
console.log(this.name)//Tom
},
sayHiAsync: function () {
console.log(this.name);//Tom
setTimeout(() => {
console.log(this.name)//Tom
}, 100)
}
}
person.sayHi()
person.sayHiAsync()
6.对象字面量增强写法:
- 属性名与变量名相同,可以省略:及之后的内容
- 方法可以省略 : function,this指向调用对象
- 通过 [] 让表达式的结果作为属性名
const bar = '345'
const obj = {
foo: 123,
// bar: bar
// 属性名与变量名相同,可以省略 : bar
bar,
// method1: function () {
// console.log('method')
// }
// 方法可以省略 : function
method () {
console.log('method')
// 这种方法就是普通的函数,同样影响 this 指向。
console.log(this)
},
// Math.random(): 123 // 不允许
// 通过 [] 让表达式的结果作为属性名
[Math.random()]: 123,
[bar]: 123
}
输出结果:
{
foo: 123,
bar: '345',
method1: [Function: method],
'0.22030889320758518': 123
}
method
{
foo: 123,
bar: '345',
method1: [Function: method],
'0.22030889320758518': 123
}
7.对象的扩展方法:
- Object.assign 将多个源对象中的属性复制到一个目标对象中,依次从前往后覆盖:
const source1 = {
a: 123,
b: 123
}
const source2 = {
b: 789,
d: 789
}
const target = {
a: 456,
c: 456
}
//source1 现覆盖 target ,source2再覆盖
const result = Object.assign(target, source1, source2)
console.log(target)//{ a: 123, c: 456, b: 789, d: 789 }
console.log(result === target)//true
- Object.is 判断两个值是否相等,相当于‘===’的判断,但同时支持NaN全等于NaN的成立
console.log(
// 0 == false // => true
// 0 === false // => false
// +0 === -0 // => true
// NaN === NaN // => false
// Object.is(+0, -0) // => false
// Object.is(NaN, NaN) // => true
)
- Proxy 代理对象:
const person = {
name: 'Tom',
age: 20
}
const personProxy = new Proxy(person, {
// 监视属性读取
get (target, property) {
console.log(target, property)
},
// 监视属性设置
set (target, property, value) {
target[property] = value
console.log(target, property, value)
}
})
personProxy.age = 100
personProxy.gender = true
console.log(personProxy.name)
8.class关键词:
- 类的具体实现
//构造函数
function Person (name) {
this.name = name
}
//原型实现方法
Person.prototype.say = function () {
console.log(`hi, my name is ${this.name}`)
}
//class声明类型:等同于上面的操作
class Person {
//构造函数
constructor (name) {
this.name = name
}
//实例对象
say () {
console.log(`hi, my name is ${this.name}`)
}
static create (name) {
return new Person(name)
}
}
const p = new Person('tom')
//调用静态方法
const tom = Person.create('tom')
p.say()
- 类的继承
//被继承的我们通常称为父类
class Person {
constructor (name) {
this.name = name
}
say () {
console.log(`hi, my name is ${this.name}`)
}
}
//继承父类的我们通常称为子类
class Student extends Person {
constructor (name, number) {
super(name) // 父类构造函数
this.number = number
}
hello () {
super.say() // 调用父类成员
console.log(`my school number is ${this.number}`)
}
}
const s = new Student('jack', '100')
s.hello()//输出内容:hi, my name is jack my school number is 100
//调用子类的方法,因为子类调用了父类成员,所以父类成员say方法也得到了实现
9.Set与Map数据结构:
- Set数据结构与数组非常类似,可以看做是一个集合,集合中的值是唯一的,不允许重复
const s = new Set()
s.add(1).add(2).add(3).add(4).add(2)
//Set数据结构的值是唯一的,所以会将重复的值删除
console.log(s) //Set(4) { 1, 2, 3, 4 }
//Set数据结构是可遍历的
s.forEach(i => console.log(i))//1 2 3 4
//Set数据结构获取长度的方法
console.log(s.size)// 4
//Set数据结构判断是否含有某个值
console.log(s.has(100))// false
//Set数据结构删除某个值
console.log(s.delete(3))//true
console.log(s)//Set(3) { 1, 2, 4 }
//将所有值清除
s.clear()
console.log(s)//Set(0) {}
// 应用场景:数组去重
const arr = [1, 2, 1, 3, 4, 1]
const result = [...new Set(arr)]
console.log(result)//[ 1, 2, 3, 4 ]
- Map 数据结构与对象非常类似,但是对象中的健只能是字符串,而Map支持任意类型的健
const m = new Map()
const tom = { name: 'tom' }
m.set(tom, 90)
console.log(m) // Map(1) { { name: 'tom' } => 90 }
console.log(m.get(tom)) // 90
//与set一样同样有以下方法
// m.has()
// m.delete()
// m.clear()
m.forEach((value, key) => {
console.log(value, key)
})
// 附:Map还有一个版本为,弱引用版本 WeakMap
// 差异就是 Map 中会对所使用到的数据产生引用
// 即便这个数据在外面被消耗,但是由于 Map 引用了这个数据,所以依然不会回收
// 而 WeakMap 的特点就是不会产生引用
// 一旦数据销毁,就可以被回收,所以不会产生内存泄漏问题
10.Symbol一种全新的原始数据类型:
- Symbol代表独一无二的值,两个 Symbol 永远不会相等,可以使用Symbol定义对象的健,避免不同模块中健的重复
// 使用 Symbol 为对象添加用不重复的键
const obj = {}
obj[Symbol()] = '123'
obj[Symbol()] = '456'
console.log(obj) //{ [Symbol()]: '123', [Symbol()]: '456' }
// 也可以在计算属性名中使用
const obj = {
[Symbol()]: 123
}
console.log(obj) // { [Symbol()]: 123 }
- 创建对象内部私有成员,由于外部不能创建一摸一样的Symbol,所以外部是访问不到的
onst name = Symbol()
const person = {
[name]: 'zce',
say () {
console.log(this[name])
}
}
// 只对外暴露 person
- 利用Symbol设置对象的自定义标签
const obj = {
[Symbol.toStringTag]: 'XObject'
}
console.log(obj.toString()) // [object XObject]
- 获取Symbol定义的对象的健获取的唯一方式:Object.getOwnPropertySymbols(obj)
const obj = {
[Symbol('foo')]: 'symbol value',
foo: 'normal value'
}
// console.log(obj);
// for (var key in obj) {
// console.log(key)
// }
// console.log(Object.keys(obj))
// console.log(JSON.stringify(obj))
// 以上方式均无法获取
console.log(Object.getOwnPropertySymbols(obj)) //[Symbol(foo) ]
11.for…of 循环,可以遍历任何一种数据类型:
- for…of 循环可以替代 数组对象的 forEach 方法,forEach 无法跳出循环,必须使用 some 或者 every 方法,而for…of 可以使用break随时终止循环;同样可以遍历Set与Map数据结构;
const arr = [100, 200, 300, 400]
// 基础语法
for (const item of arr) {
console.log(item)
if (item > 100) {
break
}
}
12.iterable可迭代接口
- 实现iterable接口是for…of的前提
- 在数据结构的__proto__原型对象下,会有一个Symbol(Symbol.iterator)方法,该方法返回一个数组对象中有一个next方法,next方法返回一个对象{value:’xxx‘,done:false},value为我们当前便利的对象的值,done表示是否可继续,不断调用next()方法,即可实现遍历
//代码实现:
const set = new Set(['foo', 'bar', 'baz'])
const iterator = set[Symbol.iterator]()
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
//输出内容
// { value: 'foo', done: false }
// { value: 'bar', done: false }
// { value: 'baz', done: false }
// { value: undefined, done: true }
// { value: undefined, done: true }
13.Generator生成器函数:避免异步编程中回调嵌套过深的问题,提供更好的异步编程解决方案
- 基本应用:生成器函数会自动帮我们返回一个生成器对象,调用函数的next方法才会让这个函数体开始执行,一旦遇到yield关键字就会暂停下来,并且yiled的值会作为next的value返回,继续调用next则会从暂停位置继续执行,直到完全结束,done的值变为true,称之为惰性执行
unction * foo () {
console.log('1111')
yield 100
console.log('2222')
yield 200
console.log('3333')
yield 300
}
const generator = foo()
console.log(generator.next()) // 第一次调用,函数体开始执行,遇到第一个 yield 暂停
//1111
//{ value: 100, done: false }
console.log(generator.next()) // 第二次调用,从暂停位置继续,直到遇到下一个 yield 再次暂停
//2222
//{ value: 200, done: false }
console.log(generator.next()) // 。。。
console.log(generator.next()) // 第四次调用,已经没有需要执行的内容了,所以直接得到 undefined
//{ value: undefined, done: true }
14.Promise一种更优的异步编程解决方案:解决了传统异步编程中回调函数嵌套过深的问题,内容较多可查看关于Promise的具体内容。
** 注:本文全手打书写,无复制粘贴,如果有错误或异议的地方,欢迎小伙伴们留言指出,感谢阅读!**
祝愿各位程序猿小伙伴,前程似锦,一路向北!
本文参考:拉钩教育
供读者参考链接:
网页:http://www.ecma-international.org/ecma-262/6.0/
PDF:http://www.ecma-international.org/ecma-262/6.0/ECMA-262.pdf