ECMAScript 新特性

本文详细介绍了ES2015及其后续版本的主要新特性,包括块级作用域、let和const、数组和对象解构、模板字符串、箭头函数、Proxy和Reflect、Class与继承、Set和Map、Symbol等。还提到了ES2016的includes方法和指数运算符,以及ES2017的Object.values、Object.entries和Object.getOwnPropertyDescriptors等。这些新特性极大地丰富了JavaScript的语法,提高了开发效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、ECMAScript 2015

1. ES2015共有三种作用域

  • 全局作用域
  • 函数作用域
  • 块级作用域(新增)

2、变量声明:let const 

 let const都是块级作用域,let是变量,const是常量,

 let、const、var 三者的区别:const:定义的变量不可以修改,当前块级作用域内,无法重复声明;var:1、只存在于当前作用域中,2、var命令会发生“变量提升”现象,可以在声明之前使用,值为undefined,且可以重复声明,3、全局声明的变量时,是顶层对象的属性。let:当前块级作用域内,不能重复声明,必须先声明,再调用

let 在 for 循环中的表现

for (var i = 0; i < 3; i++) {
  for (var i = 0; i < 3; i++) {
    console.log(i)
  }
  console.log('内层结束 i = ' + i)
}

for (var i = 0; i < 3; i++) {
  for (let i = 0; i < 3; i++) {
    console.log(i)
  }
  console.log('内层结束 i = ' + i)
}

 let 应用场景:循环绑定事件,事件处理函数中获取正确索引

var elements = [{}, {}, {}]
for (var i = 0; i < elements.length; i++) {
  elements[i].onclick = function () {
    console.log(i)
  }
}
elements[2].onclick()   //3

var elements = [{}, {}, {}]
for (let i = 0; i < elements.length; i++) {
  elements[i].onclick = function () {
    console.log(i)
  }
} 
elements[0].onclick()   //0

3、数组解构

方括号[]中的变量按顺序匹配数组元素

const arr = [100, 200, 300]

const foo = arr[0]
const bar = arr[1]
const baz = arr[2]
console.log(foo, bar, baz)

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

const [, , baz] = arr
console.log(baz)

const [foo, ...rest] = arr
console.log(rest)

const [foo, bar, baz, more] = arr
console.log(more)

const [foo, bar, baz = 123, more = 'default value'] = arr
console.log(bar, more)

const path = '/foo/bar/baz'
const tmp = path.split('/')
const rootdir = tmp[1]

const [a, rootdir] = path.split('/')   // ['','foo','bar','baz']
console.log(rootdir)


4、对象解构

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)

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

5、模板字符串

// 反引号包裹
const str = `hello es2015, this is a string`

// 允许换行
const str = `hello es2015,

this is a \`string\``

console.log(str)

const name = 'tom'
// 可以通过 ${} 插入表达式,表达式的执行结果将会输出到对应位置
const msg = `hey, ${name} --- ${1 + 2} ---- ${Math.random()}`
console.log(msg)

6、 模板字符串标签函数

模板字符串的标签就是一个特殊的函数,

使用这个标签就是调用这个函数

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

7、字符串的扩展方法

        includes()

        startsWith()

        endsWith()

const message = 'Error: foo is not defined.'

console.log(
  // message.startsWith('Error')
  // message.endsWith('.')
  message.includes('foo')
)

8、函数参数的默认值

// 带有默认值的参数要放在最后
function foo(enable = true) {
  console.log(enable)
}
foo(true) //true
foo(false) //false
foo() // true

9、剩余参数

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

foo(1, 2, 3, 4)

10、展开数组参数

const arr = ['foo', 'bar', 'baz']

console.log.apply(console, arr) // foo bar baz
console.log(...arr) // foo bar baz

11、箭头函数

        箭头函数不会改变this指向,this为上层作用域的this

function inc (number) {
  return number + 1
}

// 最简方式
const inc = n => n + 1

// 完整参数列表,函数体多条语句,返回值仍需 return
const inc = (n, m) => {
  console.log('inc invoked')
  return n + 1
}

console.log(inc(100))

const arr = [1, 2, 3, 4, 5, 6, 7]

arr.filter(function (item) {
  return item % 2
})

// 常用场景,回调函数
arr.filter(i => i % 2)
// 箭头函数不会改变 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()

12、 对象字面量增强

属性名与变量名相同,可以省略 : bar

对象方法可以直接写函数形式:method1(){}

使用方括号的方式计算动态属性名

const bar = 111
const obj = {
  foo: 123,
  // bar: bar,
  bar, // 同上一行效果
  // method1: function () {
  //   console.log(`method1: ${this}`)
  // },
  method2 () {
    // 直接写一个方法,同上面的冒号属性
    console.log(`method2: ${this}`)
  },
  [Math.random()]: 123, // 计算属性名

}
console.log(obj)

13、对象扩展方法

Object.assign(target, source):将多个源对象中的属性复制到一个目标对象中

// Object.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)
console.log(result === target)

// 应用场景

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)

14、代理对象:Proxy

ES5中有一个Object.defineProperty,Vue2就是通过这个实现数据双向绑定

ES6提供了Proxy,可以监视对象的读写过程,Vue3.0通过Proxy实现数据绑定

// Proxy 对比 Object.defineProperty() ===============
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)
  }
})

personProxy.age = 100

personProxy.gender = true

console.log(personProxy.name)
console.log(personProxy.xxx)


// 优势1:Proxy 可以监视读写以外的操作 --------------------------

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)

// 优势2:Proxy 可以很方便的监视数组操作 --------------------------

const list = []

const listProxy = new Proxy(list, {
  set (target, property, value) {
    console.log('set', property, value)
    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)

15、Reflect 统一的对象操作API

Reflect属于静态类(如Math),不能new,只能调用静态方法:Reflect.get()。Reflect内部封装了一系列对对象的底层操作。Reflect成员方法就是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(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))

16、 类 关键词 Class

// function Person (name) {
//   this.name = name
// }

// Person.prototype.say = function () {
//   console.log(`hi, my name is ${this.name}`)
// }

class Person {
  constructor (name) {
    this.name = name
  }

  say () {
    console.log(`hi, my name is ${this.name}`)
  }
}

const p = new Person('tom')
p.say()

静态方法, this 指向当前类型,而不是实例

class Person {
  constructor(name) {
    this.name = name
  }
  say () {
  console.log(`hi, my name is ${this.name}`)
  }
  static create(name) {
    // this 指向当前类型,而不是实例
    console.log(this) // [Function: Person]
    return new Person(name)
  }
}

const tom = Person.create('tom')
tom.say() // hi, my name is tom

继承,关键词 extends

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

17、Set 数据结构

弱引用版本 WeakSet

差异就是 Set 中会对所使用到的数据产生引用

即便这个数据在外面被消耗,但是由于 Set 引用了这个数据,所以依然不会回收

而 WeakSet 的特点就是不会产生引用,

一旦数据销毁,就可以被回收,所以不会产生内存泄漏问题。

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)

18、数据结构 Map

弱引用版本 WeakMap

差异就是 Map 中会对所使用到的数据产生引用

即便这个数据在外面被消耗,但是由于 Map 引用了这个数据,所以依然不会回收

而 WeakMap 的特点就是不会产生引用,

一旦数据销毁,就可以被回收,所以不会产生内存泄漏问题。

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

19、原始数据类型 Symbol

最主要的作用就是为对象添加独一无二的属性名

const s = Symbol()
console.log(s) // Symbol()
console.log(typeof s) // symbol

console.log(Symbol() === Symbol()) // false

console.log(Symbol('foo')) // Symbol(foo)
console.log(Symbol('bar')) // Symbol(bar)
console.log(Symbol('baz')) // Symbol(baz)

const obj = {}
obj[Symbol()] = 111
obj[Symbol()] = 2
console.log(obj) // { [Symbol()]: 111, [Symbol()]: 2 }


const name = Symbol()
const person = {
  [name]: 'jal', // 作为私有成员防止被访问
  say(){
    console.log(this[name])
  }
}
person.say()// jal
console.log(person[Symbol()]) // undefined
// console.log(person[name]) // jal

截止到ES2019一共定义了6种原始类型,和一个object类型,未来还会增加一个bigint的原始类型(stage-4阶段)标准化过后就是8种数据类型了

const s1 = Symbol.for('foo')
const s2 = Symbol.for('foo')
console.log(
  s1 === s2, // true
// Symbol.for的参数会被转化为字符串
Symbol.for(true) === Symbol.for('true'), // true
)
const obj2 = {
  // 为对象实现迭代器时会用到
  [Symbol.toStringTag]: 'XObject'
}
console.log(obj2.toString()) // [object Object] [object XObject]

const obj3 = {
  [Symbol()]: 'symbol value',
  foo: 'normal value'
}
for(var key in obj3) {
  console.log(key)
}
// foo

console.log(Object.keys(obj3)) // [ 'foo' ]
console.log(JSON.stringify(obj3)) // {"foo":"normal value"}

console.log(Object.getOwnPropertySymbols(obj3)) // [ Symbol() ]

20、 for … of 作为遍历所有数据结构的统一方式

// for ... of 循环, 可以使用break
const arr = [1, 2, 3, 4]
for (const item of arr) { // item为每个对象实例
  console.log(item)
}
// 相当于
// arr.forEach(item => {
//   console.log(item)
// })

可以使用break终止循环

// arr.forEach ,但是这个方法不能终止遍历
// 为了终止遍历,我们之前,我们曾使用
// arr.some() 返回true
// arr.every() 返回false

for(const item of arr) {
  console.log(item) 
  if(item > 1)break
}

遍历集合Set

const s = new Set(['foo', 'bar'])
for(const item of s) {
  console.log(item)
}
// foo bar

遍历集合Map

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

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

// 解构键和值
for(const [key, value] of m) {
  console.log(key,value)
}
// foo 123
// bar 34

遍历对象,报错了:TypeError: obj is not iterable

const obj = {name: 'jal', age: 22}

for(const item of obj) {
  console.log(item) // TypeError: obj is not iterable
}

21、迭代器(Iterator)

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 (true) {
  const current = iterator.next()
  if (current.done) {
    break // 迭代已经结束了,没必要继续了
  }
  console.log(current.value)
}

二、ECMAScript 2016

1、数组的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(Math.pow(2, 10))

console.log(2 ** 10)

三、ECMAScript 2017

 Object.values(obj)

获取对象所有的值数组

const obj = {
  name: 'jal',
  age: 20
}
// 对象的值组成的数组
console.log(Object.values(obj)) // [ 'jal', 20 ]

获取对象的键值数组 Object.entries(obj)

// 对象的键值数组, 可以for...of 这个对象了
console.log(Object.entries(obj)) // [ [ 'name', 'jal' ], [ 'age', 20 ] ]
for (const [key, value] of Object.entries(obj)) {
  console.log(key, value)
}
// name jal
// age 20

console.log(new Map(Object.entries(obj))) // Map(2) { 'name' => 'jal', 'age' => 20 }

获取对象的详细描述 Object.getOwnPropertyDescriptors(obj)

const p1 = {
  firstName: 'Ji',
  lastName: 'Ailing',
  get fullName() {
    return this.firstName + ' '+ this.lastName
  }
}

const p2 = Object.assign({}, p1)
p2.firstName = 'zce'
console.log(p2) // { firstName: 'zce', lastName: 'Ailing', fullName: 'Ji Ailing' }
const descriptors = Object.getOwnPropertyDescriptors(p1)
console.log(descriptors)
/*
{
  firstName: { value: 'Ji', writable: true, enumerable: true, configurable: true },
  lastName: {
    value: 'Ailing',
    writable: true,
    enumerable: true,
    configurable: true
  },
  fullName: {
    get: [Function: get fullName],
    set: undefined,
    enumerable: true,
    configurable: true
  }
}
*/

const p3 = Object.defineProperties({}, descriptors)
p3.firstName = 'zce'
console.log(p3.fullName) // zce Ailing

用指定字符串填充目标字符串的头部或者尾部,直到达到指定的长度为止 padEnd/padStart

const books = {
  html: 5,
  css: 16,
  javascript: 128
}
for(const [key, value] of Object.entries(books)) {
  console.log(key, value)
}
// html 5
// css 16
// javascript 128

for(const [key, value] of Object.entries(books)) {
  console.log(`${key.padEnd(16, '-')}|${value.toString().padStart(3, '0')}`)
}
// html------------|005
// css-------------|016
// javascript------|128

 在函数参数中添加尾逗号

function foo (
 bar, 
 baz,
) {
  
}

const arr = [
  10,
  20,
  30,
]

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值