ES新特性

简介

一、ES2015

1、语言与平台关系

JavaScript Web:ECMAScript、BOM、DOM
JavaScript Node.js: ECMAScript、Node APIs:fs net etc等

2、JS与ES的关系

通常 认为ESMAScript 是JS规范,实际上JS是ES的扩展语言 本身就ES,ES只提供了基本语法
09 ES 5
11 ES 5.1
15 ES 2015 ES6
图示
在这里插入图片描述

一、let

1、let 声明的成员只会在所声明的块中生效
2、let 应用场景:循环绑定事件,事件处理函数中获取正确索引
3、for 循环会产生两层作用域 所以可以解决上述问题
4、let 修复了变量声明提升现象

二、const

1、声明时必须要设置初始值
2、声明的成员不能被修改:声明过后不能重新去指向一个新的内存地址,而属性是可以修改的。如常量被赋值一个新的对象则会报错
推荐:不用var 主用const 配合使用let

三、数组的解构

const arr = [100, 200, 300]
1、按照位置分配 const [foo, bar, baz] = arr
2、逗号分隔 const [, , baz] = arr 通过结构获取数组指定位置的值
3、…只能放在解构的最后一个位置上使用 const [foo, …rest] = arr
4、如若大于则是undefined,如: const [foo, bar, baz, more] = arr
5、=设置默认值 const [foo, bar, baz = 123, more = ‘default value’] = arr

四、对象的解构 类似数组解构

const obj = { name: ‘zce’, age: 18 }
1、通过属性名去匹配 const { name } = obj
2、同名会报错,可重命名、设置默认值 如:

// const name = 'tom'
const { name: objName = 'jack' } = obj

3、简化代码

const { log } = console
log('foo')
log('bar')
log('123')

五、 `` 模板字符串

1、支持换行
2、插值表达式${}
3、表达式的执行结果将会输出到对应位置

const msg = `hey, ${name} --- ${1 + 2} ---- ${Math.random()}`

六、 带标签的模板字符串

1、标签:一个特殊的函数,使用标签就是调用这个函数
2、使用必须要先定义函数
3、打印模版字符串中内容分割过后的结果,因为模板字符串中可能会有嵌入的表达式.strings是按照表达式分割过后那些静态的内容。
4、函数的返回值就是带标签的模板字符串的返回值

const name = 'tom'
const gender = false
function myTagFunc (strings, name, gender) {
  console.log(strings) // ["hey, ", " is a ", "."]
  const sex = gender ? 'man' : 'woman'
  return strings[0] + name + strings[1] + sex + strings[2] // 返回正常值 'hey, tom is a woman.'
}
const result = myTagFunc`hey, ${name} is a ${gender}.`

七、字符串扩展方法

1、includes() // 判断是否包含
2、startsWith() // 判断是否以…开头
3、endsWith() // 判断是否以…结尾

八、参数默认值

1、通过=号设置默认值:function foo (enable = true) {}
2、如果没有传递实参或者传递的是undefined时才会被使用

九、剩余参数…

1、形参前加上…
2、这个形参会以数组的形式接收从当前位置往后所有的实参
3、只能出现在形参的最后一位,且只能使用一次

function foo (first, ...args) {
  console.log(args)
}

十、展开数组…

1、把数组中的成员按照次序传入到参数列表中

const arr = ['foo', 'bar', 'baz']
console.log.apply(console, arr) //  foo bar baz
console.log(...arr)  //  foo bar baz

十一、箭头函数

1、()=>{return }
2、()=>
3、不会改变this的指向 始终指向当前作用域中的this

十二、对象字面量增强

1、属性名与变量名相同,可以省略 : bar
2、方法可以省略 : function 直接 const obj = {a()}
3、上述方法中this,谁调用指向谁
4、通过 [] 让表达式的结果作为属性名

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)

十三、对象扩展方法

1、Object.assign 将多个源对象中的属性复制到一个目标对象中,源对象会覆盖目标对象中相同的属性

  const result = Object.assign(target, source1, source2)

2、assign的返回值就是目标对象 true
3、可以应用在设置默认值等场景
4、Object.is

+0===-0 // true
NaN===NaN // false
Object.is(+0,-0) // false
Object.is(NaN,NaN) // true

十四、Proxy 代理对象

1、监视对象的属性读写 ES5 Object.defineProperty VUE3以前就是利用这个方法完成双向数据绑定
2、new 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 vs Object.defineProperty(目标对象,单个属性,{get(){},set(value){}})

1、Proxy 可以监视读写以外的操作 如:delete操作、对对象中方法的调用等 deleteProperty

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) // {name: "zce"}

2、Proxy 可以很方便的监视数组操作 set

// const list = []
// const listProxy = new Proxy(list, {
//   set (target, property, value) {
//     console.log('set', property, value)  // property 就是索引
//     target[property] = value
//     return true // 表示设置成功
//   }
// })

// listProxy.push(100)
// listProxy.push(100)

3、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)

在这里插入图片描述

十六、Reflect

全新的内置对象,提供了一套统一的对象操作API
1、属于一个静态类,不能通过new的方式去构建一个实例对象
2、只能调用这个静态类的静态方法如Reflect.get()和Math.randomNum类似
3、Reflect内部封装了一系列对对象的底层操作 14-1个。
4、成员方法是Proxy处理对象的默认实现

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(Reflect.has(obj, 'name'))  // false
console.log(Reflect.deleteProperty(obj, 'age')) // true
console.log(Reflect.ownKeys(obj)) //  ["foo", "bar"]

十七、Promise

解决异步编程中回调函数嵌套过深的问题
链接: https://blog.csdn.net/weixin_38550182/article/details/112218464.

十八、class类

class Person {
// 当前我们这个类型的构造函数
  constructor (name) {
    this.name = name // 通过this访问当前类型的实例对象
  }
// 为这个类型定义实例方法
  say () {
    console.log(`hi, my name is ${this.name}`)
  }
}
const p = new Person('tom')
p.say()
// function Person (name) {
//   this.name = name
// }
// Person.prototype.say = function () {
//   console.log(`hi, my name is ${this.name}`)
// }

十九、static 静态成员

1、类型中的方法:实例方法vs静态方法
实例方法:需要通过这个类型构造的实例对象去调用
静态方法:则是通过类型本身去调用
以前实现静态方法是直接在构造函数对象上去挂载方法来去实现,因为在js中函数也是对象,也可以去添加一些方法成员
ES 2015中新增添加静态成员的 static 关键词
注意:静态方法是挂载到类型上的,所以在静态方法内部 this 就不会指向某一个实例对象 而是当前的类型
clas

s Person {
  constructor (name) {
    this.name = name
  }
  say () {
    console.log(`hi, my name is ${this.name}`)
  }
  static create (name) {
    return new Person(name)
  }
}
const tom = Person.create('tom')
tom.say()

二十、类的继承 extends

class Person

 {
  constructor (name) {
    this.name = name
  }
  say () {
    console.log(`hi, my name is ${this.name}`) // hi, my name is jack
  }
}
// Student中会拥有Person中所有的成员了
class Student extends Person {
// Student的构造函数
  constructor (name, number) {
    super(name) // 父类构造函数 / 这边好像是必须要有 不然报错
    this.number = number
  }
  hello () {
    super.say() // 调用父类成员
    console.log(`my school number is ${this.number}`)  // my school number is 100
  }
}
const s = new Student('jack', '100')
s.hello()

二十一、Set 数据结构

1、全新的数据结构,可以理解成集合,与数组类似
2、Set内部成员不允许重复
3、它是一个类型,通过这个类型构造出来的实例就可以用来存放不同的数据 add

const s = new Set()
s.add(1).add(2).add(3).add(4).add(2)   // Set(4) {1, 2, 3, 4}
// s.forEach(i => console.log(i)) // 1 2 3 4
// for (let i of s) {
//   console.log(i)
// }

4、size // console.log(s.size) 类似length
5、has // console.log(s.has(100)) 返回true/false
6、delete // console.log(s.delete(3)) 返回true/false
7、clear // s.clear() 清除集合中的全部内容
8、// 应用场景:数组去重

const arr = [1, 2, 1, 3, 4, 1]

// const result = Array.from(new Set(arr)) // 转成数组
const result = […new Set(arr)] // 转成数组

console.log(result)

二十二、Map 数据结构

用来映射两个任意类型数据之前的对应关系
1、类似对象 本质上都是键值对集合,对象中的键只能是字符串类型,存放复杂结构的数据时会有些问题

const obj = {}
obj[true] = 'value'
obj[123] = 'value'
obj[{ a: 1 }] = 'value'
console.log(obj) // {123: "value", true: "value", [object Object]: "value"}
console.log(Object.keys(obj)) //  ["123", "true", "[object Object]"]
console.log(obj['[object Object]']) // value

2、用法
const m = new Map()
const tom = { name: ‘tom’ }
m.set(tom, 90)
console.log(m) // Map(1) {{…} => 90}
console.log(m.get(tom)) // get方法获取

3、
// m.has()
// m.delete()
// m.clear()
4、

m.forEach((value, key) => {
  console.log(value, key)
})

二十三、Symbol

一种全新的原始数据类型 最主要的作用就是为对象添加独一无二的属性名
1、用来表示一个独一无二的值
2、通过Symbol()函数来创建一个Symbol类型的数据
3、

Symbol()===Symbol() // false 
Symbol===Symbol // true

4、

console.log(typeof Symbol()) // “symbol”

5、

console.log(Symbol()) // Symbol()

6、允许传入字符串,作为这个值的描述文本

Symbol('foo') //Symbol(foo)

7、可作为对象属性,为对象添加用不重复的键
8、也可以在计算属性名中使用

const obj = {
  [Symbol()]: 123
}
console.log(obj) // Symbol(): 123}

9、Symbol 模拟实现私有成员
用Symbol来创建私有成员的属性名了,在这个对象的内部可以通过创建属性的Symbol()拿到对应的属性成员
外部文件没有办法创建相同的Symbol所以就不能直接访问到这个成员,只能够调用这个对象中普通名称的成员,这样就实现了所谓的私有成员

// a.js 
const name = Symbol()
const person = {
  [name]: 'zce',
  say () {
    console.log(this[name])
  }
}
// 只对外暴露 person
// b.js 
// 由于无法创建出一样的 Symbol 值,
// 所以无法直接访问到 person 中的「私有」成员
// person[Symbol()]
person.say()

10、未来还会出现BigInt 的原始数据类型 用来存放更长的数字

二十四、Symbol补充

1、唯一性,每次都是全新的值
// console.log(
// // Symbol() === Symbol()
// Symbol(‘foo’) === Symbol(‘foo’)
// )
2、Symbol 全局注册表 全局复用一个相同的Symbol值 使用全局变量或者使用Symbol的静态方法for 来实现

const s1 = Symbol.for('foo')
const s2 = Symbol.for('foo')
console.log(s1 === s2) // true
console.log(
  Symbol.for(true) === Symbol.for('true') // true
 )

3、这个for方法维护的是字符串和Symbol的对应关系 会强制转换成字符串

// console.log(
//   Symbol.for(true) === Symbol.for('true') // true
// )

4、提供了很多内置的Symbol常量, 用来作为内部方法的标识,这些标识符可以让自定义对象实现一些JS当中内置的接口

console.log(Symbol.iterator) // Symbol(Symbol.iterator)
console.log(Symbol.hasInstance) // Symbol(Symbol.hasInstance)
console.log({}.toString()) // [object Object] 这样的结果叫作对象的toString标签
// const obj = {
//   [Symbol.toStringTag]: 'XObject'   // 为对象实现迭代器时会经常用到
// }
// console.log(obj.toString())  // [object XObject]

5、for in和Object.keys方法拿不到属性名。 Object.keys只能获取字符串属性名
6、JSON.stringify(obj) 也会忽略掉Symbol属性
7、console.log(Object.getOwnPropertySymbols(obj)) 只能获取到Symbol属性名

二十五、for…of 循环

全新的遍历方式
for遍历数组、for…in 遍历键值对、ES5函数式的遍历方法 数组的forEach
1、作为遍历所有数据结构的统一方式
2、// for…of 循环可以替代 数组对象的 forEach 方法

const arr = [100, 200, 300, 400]
// for (const item of arr) {
//   console.log(item)
// }
// arr.forEach(item => {
//   console.log(item)
// })

3、可以使用break,forEach不能跳出循环
// for (const item of arr) {
// console.log(item)
// if (item > 100) {
// break
// }
// }
4、
// forEach 无法跳出循环,必须使用 some 或者 every 方法
// arr.forEach() // 不能跳出循环
// arr.some() // 返回true 可终止遍历
// arr.every() // 返回false 可中止遍历
5、伪数组也可遍历 如arguments对象、节点列表
6、遍历 Set 与遍历数组相同
// const s = new Set([‘foo’, ‘bar’])
// for (const item of s) {
// console.log(item) // foo bar
// }
7、遍历 Map 可以配合数组解构语法,直接获取键值

const m = new Map()
m.set('foo', '123')
m.set('bar', '345')

for (const key of m) {
  console.log(key) // ['foo','123'] ['bar', '345']
}

// 配合解构

for (const [key, value] of m) {
   console.log(key, value) // foo 123  ...
}

8、 普通对象不能被直接 for…of 遍历
// const obj = { foo: 123, bar: 456 }
// for (const item of obj) {
// console.log(item)
// }

二十六、可迭代Iterable接口

为了给各种各样的数据结构提供统一的遍历方式
1、ES中能够表示有结构的数据类型越来越多 数组 对象 set map
2、如果不理解接口可理解成一种规格标准,例如在ECMAScript任意一种数据类型都有toString方法,就是因为它们都实现了统一的规则标准,编程语言中更专业说法是它们都实现了统一的接口
3、如下这个三个可以被for…of遍历的对象中都有如下的方法iterator。iterator接口约定的是对象中必须要挂在一个叫做iterator的方法。这就是for…of循环的工作原理在这里插入图片描述在这里插入图片描述

3、实现Iterable接口就是for…of的前提
// 迭代器(Iterator)
1、所有可以被for…of循环遍历的数据类型都必须要实现iterator接口,也就是内部必须要挂在iterator方法。这个方法需要返回带有next方法的对象,不断调用这个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())

也可以用while实现

while (true) {
  const current = iterator.next()
  if (current.done) {
    break // 迭代已经结束了,没必要继续了
  }
  console.log(current.value)
}

二十七、实现可迭代Iterable接口

1、为什么for…of循环可以作为遍历所有数据结构的统一方式了,因为它内部就是区调用被遍历对象的itarable方法,得到一个迭代器,从而去遍历内部所有的数据,这也就是iterable接口所约定的内容,只要对象实现了iterable接口,就可以实现用for…of遍历我们自己的对象。

// 实现可迭代接口(Iterable):约定内部必须要有一个用于返回迭代器的iterator的方法
// const obj = {
//   [Symbol.iterator]: function () {
  // iterator方法返回的这个对象,实现了迭代器接口iterator,约定内部必须要有一个用于迭代的next方法
//     return {
//       next: function () {
  // 实现了迭代结果的接口iteration result value done
//         return {
//           value: 'zce',
//           done: true
//         }
//       }
//     }
//   }
// }
const obj = {
  store: ['foo', 'bar', 'baz'],
  [Symbol.iterator]: function () {
    let index = 0
    const self = this
    return {
      next: function () {
        const result = {
          value: self.store[index],
          done: index >= self.store.length
        }
        index++
        return result
      }
    }
  }
}
for (const item of obj) {
  console.log('循环体', item)
}

二十八、迭代器模式

const todos = {
  life: ['吃饭', '睡觉', '打豆豆'],
  learn: ['语文', '数学', '外语'],
  work: ['喝茶'],
  // 对外提供统一遍历访问接口
  each: function (callback) {
    const all = [].concat(this.life, this.learn, this.work)
    for (const item of all) {
      callback(item)
    }
  },
  // 提供迭代器(ES2015 统一遍历访问接口)
  [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
        }
      }
    }
  }
}

// for (const item of todos.life) {
//   console.log(item)
// }
// for (const item of todos.learn) {
//   console.log(item)
// }
// for (const item of todos.work) {
//   console.log(item)
// }
todos.each(function (item) {
  console.log(item)
})
for (const item of todos) {
  console.log(item)
}

二十九、生成器Generator

1、避免异步编程中回调嵌套过深。提供更好的异步编程解决方案
2、也实现了迭代器接口

 function * foo () {
   console.log('zce')
   return 100
 }
 const result = foo()
 console.log(result) // foo {<suspended>}
 console.log(result.next()) // zce   {value:100,done:true}

3、特点:惰性执行

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

三十、生成器应用

// 案例1:发号器

function * createIdMaker () {
  let id = 1
  while (true) {
    yield id++ // 每次调用时加1.不用担心死循环
  }
}

const idMaker = createIdMaker()

console.log(idMaker.next().value)  // 1
console.log(idMaker.next().value)  // 2
console.log(idMaker.next().value)  // 3
console.log(idMaker.next().value)  // 4

// 案例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)
}

三十一、ES Modules

语言层面的模块化标准

二、ES2016

1、2016.6发布
2、

// ECMAScript 2016

// 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)) // -1

// 直接返回是否存在指定元素
// console.log(arr.includes('foo')) // true
// 能够查找 NaN
// console.log(arr.includes(NaN)) // true

// 指数运算符 

// console.log(Math.pow(2, 10))

console.log(2 ** 10)

三、ES2017

1、

// ECMAScript 2017
// const obj = {
//   foo: 'value1',
//   bar: 'value2'
// }
// Object.values -----------------------------------------------------------

// console.log(Object.values(obj))

// Object.entries ----------------------------------------------------------
// console.log(Object.entries(obj))

// for (const [key, value] of Object.entries(obj)) {
//   console.log(key, value)
// }

// console.log(new Map(Object.entries(obj)))

// 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)  // 配合 es5中新增的getter seter使用
// // console.log(descriptors)
// 将描述信息定义到新的对象当中,这样就对gettersetter属性做到一个复制
// const p2 = Object.defineProperties({}, descriptors)
// p2.firstName = 'zce'
// console.log(p2.fullName)

// 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')}`)
// }

// 在函数参数中添加尾逗号  -----------------------------------------------------

// function foo (
//   bar,
//   baz,
// ) {

// }

// const arr = [
//   100,
//   200,
//   300,
// ]
// const arr = [
//   100,
//   200,
//   300,
//   400,
// ]
// const arr = [
//   100,
//   200,
//   300
// ]
// const arr = [
//   100,
//   200,
//   300,
//   400
// ]

// Async/Await Promise语法糖

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值