首页 > 其他分享 >ECMAScript 新特性

ECMAScript 新特性

时间:2022-12-30 16:56:20浏览次数:49  
标签:const name 特性 obj ECMAScript console foo log

1.ES2015官方文档

https://262.ecma-international.org/6.0/

pdf地址:https://www.ecma-international.org/wp-content/uploads/ECMA-262_6th_edition_june_2015.pdf

2.ES2015新特性分类

  • 解决原有语法上的一些问题或者不足

  • 对原有语法进行增强

  • 全新的对象、全新的方法、全新的功能

  • 全新的数据类型和数据结构

3.作用域

在ES2015之前,只存在两种作用域,全局作用域和函数作用域,在ES2015中新增了块级作用域。

if(true) {
  var foo = 'foo'
}
console.log(foo)
// 输出为'foo', 使用var 没有块级作用域

if(true) {
  let bar = 'bar'
}
// console.log(bar)
// ReferenceError: bar is not defined,使用let定义的变量只在块级作用域中有效


for(var i = 0; i < 3; i++) {
  for(var i = 0; i < 3; i++) {
    console.log(i)
  }
}
// 只会输出0, 1, 2, 相当于只走了内层的for循环

for(let i = 0; i < 3; i++) {
  for(let i = 0; i < 3; i++) {
    console.log(i)
  }
}
// 改成let后就会输出三次0, 1, 2,因为两个i只会在自己的作用域生效


// 模拟DOM元素上绑定点击事件
var elements = [{}, {}, {}]
for(var i = 0; i < elements.length; i++) {
  elements[i].onclick = function () {
    console.log(i)
  }
}
elements[1].onclick()
// 无论调用哪个元素的点击事件都会输出3,因为var是全局作用域的变量,循环执行完成后i的值都会变成3

// 之前的解决方法是使用闭包来解决
var elements = [{}, {}, {}]
for(var i = 0; i < elements.length; i++) {
  elements[i].onclick = (function (i) {
    return function (){
      console.log(i)
    }
  })(i)
}
elements[1].onclick()
// 输出为1

// 改为let声明就可以解决
var elements = [{}, {}, {}]
for(let i = 0; i < elements.length; i++) {
  elements[i].onclick = function () {
    console.log(i)
  }
}
elements[1].onclick()
// 输出为当前i的值1


for (let i = 0; i < 3; i++){
  let i = 'foo'
  console.log(i)
}
// 输出三次foo,虽然变量名称相同,但是作用域互不影响,可以看成三次if判断


console.log(zoo)
// 因为var定义的变量存在声明提升,所以不会报错,打印的是undefined
var zoo = 'zoo'

// console.log(zoo1)
// let定义的变量不存在声明提升,所以报错,打印的是ReferenceError: Cannot access 'zoo1' before initialization
let zoo1 = 'zoo1'


const baz = 'baz'
// baz = 'baz1'
// const定义的变量无法再赋值,且必须在声明的时候赋值;TypeError: Assignment to constant variable.

const obj = {}
obj.name = 'cxz'
// 这里的赋值不会报错,是因为我们没有修改obj的内存地址的指针,只是修改了对应的值

最佳实践:不用var,主用const,配合let

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 [, bar, baz] = arr
console.log(bar, baz)
// 输出:200 300

const [foo, ...rest] = arr
console.log(foo, rest)
// 输出:100 [ 200, 300 ], 使用...rest进行剩余成员收集

const [a, b, c, d = 400, e] = arr
console.log(a, b, c, d, e)
// 输出:100 200 300 400 undefined,可以赋值默认值, 如果取不到,就是undefined

// 获取字符串指定位置的值
const str = 'foo/bar/baz'
const strArr = str.split('/')
const str1 = strArr[1]
console.log(str1)

// 使用解构获取
const [, , str2] = str.split('/')
console.log(str2)

5.对象的解构

对象是没有顺序和下标的,所以必须按照key进行解构

const obj = { name: 'zxc', age: 18 }
const { name } = obj
console.log(name)
// 输出:zxc

// 如果当前作用域中已存在和key相同的变量,这时需要使用重命名
const age = 20
// const { age } = obj
// 会提示age已存在,需要使用以下写法
const { age: objAge } = obj
console.log(age, objAge)
// 输出:20 18

const { sex = 'man' } = obj
console.log(sex)
// 输出:man

// 如果需要在别名后面加默认值,直接加等号就行
const { level: objLevel = 2 } = obj
console.log(objLevel)
// 输出:2

// 也可以解构出log方法
const { log } = console
log(123)
// 输出:123

6.模板字符串

const str = `hello \'template string\'`
console.log(str)

// 支持换行
const str1 = `
    hello
    \' template string \'
`
console.log(str1)

// 支持插值表达式在字符串中嵌入变量的值
const name = 'template'
const str2 = `hello '${name} string'`
console.log(str2)

7.带标签的模板字符串

const str = console.log`tagged template`
// 输出:[ 'tagged template' ]

const name = 'tom'
const gender = true

function myTagFunc(strings, name, gender) {
  console.log(strings)
  // 输出:[ 'hey, ', ' is a ', '' ],是按照表达式分隔的静态内容
  console.log(name, gender)
  // 输出:tom true,可以得到差值表达式的返回值
  // 这种好处就是可以更方便的处理字符串中的变量,例如:
  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)
// 输出:hey, tom is a man

8.字符串的扩展方法

includes()、endsWith()、startsWidth

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

console.log(
  message.startsWith('Error'),
  message.endsWith('.'),
  message.includes('foo')
)
// 输出:true true true

9.参数默认值

function foo(enable) {
  // 之前默认值需要加判断,这种短路运算传入是false的时候,也会取默认值true
  // enable = enable || true
  // 正确的做法是判断是否是undefined
  enable = enable === undefined ? true : enable
  console.log(enable)
}

foo(false)
foo()

// 使用参数默认值的方式
function foo1(enable = true) {
  console.log(enable)
}
foo1(false)
foo1()

10.剩余参数

function foo(){
  console.log(arguments)
}
foo(1, 2, 3)
// 输出:[Arguments] { '0': 1, '1': 2, '2': 3 }

function foo1(...rest) {
  console.log(rest)
}
foo1(1, 2, 3, 4)
// 输出:[ 1, 2, 3, 4 ]

// 因为...rest是全部的参数,所以只能放在最后,且只能使用一次
function foo2(first, ...rest) {
  console.log(first, rest)
}
foo2(1, 2, 3, 4)
// 输出:1 [ 2, 3, 4 ]

11.展开数组

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

console.log(
  arr[0],
  arr[1],
  arr[2],
  arr[3]
)
// 输出:0 1 2 3

// 如果参数不确定,之前我们需要使用apply方法获取实参
console.log.apply(console, arr)
// 输出:0 1 2 3

// 使用数组展开操作符
console.log(...arr)
// 输出:0 1 2 3

12.箭头函数

function foo(num) {
  return num + 1
}
console.log(foo(100))

// 箭头函数
const foo1 = num => num + 1
console.log(foo1(100))


const arr = [1, 2, 3, 4, 5]
// 过滤奇数
const odd = arr.filter(function (num) {
  return num % 2
})
console.log(odd)

// 箭头函数
const odd1 = arr.filter(num => num % 2)
console.log(odd1)

13.箭头函数与this

const person = {
  name: 'tom',
  sayHi: function () {
    console.log(`Hi, my name is ${this.name}`)
    // 输出:Hi, my name is tom
  }
}
person.sayHi()

// 改成箭头函数的方式
const person1 = {
  name: 'tom',
  sayHi: () => {
    console.log(`Hi, my name is ${this.name}`)
    // 输出:Hi, my name is undefined,因为箭头函数没有this机制,不会改变this指向
  }
}
person1.sayHi()

const person2 = {
  name: 'tom',
  sayHi: function () {
    setTimeout(function (){
      console.log(`Hi, my name is ${this.name1}`)
    },0)
    // 输出:Hi, my name is undefined,因为setTimeout会放在全局作用域去执行,这时不存在person对象的name属性
  }
}
person2.sayHi()

// 可以通过箭头函数修改
const person3 = {
  name: 'tom',
  sayHi: function () {
    setTimeout(() =>{
      console.log(`Hi, my name is ${this.name}`)
    },0)
    // 输出:Hi, my name is undefined,因为setTimeout会放在全局作用域去执行,这时不存在person对象的name属性
  }
}
person3.sayHi()

14.对象字面量增强

const baz = '123'
const obj = {
  name: 'tom',
  // baz: baz 如果key和变量名相同可以简写为:
  baz,
  // method1: function () {
  //   console.log('method1')
  // }
  // 方法也可以省略冒号和function,简写为:
  method1() {
    console.log('method1')
  },
  // Math.random(): 'abx'
  // key必须是确定的值,或者定义完成后,通过[]的方式添加
  // ES6以后可以直接使用[]的方式定义动态属性名,如下
  [Math.random()]: 'abx'
}
// 如果key不确定,只能通过这种方式添加
// obj[Math.random()] = 'abx'

console.log(obj)
obj.method1()

15.Object.assign

将多个源对象中的属性复制到一个目标对象中,如果属性名相同,会进行覆盖。

对于Object.assign()而言,如果对象的属性值为简单类型(string,number),通过Object.assign({},srcobj);得到的新对象为深拷贝;如果属性值为对象或其他引用类型,那对于这个对象而言其实是浅拷贝的,这是Object.assign()特别需要注意的地方。

const source1 = {
  a: 123,
  b: 123,
}

const source2 = {
  b: 789,
  d: 789
}

const target = {
  a: 456,
  c: 456
}

const res = Object.assign(target, source1, source2)
console.log(res === target) // true
console.log(target)

// 浅拷贝
let obj = {
  foo: {
    bar: {
      baz: 1
    }
  }
}

let objCopy = Object.assign({}, obj)
objCopy.foo.bar.baz = 2
console.log(obj, objCopy)

// 深拷贝
let obj1 = {
  foo: '11'
}

let objCopy1 = Object.assign({}, obj)
objCopy1.foo = '22'
console.log(obj1, objCopy1)

16.Object.is

console.log(
  0 == false,
  0 === false,
  +0 === -0,
  NaN === NaN
)
// true false true false

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

17.Proxy

const person = {
  name: 'tom',
  age: 18
}

const proxyPerson = new Proxy(person, {
  get(target, property) {
    // console.log(target, property)
    // return 100
    return property in target ? target[property] : 'default'
  },
  set(target, property, value) {
    // console.log(target, property, value)
    // 数据校验
    if(property === 'age') {
      if(!Number.isInteger(value)){
        throw new TypeError(`${property} is not an int `)
      }
    }
    target[property] = value
  }
})

console.log(proxyPerson.name) // tom
console.log(proxyPerson.name1) // default

proxyPerson.gender = true
console.log(proxyPerson)
proxyPerson.age = '20' // TypeError: age is not an int

18.Proxy vs defineProperties

Proxy 可以监视到更多的对象操作

const person = {
  name: 'aa',
  age: 18
}

// 可以监听到删除属性操作
const proxyPerson = new Proxy(person, {
  deleteProperty(target, p) {
    console.log('delete', p)
    delete target[p]
  }
})

delete proxyPerson.age
console.log(person)

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)

19.Reflect

统一的对象操作API,按照后端语言的说法,Reflect属于一个静态类(不能通过new创建对象,只能调用其静态方法,像Math对象一样),Reflect 内部封装了一系列对对象的底层操作,Reflect 成员方法就是 Proxy 处理对象的默认实现,意义在于统一提供了操作对象的API。

const obj = {
  foo: '123',
  bar: '456'
}

const proxy = new Proxy(obj, {
  get(target, p) {
    console.log('watch logic~')
    return Reflect.get(target, p)
  }
})
console.log(proxy.bar)

// 示例
const person = {
  name: 'tom',
  age: 18
}

// 之前的这些操作方法和操作符后续可能废弃,统一使用Reflect的方式
// console.log(
//   'name' in person,
//   delete person['age'],
//   Object.keys(person)
// )

// Reflect 方式
console.log(
  Reflect.has(person, 'name'),
  Reflect.deleteProperty(person, 'age'),
  Reflect.ownKeys(person)
)

20.Promise

解决了传统异步编程中回调函数嵌套过深的问题,细节在《异步编程》章节中,这里不再演示

21.class类

// 之前函数式定义对象
function Person(name) {
  this.name = name
}

Person.prototype.say = function (){
  console.log(`Hi, ${this.name}`)
}

new Person('tom').say()

// 类方式定义
class PersonClass {
  constructor(name) {
    this.name = name
  }
  say() {
    console.log(`Hi, ${this.name}`)
  }
}
new PersonClass('jack').say()

22.静态方法

ES2015 中新增添加静态成员的static关键字

class Person {
  constructor(name) {
    this.name = name
  }
  say() {
    console.log(`Hi, ${this.name}`)
  }
  static create(name) {
    return new Person(name)
  }
}
Person.create('jack').say()

23.类的继承

class Person {
  constructor(name) {
    this.name = name
  }
  say() {
    console.log(`Hi, ${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}`)
  }
}
let s = new Student('Tom', '96281')
s.hello()

24.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('size:', s.size)
console.log(s.has(100))
console.log(s.delete(2))
s.clear()
console.log(s)

// 去重
const arr = [1, 2, 1, 3, 4]
const res = new Set(arr)
const arr1 = Array.from(res)
console.log(arr1)
// 展开运算符将Set转为Array
console.log([...res])

25.map

与Object对象类似,Object只能用字符串作为键,Map可以用任意类型的值作为键

// Object
const obj = {}
obj[true] = 'value'
obj[123] = 'value'
obj[{a: 1}] = 'value'
console.log(Object.keys(obj))
// 输出:[ '123', 'true', '[object Object]' ]
// 可以看到是将所有的key进行了toString转换,如果我们的key是不同的对象,最后会变成相同的key,即为'[object Object]'
console.log(obj['[object Object]']) // value

// Map 的key必须是字符串,就可以避免以上问题
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))
console.log(m.has(tom))
// m.clear()
// m.delete()
m.forEach((value, key) => console.log(key, value))

26.Symbol

一种全新的原始数据类型,当前最主要的作用就是为对象添加一个独一无二的属性名

// shared.js===============================共享文件
const cache = {}

// a.js==============================存储一个共享变量
cache['foo'] = '123'

// b.js==因为不知道已经存在foo的key,也存储了键为foo的变量
cache['foo'] = '456'

// 这时获取全局共享的变量,值就有问题了
console.log(cache['foo'])

// 使用Symbol
const s = Symbol()
console.log(s) // Symbol()
console.log(typeof s) // Symbol
console.log(Symbol() === Symbol()) // false

const obj = {}
obj[Symbol()] = '123'
obj[Symbol()] = '456'
console.log(obj)

// 私有属性
const name = Symbol()
const person = {
  [name]: 'Tom',
  say(){
    console.log('Hi, ', this[name])
  }
}
person.say()

Symbol的更多使用:

// Symbol创建的值肯定是唯一的,无论传入的值是否相同
console.log(
  Symbol() === Symbol(),
  Symbol('foo') === Symbol('foo')
)
// false false

// Symbol 提供了for()方法,传入相同的字符串,返回的值相同,
// 如果传入的不是字符串,会使用toString转成字符串后传入,也就是说在for方法中,
// 传入数字10和字符串10得到的值也是相同的
const s1 = Symbol.for('foo')
const s2 = Symbol.for('foo')
console.log(s1 === s2) // true
console.log(Symbol.for(10) === Symbol.for('10')) // true

const obj = {
  [Symbol.toStringTag]: 'XObject'
}
console.log(obj.toString()) // [object XObject]

// 通过Symbol 定义的属性名在外面是拿不到的,所以更好的作为私有属性
const obj11 = {
  [Symbol()]: 'symbol value',
  'foo': 'normal value'
}

// 输出: foo
for (var key in obj11) {
  console.log(key)
}
console.log(Object.keys(obj11)) // 输出:[ 'foo' ]
console.log(JSON.stringify(obj11)) // 输出:{"foo":"normal value"}

// 可以通过以下方法获取属性为Symbol的属性名
console.log(Object.getOwnPropertySymbols(obj11)) // [ Symbol() ]

27.for...of

作为遍历所有数据结构的统一方式

const arr = [1, 2, 3, 4]
for(const i of arr) {
  console.log(i)
}
// 以前使用forEach
arr.forEach(i => console.log(i))

for(const i of arr) {
  console.log(i)
  // 可以终止循环
  if(i > 1) break;
}
// arr.forEach() // 不能终止遍历

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

// 遍历map
const m = new Map()
m.set('foo', 123)
m.set('bar', 456)
for(const i of m) {
  console.log(i)
}
// [ 'foo', 123 ] [ 'bar', 456 ]

// 遍历对象
const obj = {foo: 123, bar: 456}
for (const i of obj) {
  console.log(i)
}
// 报错:TypeError: obj is not iterable
// 29小节解决

28.可迭代接口

因为ES中能够表示有结构的数据类型越来越多(之前的数组和对象,新增的Set和Map),为了给各种各样的数据结构提供统一的遍历方式,ES2015提供了Iterable接口,实现Iterable就是for...of的前提。

在浏览器的开发人员工具中,打印我们可以for...of的3种数据,可以在原型链上看到他们都实现了Symbol(Symbol.iterator)方法,如图所示依次是Set、数组和Map:

接着我们定义一个数组,去调用Symbol(Symbol.iterator)方法,可以得到如图结果,

可以看到Symbol(Symbol.iterator)方法中有一个next方法,调用该方法,可以依次得到数组的值和一个done标识,done表示是否迭代完数据。

29.实现可迭代接口

// const obj = {
//   [Symbol.iterator]: function () {
//     return {
//       next: function (){
//         return {
//           value: 'zxc',
//           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)
}

30.迭代器模式

// 协同开发一个任务清单应用
// A 的代码:保存数据
const todos = {
  life: ['吃饭', '睡觉', '打豆豆'],
  learn: ['语文', '数学', '英语'],
  work: ['喝茶'],
  each: function (callback) {
    const all = [].concat(this.life, this.learn, this.work)
    for(const i of all) {
      callback(i)
    }
  },
  [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
        }
      }
    }
  }
}

// B的代码:展示数据
// 如果直接遍历,可以拿到
for(const i of todos.life) { console.log(i) }
for(const i of todos.learn) { console.log(i) }

// 但是如果A的数据结构发生变化,比如增加一个work项,那么B也要增加
for(const i of todos.work) { console.log(i) }

// 所以最好的办法是A的代码中提供一个迭代方法each,B只管拿值使用就行
todos.each((i) => console.log(i))

// 最后再使用迭代器的方式解决一下,实现[Symbol.iterator]方法
for(const i of todos) { console.log(i) }

31.生成器

避免异步编程中回调嵌套过深的问题,提供更好的异步编程解决方案

function * foo(){
  console.log('zcz')
  return 100
}

const result = foo()
console.log(result)
// 输出:Object [Generator] {}

console.log(result.next())
// zcz
// { value: 100, done: true }

// 使用yield关键字
function * bar () {
  console.log('111')
  yield 100
  console.log('222')
  yield 200
  console.log('333')
  yield 300
}
const generator = bar()
console.log(generator.next())
// 111
// { value: 100, done: false }
console.log(generator.next())
// 222
// { value: 200, done: false }
console.log(generator.next())
// 333
// { value: 300, done: false }

32.生成器应用

// 案例1:发号器
function * createIdMaker () {
  let id = 1
  while (true) {
    yield id++
  }
}

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 函数实现Iterable方法,修改之前的示例
const todos = {
  life: ['吃饭', '睡觉', '打豆豆'],
  learn: ['语文', '数学', '英语'],
  work: ['喝茶'],
  [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 i of all) {
      yield i
    }
  }
}
for(const i of todos) { console.log(i) }

33.ES2016

新增了数组方法 includes 和指数运算 **

// 新增1:Array.prototype.includes
const arr = ['foo', 1, NaN, false]
// 之前判断数组是否包含某值
console.log(arr.indexOf('foo')) // 0
console.log(arr.indexOf(1)) // 1
console.log(arr.indexOf(NaN)) // -1,这就是问题,不能查询NaN

// includes方法, 直接返回布尔值表示存不存在
console.log(arr.includes(1)) // true
console.log(arr.includes(NaN)) // true
console.log(arr.includes(2)) // false


// 新增2:指数运算符
// 之前使用Math.pow
console.log(Math.pow(2, 10)) // 1024
// 使用**
console.log( 2 ** 10) // 1024

34.ES2017

const obj = {
  foo: 'value1',
  bar: 'value2'
}

// 新增1:Object.values 返回对象值组成的数组
console.log(Object.values(obj)) // [ 'value1', 'value2' ]

// 新增2:Object.entries 返回键值对组成的数组
console.log(Object.entries(obj)) // [ [ 'foo', 'value1' ], [ 'bar', 'value2' ] ]
// 这样就可以直接使用for...of方法了,因为之前看到Object对象无法直接使用for...of
for(const i of Object.entries(obj)) { console.log(i) }
// [ 'foo', 'value1' ]
// [ 'bar', 'value2' ]

// 新增3:Object.getOwnPropertyDescriptors 获取对象的完整描述信息
console.log(Object.getOwnPropertyDescriptors(obj))

const p1 = {
  firstName: 'Lei',
  lastName: 'Li',
  get fullName () {
    return this.firstName + ' ' + this.lastName
  }
}
console.log(p1.fullName)

const p2 = Object.assign({}, p1)
p2.firstName = 'mao'
console.log(p2)
// 输出为:{ firstName: 'mao', lastName: 'Li', fullName: 'Lei Li' }
// 可以发现 fullName没有改变,是因为Object.assign复制的时候,将fullName当成普通属性复制的
// 使用Object.getOwnPropertyDescriptors获取对象属性的完整信息,并且重新定义一个新的对象
const p3 = Object.defineProperties({}, Object.getOwnPropertyDescriptors(p1))
p3.firstName = 'mao'
console.log(p3.fullName) // mao Li

// 新增4:String.prototype.padStart 和 String.prototype.padEnd
// 给指定的字符串填充指定的符号(开始或者结束),达到给定的长度
const books = {
  html: 5,
  css: 6,
  js: 128
}
for(const [name, count] of Object.entries(books)) {
  console.log(name, count)
}
// html 5
// css 6
// js 128
// 看起来输出有些乱,可以使用pad方法做一个对齐
for(const [name, count] of Object.entries(books)) {
  console.log(`${ name.padEnd(16, '-') } | ${count.toString().padStart(3, '0')}`)
}

// 新增5:在函数参数的末尾可以添加尾逗号
function foo (
  bar, baz,
){

}
// 数组也可以
const arr = [100, 200, 300,]

// 新增6:Async/await 异步编程小节中有详细解释

35.后续版本更新

https://github.com/tc39/proposals/blob/main/finished-proposals.md

36.示例代码

https://github.com/guduqiucai/web/tree/main/JavaScript/newFeaturesOfES6

标签:const,name,特性,obj,ECMAScript,console,foo,log
From: https://www.cnblogs.com/caicai521/p/17015276.html

相关文章

  • 第六章《类的高级特性》第5节:接口
    ​在Java语言中,一个类只能继承一个父类,专业上把这种继承机制称为“单继承”。单继承是一种较为稳妥继承机制,能够规避很多潜在的问题,但这种继承机制的局限性也显而易见:子类不......
  • 第六章《类的高级特性》第6节:面向对象三大特征
    ​面向对象编程具有三大特征,分别是:封装、继承和多态。一、封装​封装就是隐藏一切可隐藏的东西,只向外界提供最简单的操作接口。举例来说:电视机的工作原理很复杂,为完成这些......
  • 第六章《类的高级特性》第3节:访问修饰符
    ​在日常生活中,各种信息的公开程度是不一样的。例如:张贴在公告栏中的信息就是完全公开的,任何人都可以看,而有一些具有保密性质的档案就不能被所有人查看,它仅能被拥有权限的特......
  • 墨菲安全软件供应链安全产品v3.0正式公测之产品特性简介及用户升级说明
    墨菲安全2.0产品3月份发布以来过去了9个月的时间,在这期间收获了超过10000+开发者用户,700+的开源项目star以及包括蚂蚁、平安、快手等在内的数十个企业版客户;在这......
  • 墨菲安全软件供应链安全产品v3.0正式公测之产品特性简介及用户升级说明
    墨菲安全2.0产品3月份发布以来过去了9个月的时间,在这期间收获了超过10000+开发者用户,700+的开源项目star以及包括蚂蚁、平安、快手等在内的数十个企业版客户;在这......
  • PHP 8.1有哪些变化:新特性、改变及弃用等
    不久前,PHP8.0大张旗鼓地发布了。它带来了许多新特性、性能增强和变化——其中最令人兴奋的是新的JIT编译器。技术世界总是在向前发展,PHP也是如此。PHP8.1也快来了,包含......
  • PHP8 新特性
    命名参数属性构造函数属性提升联合类型Match表达式Nullsafe操作符更合理的字符串与数值的比较内置函数的一致错误类型JIT编译类型系统和错误处理的改进其他的语法微调和......
  • PHP 8.2已发布所有新特性概览
    PHP8.2已经发布。与8.0和8.1相比,这是一个次要版本。这可以部分归因于NikitaPopov作为PHP语言最重要的贡献者之一的离开。PHP语言有很多维护者,并不是一个人创建的,但Nikit......
  • 白话Java高级特性之异常
    白话Java高级特性之异常对于本文的内容,属于基础知识研究范畴,切勿以为读完此文就能将异常知识掌握到家。切记:操千曲而后晓声,观千剑而后识器,所以我觉得没有大量的源码阅读经验......
  • DAMA试题分析:主键有什么特性?
    编 辑:彭文华​彭友萌好,我是老彭友嗯,上周末我去考了个CDGA,其中有道题出重复了(15和55题),题目大概是这样的,你也来选一下看看结果:就是这样一道看上去很简单的题,结果引起一场超级......