ECMAScript新特性
1. ECMA概述
- ECMASCript提供了最基本的语法
- 2015年每年一个版本的迭代
- javaScript@web ECMAScript操作的是DOM和BOM
- javaScript@NodeJS ECMAScript操作的是NodeJS的API
2.ES2015概述
- 解决原有语法上的一些问题或者不足
- 对原有语法进行增强
- 全新的对象,全新的方法,全新的功能
- 全新的数据类型和解构
3.let与块级作用域
-
作用域某个作用成员能够生效的范围
-
ES6以前只有两种作用域
- 全局作用域
- 函数作用域
-
let声明的成员只会在所声明的块中生效
if (true) { // var foo = 'zce' //var 定义的可以访问到 let foo = 'zce' console.log(foo) } console.log(foo)//foo is not defined
-
let 在for循环中的表现
for (var i = 0; i < 3; i++) { for (var i = 0; i < 3; i++) { console.log(i) } console.log('内层结束 i = ' + i) } /*结果 0 1 2 内层结束 i = 3*/
-
let应用场景,循环绑定事件,事件处理程正确获取索引
var elements = [{}, {}, {}] for (var i = 0; i < elements.length; i++) { elements[i].onclick = function () { console.log(i) } } elements[2].onclick()//2
-
for循环会产生两层作用域
for (let i = 0; i < 3; i++) { console.log(i) let i = 'foo' console.log(i) }
-
let修复了变量的提升现象
console.log(foo) var foo = 'zce' console.log(foo)//Cannot access 'i' before initialization let foo = 'zce'
-
const 是在let基础上多了只读(声明过后不允许修改)
const name = 'zce' //恒量声明过后不允许重新赋值 name = 'jack'//Assignment to constant variable.
-
const 声明同时必须赋值
const name name = 'zce'//Missing initializer in const declaration
-
const内层指向是不允许修改 可以修改其成员
// 恒量只是要求内层指向不允许被修改 // const obj = {} // 对于数据成员的修改是没有问题的 // obj.name = 'zce'
临时死区(temporal deada zone)
js社区
js扫描代码遇到var提升至全局,遇到let和const存放在TDZ中,访问TDZ的变量会触发错误 只有执行的时候 变量才会从TDZ中移除,然后才可以访问
4.数组,对象的解构
-
数组的解构
-
//普通模式 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)//[ 200, 300 ]
-
const [foo, bar, baz, more] = arr console.log(more)//undefined
const [foo, bar, baz = 123, more = 'default value'] = arr console.log(more)//default value
-
const path = '/foo/bar/baz' const tmp = path.split('/')//[ '', 'foo', 'bar', 'baz' ] const rootdir = tmp[1] console.log(rootdir)//foo
-
const [, rootdir] = path.split('/')
-
-
对象的解构
-
const obj = { name: 'zce', age: 18 }
-
const { name } = obj console.log(name)
const name = 'tom' const { name: objName } = obj console.log(objName)
const name = 'tom' const { name: objName = 'jack' } = obj console.log(objName)//zce
-
5.模板字符串
-
反引号包裹
const str = `hello es2015, this is a string`
-
允许换行
const str = `hello es2015, this is a \`string\``
-
可以通过 ${} 插入表达式,表达式的执行结果将会输出到对应位置
const name = 'tom' // 可以通过 ${} 插入表达式,表达式的执行结果将会输出到对应位置 const msg = `hey, ${name} --- ${1 + 2} ---- ${Math.random()}` console.log(msg)//hey, tom --- 3 ---- 0.7692434959917887
-
带标签的模板字符串
// 带标签的模板字符串 // 模板字符串的标签就是一个特殊的函数, // 使用这个标签就是调用这个函数 // const str = console.log`hello world` const name = 'tom' const gender = false function myTagFunc (strings, name, gender) { // console.log(strings, name, gender) // return '123' const sex = gender ? 'man' : 'woman' return strings[0] + name + strings[1] + sex + strings[2] } const result = myTagFunc`hey, ${name} is a ${gender}.` console.log(result)
-
模板字符串(字符串)的扩展方法
const message = 'Error: foo is not defined.' console.log( message.startsWith('Error'), message.endsWith('.'), message.includes('foo') )// true true true
6.ES2015参数默认值,剩余参数,展开数组
-
参数默认
function foo (enable = true) { console.log('foo invoked - enable: ') console.log(enable) } //默认参数一定是在形参列表的最后
-
剩余参数
function foo (first, ...args) { console.log(args) } foo(1, 2, 3, 4)
-
展开数组
const arr = ['foo', 'bar', 'baz'] console.log(...arr)
7.ES2015箭头函数
-
对比
function inc (number) { return number + 1 } // 完整参数列表,函数体多条语句,返回值仍需 return const inc = (n, m) => { console.log('inc invoked') return n + 1 } //最简方式 const inc = n => n + 1
-
常用场景——回调函数
const arr = [1, 2, 3, 4, 5, 6, 7] // arr.filter(function (item) { // return item % 2 // }) // 常用场景,回调函数 arr.filter(i => i % 2)
-
箭头函数不会改变this的指向
// 箭头函数与 this // 箭头函数不会改变 this 指向 const person = { name: 'tom', // sayHi: function () { // console.log(`hi, my name is ${this.name}`) // } sayHi: () => { console.log(`hi, my name is ${this.name}`) }, sayHiAsync: function () { // const _this = this // setTimeout(function () { // console.log(_this.name) // }, 1000) console.log(this) setTimeout(() => { // console.log(this.name) console.log(this) }, 1000) } } person.sayHiAsync()
8.ES2015对象字面量的增强
const bar = '345'
const obj = {
foo: 123,
// bar: bar
// 属性名与变量名相同,可以省略 : bar
bar,
// method1: function () {
// console.log('method111')
// }
// 方法可以省略 : function
method1 () {
console.log('method111')
// 这种方法就是普通的函数,同样影响 this 指向。
console.log(this)
},
// Math.random(): 123 // 不允许
// 通过 [] 让表达式的结果作为属性名
[bar]: 123
}
// obj[Math.random()] = 123
console.log(obj)
obj.method1()
9.object.assign object.is Proxy defineProperty
-
object.assign
-
assign方法可以将两个对象合并成一个对象,第一个参数是合并返回的对象 后面是合并的对象
const source1 = { a: 123, b: 123 } const source2 = { b: 789, d: 789 } const target = { a: 456, c: 456 } const result = Object.assign(target, source1, source2) console.log(target)//{ a: 123, c: 456, b: 789, d: 789 } console.log(result === target)//true
-
方便copy对象
function func (obj) { // obj.name = 'func obj' // console.log(obj) const funcObj = Object.assign({}, obj) funcObj.name = 'func obj' console.log(funcObj) } const obj = { name: 'global obj' } func(obj) console.log(obj)
-
-
object.is(用来判断两个值是否相同)
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: 'zce', age: 20 } const personProxy = new Proxy(person, { // 监视属性读取 get (target, property) { return property in target ? target[property] : 'default' // console.log(target, property) // return 100 }, // 监视属性设置 set (target, property, value) { if (property === 'age') { if (!Number.isInteger(value)) { throw new TypeError(`${value} is not an int`) } } target[property] = value // console.log(target, property, value) } })
Proxy对比object.defineProperty
可以监视读写以外的操作
const person = { name: 'zce', age: 20 } const personProxy = new Proxy(person, { deleteProperty (target, property) { console.log('delete', property) delete target[property] } }) delete personProxy.age console.log(person)
可以很方便的监视数组操作
const list = [] const listProxy = new Proxy(list, { set(target, property, value) { console.log('set', property, value) target[property] = value return true // 表示设置成功 } }) listProxy.push(100) //set 0 100 set length 1 listProxy.push(100) //set 1 100 set length 2
Proxy不需要入侵对象
const person = {} Object.defineProperty(person, 'name', { get () { console.log('name 被访问') return person._name }, set (value) { console.log('name 被设置') person._name = value } }) Object.defineProperty(person, 'age', { get () { console.log('age 被访问') return person._age }, set (value) { console.log('age 被设置') person._age = value } }) person.name = 'jack' console.log(person.name) // Proxy 方式更为合理 const person2 = { name: 'zce', age: 20 } const personProxy = new Proxy(person2, { get(target, property) { console.log('get', property) return target[property] }, set(target, property, value) { console.log('set', property, value) target[property] = value } }) personProxy.name = 'jack' console.log(personProxy.name) /* set 0 100 set length 1 set 1 100 set length 2 name 被设置 name 被访问 jack set name jack get name jack */
-
Object.defineProperty()
10.Reflect Promise
// Reflect 内置对象,提供拦截javaScript的方法
// const obj = {
// foo: '123',
// bar: '456'
// }
// const proxy = new Proxy(obj, {
// get (target, property) {
// console.log('watch logic~')
// return Reflect.get(target, property)
// }
// })
// console.log(proxy.foo)
const obj = {
name: 'zce',
age: 18
}
// console.log('name' in obj)
// console.log(delete obj['age'])
// console.log(Object.keys(obj))
console.log(Reflect.has(obj, 'name'))
console.log(Reflect.deleteProperty(obj, 'age'))
console.log(Reflect.ownKeys(obj))
11.ES2015 Class 类 ES2015 静态方法 继承
1.创建一个类
function Person(name){
this.name=name
}
Person.prototype.say=function(){
console.log(`hi name is${this.name}`)
}
class Person{
constructor(name){
this.name=name;
}
say(){
console.log(`hi,name is ${this.name}`)
}
}
2.静态方法
class Person{
constructor(name){
this.name=name;
}
say(){
console.log(`hi,name is ${this.name}`)
}
static create(name){
return new Person(name)
}
}
3.类的继承
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()
12.Set Map Symbol
- set
// Set 数据结构
const s = new Set()
s.add(1).add(2).add(3).add(4).add(2)
// console.log(s)
// s.forEach(i => console.log(i))
// for (let i of s) {
// console.log(i)
// }
// console.log(s.size)
// console.log(s.has(100))
// console.log(s.delete(3))
// console.log(s)
// s.clear()
// console.log(s)
// 应用场景:数组去重
const arr = [1, 2, 1, 3, 4, 1]
// const result = Array.from(new Set(arr))
const result = [...new Set(arr)]
console.log(result)
// 弱引用版本 WeakSet
// 差异就是 Set 中会对所使用到的数据产生引用
// 即便这个数据在外面被消耗,但是由于 Set 引用了这个数据,所以依然不会回收
// 而 WeakSet 的特点就是不会产生引用,
// 一旦数据销毁,就可以被回收,所以不会产生内存泄漏问题。
-
Map
// Map 数据结构 // const obj = {} // obj[true] = 'value' // obj[123] = 'value' // obj[{ a: 1 }] = 'value' // console.log(Object.keys(obj)) // console.log(obj['[object Object]']) const m = new Map() const tom = { name: 'tom' } m.set(tom, 90) console.log(m) console.log(m.get(tom)) // m.has() // m.delete() // m.clear() m.forEach((value, key) => { console.log(value, key) }) // 弱引用版本 WeakMap // 差异就是 Map 中会对所使用到的数据产生引用 // 即便这个数据在外面被消耗,但是由于 Map 引用了这个数据,所以依然不会回收 // 而 WeakMap 的特点就是不会产生引用, // 一旦数据销毁,就可以被回收,所以不会产生内存泄漏问题。
-
Symbol
// Symbol 数据类型 // 场景1:扩展对象,属性名冲突问题 // // shared.js ==================================== // const cache = {} // // a.js ========================================= // cache['a_foo'] = Math.random() // // b.js ========================================= // cache['b_foo'] = '123' // console.log(cache) // ========================================================= // const s = Symbol() // console.log(s) // console.log(typeof s) // 两个 Symbol 永远不会相等 // console.log( // Symbol() === Symbol() // ) // Symbol 描述文本 // console.log(Symbol('foo')) // console.log(Symbol('bar')) // console.log(Symbol('baz')) // 使用 Symbol 为对象添加用不重复的键 // const obj = {} // obj[Symbol()] = '123' // obj[Symbol()] = '456' // console.log(obj) // 也可以在计算属性名中使用 // const obj = { // [Symbol()]: 123 // } // console.log(obj) // ========================================================= // 案例2:Symbol 模拟实现私有成员 // a.js ====================================== const name = Symbol() const person = { [name]: 'zce', say () { console.log(this[name]) } } // 只对外暴露 person // b.js ======================================= // 由于无法创建出一样的 Symbol 值, // 所以无法直接访问到 person 中的「私有」成员 // person[Symbol()] person.say()
// Symbol 补充 // console.log( // // Symbol() === Symbol() // Symbol('foo') === Symbol('foo') // ) // Symbol 全局注册表 ---------------------------------------------------- // const s1 = Symbol.for('foo') // const s2 = Symbol.for('foo') // console.log(s1 === s2) // console.log( // Symbol.for(true) === Symbol.for('true') // ) // 内置 Symbol 常量 --------------------------------------------------- // console.log(Symbol.iterator) // console.log(Symbol.hasInstance) // const obj = { // [Symbol.toStringTag]: 'XObject' // } // console.log(obj.toString()) // Symbol 属性名获取 --------------------------------------------------- const obj = { [Symbol()]: 'symbol value', foo: 'normal value' } // for (var key in obj) { // console.log(key) // } // console.log(Object.keys(obj)) // console.log(JSON.stringify(obj)) console.log(Object.getOwnPropertySymbols(obj))
13.for。。。of
// for...of 循环
const arr = [100, 200, 300, 400]
// for (const item of arr) {
// console.log(item)
// }
// for...of 循环可以替代 数组对象的 forEach 方法
// arr.forEach(item => {
// console.log(item)
// })
for (const item of arr) {
console.log(item)
if (item > 100) {
break
}
}
// forEach 无法跳出循环,必须使用 some 或者 every 方法
// arr.forEach() // 不能跳出循环
// arr.some()
// arr.every()
// 遍历 Set 与遍历数组相同
const s = new Set(['foo', 'bar'])
for (const item of s) {
console.log(item)
}
// 遍历 Map 可以配合数组结构语法,直接获取键值
// const m = new Map()
// m.set('foo', '123')
// m.set('bar', '345')
// for (const [key, value] of m) {
// console.log(key, value)
// }
// 普通对象不能被直接 for...of 遍历
// const obj = { foo: 123, bar: 456 }
// for (const item of obj) {
// console.log(item)
// }
14.可迭代的接口
// 迭代器(Iterator)
const set = new Set(['foo', 'bar', 'baz'])
const iterator = set[Symbol.iterator]()
// console.log(iterator.next()) //{value:"foo",done:false}
// console.log(iterator.next())
// console.log(iterator.next())
// console.log(iterator.next())
// console.log(iterator.next())
while (true) {
const current = iterator.next()
if (current.done) {
break // 迭代已经结束了,没必要继续了
}
console.log(current.value)
}
//interable 接口的实现
const obj={
store:[]
[Symbol.interator]:function(){
let index=0;
const self=this
return {
next:function(){
const result=return{ //迭代结果接口
value:"",//迭代结果
done:true//表示迭代完成
}
index++
return index
}
}
}
}
for(const item of obj){
console.log('循环体')
}
15.迭代器模式
//
const todos={
life:['吃饭','睡觉','打豆豆'],
learn:['语文','数学','外语'],
work:['写代码'],
each:function(callback){
const all=[].concat(this.life,this.learn,this.work)
for(const item of all){
callback(item)
}
}
[Symbol.iterator].function(){
const all=[...this.life,...this.learn,...this.work]
let index=0
return {
next:function(){
return{
value:all[index],
done:index++>all.length
}
}
}
}
}
16.生成器 及应用
-
Generator
// Generator 函数 // function * foo () { // console.log('zce') // return 100 // } // const result = foo() // console.log(result.next()) function * foo () { console.log('1111') yield 100 console.log('2222') yield 200 console.log('3333') yield 300 } const generator = foo() console.log(generator.next()) // 第一次调用,函数体开始执行,遇到第一个 yield 暂停 console.log(generator.next()) // 第二次调用,从暂停位置继续,直到遇到下一个 yield 再次暂停 console.log(generator.next()) // 。。。 console.log(generator.next()) // 第四次调用,已经没有需要执行的内容了,所以直接得到 undefined
-
应用
function * createIdMaker () { let id = 1 while (true) { yield id++ } } const idMaker = createIdMaker() console.log(idMaker.next().value) console.log(idMaker.next().value) console.log(idMaker.next().value) console.log(idMaker.next().value) // 案例2:使用 Generator 函数实现 iterator 方法 const todos = { life: ['吃饭', '睡觉', '打豆豆'], learn: ['语文', '数学', '外语'], work: ['喝茶'], [Symbol.iterator]: function * () { const all = [...this.life, ...this.learn, ...this.work] for (const item of all) { yield item } } } for (const item of todos) { console.log(item) }
17.ESMoudules(语言层面的模块化开发)
18.ECMAScript2016
-
Array.prototype.includes
// Array.prototype.includes ----------------------------------- const arr = ['foo', 1, NaN, false] // 找到返回元素下标 console.log(arr.indexOf('foo')) // 找不到返回 - 1 console.log(arr.indexOf('bar')) // 无法找到数组中的 NaN console.log(arr.indexOf(NaN)) // 直接返回是否存在指定元素 console.log(arr.includes('foo')) // 能够查找 NaN console.log(arr.includes(NaN))
-
指数运算符
console.log(2 ** 10)//1024
19.ECMAScript2017
-
object.values 将对象的所有value存进一个数组
const obj = { foo: 'value1', bar: 'value2' } console.log(Object.values(obj))//[ 'value1', 'value2' ]
-
object.entries 数组的每一个属性对应的值变成一个数组 并返回所有属性
console.log(Object.entries(obj))//[ [ 'foo', 'value1' ], [ 'bar', 'value2' ] ]
-
object.getOwnPropertyDescriptors
const p1 = { firstName: 'Lei', lastName: 'Wang', get fullName() { return this.firstName + ' ' + this.lastName } } console.log(p1.fullName) // const p2 = Object.assign({}, p1) // p2.firstName = 'zce' // console.log(p2) const descriptors = Object.getOwnPropertyDescriptors(p1) console.log(descriptors)// 打印值和属性描述符 /*{ <p>“属性描述符”对象只能在Object.defineProperty或Object.defineProperties中使用 firstName: { value: 'Lei', //数据属性 writable: true, //可以写的 enumerable: true, //可以枚举的 configurable: true//可以配置的 }, lastName: { value: 'Wang', writable: true, enumerable: true, configurable: true }, fullName: { get: [Function: get fullName], set: undefined, enumerable: true, configurable: true } }*/
-
String.prototype.padStart / String.prototype.padEnd
const books = { html: 5, css: 16, javascript: 128 } // for (const [name, count] of Object.entries(books)) { // console.log(name, count) // } for (const [name, count] of Object.entries(books)) { console.log(`${name.padEnd(16, '-')}|${count.toString().padStart(3, '0')}`) }
-
函数参数尾部可以添加逗号