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