ECMAScript

ECMAScript 与JavaScript

  • 浏览器环境中 JavaScript —— ECMAScript + Web apis(BOM, DOM)

  • node环境中国 JavaScript —— ECMAScript + Node apis(fs, net, etc)
    JavaScript中语言本身指的就是ECMAScript

ES2015相较之前的变化

  • 对原有语法进行增幅 (解构,展开, 参数默认值, 模版字符串)
  • 解决了原有语法的一些问题或者缺陷 ( let const 块级作用域)
  • 全新的对象, 全新的方法, 全新的功能 (promise, obj.assign)
  • 全新的数据类型和数据结构( Symbol, set, map)

ESMAScript新特性

let const

  • es2015之前(全局作用域, 函数作用域)
  • 块级作用域 ( 代码中 {} 包裹起来的范围)

let

  • let声明的成员只会在所声明的块中生效
  • let在for循环中的表现——可以利用var和let作用域不同,在双for循环中区分计数器.
  • let 应用场景: 循环绑定事件, 事件处理函数中获取正确索引
  • for循环会产生两层作用域
  • let 修复了变量声明提升现象

const

  • 声明恒量/常量,相比let,特点是只读

const只读:

声明的成员不能被修改的意思是,不允许在声明过后重新指向新的内存地址,并不是不能修改属性中的恒量成员

  1. // const name = 'zce'
  2. // name = 'jack'
  3. // 恒量声明过后不允许重新赋值
  4. // const name
  5. // name = 'zce'
  6. // 恒量要求声明同时赋值
  7. // const obj = {}
  8. // obj.name = 'zce' // success
  9. // 恒量只是要求内层指向不允许被修改
  10. // 对于数据成员的修改是没有问题的
  11. // obj = {} // error 赋值会改变内存指向

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

解构

数组的解构

  1. // 数组的解构
  2. const arr = [100, 200, 300]
  3. // 通过下标定位解构
  4. // const foo = arr[0]
  5. // const bar = arr[1]
  6. // const baz = arr[2]
  7. // console.log(foo, bar, baz) => [100, 200, 300]
  8. // 按照顺序填写变量名, 不需要项可以删除, 但要保留逗号
  9. // const [foo, bar, baz] = arr
  10. // console.log(foo, bar, baz) => [100, 200, 300]
  11. // const [, , baz] = arr
  12. // console.log(baz) => 300
  13. // ... 表示提取从当前位置往后的所有成员,会放在一个数组当中(rest),但只能在解构最后一个位置使用
  14. // const [foo, ...rest] = arr
  15. // console.log(rest) => [200, 300]
  16. // const [foo, bar, baz, more] = arr
  17. // console.log(more) => undefined
  18. // 给解构的数据设置默认值
  19. // const [foo, bar, baz = 123, more = 'default value'] = arr
  20. // console.log(bar, more)
  21. const path = '/foo/bar/baz'
  22. // const tmp = path.split('/')
  23. // const rootdir = tmp[1]
  24. const [, rootdir] = path.split('/')
  25. console.log(rootdir)

对象的解构

  1. const obj = { name: 'zce', age: 18 }
  2. // 通过属性名定位解构
  3. // const { name } = obj
  4. // console.log(name)
  5. // 外部变量名与要解构的属性名重复的情况下可以重命名
  6. // name : onjname
  7. // 冒号左边是重复的属性名, 右边是重命名之后的属性名
  8. // const name = 'tom'
  9. // const { name: objName } = obj
  10. // console.log(objName)
  11. // 设置默认值
  12. // const name = 'tom'
  13. // const { name: objName = 'jack' } = obj
  14. // console.log(objName)
  15. const { log } = console
  16. log('foo')
  17. log('bar')
  18. log('123')

模版字符串 ``

  • 反引号包裹
  • 允许换行
  • 可以通过 ${} 插入表达式, 表达式的执行结果将会输出到相应位置

带标签的模版字符串

  1. // 模版字符串的标签就是一个特殊的函数,使用这个标签就是调用这个函数
  2. // const str = console.log`hello world`
  3. const name = 'tom'
  4. const gender = false
  5. function myTagFunc (strings, name, gender) {
  6. // console.log(strings, name, gender)
  7. // return '123'
  8. const sex = gender ? 'man' : 'woman'
  9. return strings[0] + name + strings[1] + sex + strings[2]
  10. }
  11. const result = myTagFunc`hey, ${name} is a ${gender}.`
  12. console.log(result)
  • 可以实现文本的多语言转换
  • 检查文本字符串中是否存在一些不安全的字符
  • 小型的模版引擎

字符串的扩展方法

  • startsWith 是否以‘’开始
  • endsWith 是否以‘’结尾
  • includes 是否包含‘’

函数参数的默认值

  • 设置默认值使用 形参 = 默认值 方式设置
  • 多个形参, 默认值要写在形参列表最后
  • 默认值只在函数调用没有传实参的情况下生效
  1. function foo (enable = true) {
  2. console.log('foo invoked - enable: ')
  3. console.log(enable)
  4. }
  5. foo(false)

剩余参数

  1. // function foo () {
  2. // console.log(arguments)
  3. // }
  4. // es2015
  5. function foo (first, ...args) {
  6. console.log(args)
  7. }
  8. foo(1, 2, 3, 4) => [2, 3, 4]

展开数组参数

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

箭头函数

  1. // function inc (number) {
  2. // return number + 1
  3. // }
  4. // 最简方式
  5. // const inc = n => n + 1
  6. // 完整参数列表,函数体多条语句,返回值仍需 return
  7. const inc = (n, m) => {
  8. console.log('inc invoked')
  9. return n + 1
  10. }
  11. console.log(inc(100))
  12. const arr = [1, 2, 3, 4, 5, 6, 7]
  13. // arr.filter(function (item) {
  14. // return item % 2
  15. // })
  16. // 常用场景,回调函数
  17. arr.filter(i => i % 2)
  • 箭头函数不会改变 this 指向
  1. const person = {
  2. name: 'tom',
  3. // sayHi: function () {
  4. // console.log(`hi, my name is ${this.name}`)
  5. // }
  6. sayHi: () => {
  7. console.log(`hi, my name is ${this.name}`)
  8. // name is undefined
  9. },
  10. sayHiAsync: function () {
  11. // const _this = this
  12. // setTimeout(function () {
  13. // console.log(_this.name) // 取不到
  14. // }, 1000)
  15. console.log(this)
  16. setTimeout(() => {
  17. // console.log(this.name)
  18. console.log(this)
  19. }, 1000)
  20. }
  21. }
  22. person.sayHiAsync()

对象字面量

  • 属性名与变量名相同, 可以省略 // bar: bar, => bar,
  • 方法可以省略 method1: function () {} => method1 () {}
  • 给对象添加动态属性名
  1. // Math.random() : 123 不被允许
  2. // 通过 [] 让表达式的返回值作为属性名
  3. const obj = {
  4. bar: 123
  5. }
  6. obj[Math.random()] = 456

Object.assign

  • 将多个源对象中的属性复制到一个目标对象中
  1. const data1 = {
  2. a: 123,
  3. b: 123
  4. }
  5. const data2 = {
  6. a: 4567,
  7. b: 4567
  8. }
  9. const data = {
  10. b: 087
  11. c: 824,
  12. d: 789
  13. }
  14. const reslut = Object.assign(data, data1, data2)
  15. const reslut2 = Object.assign({}, data1, data2)
  16. console.log(data)
  17. console.log(reslut === data)
  18. // 合并的结果和目标对象完全相同, 指向同一内存地址.改一个另一个同时改
  19. // 可以通过设置目标对象为空对象实现一个全新的对象, 修改就不会影响外部数据

Object.is

  • == 会默认转换类型进行比较
  • === 严格对比数值是否相同

=== 的两个缺陷

  • -0 === +0 => true
  • NaN === NaN => false
  1. // Object.is(+0, -0) // => false
  2. // Object.is(NaN, NaN) // => true

Proxy 对象

  1. // Proxy 对象
  2. // const person = {
  3. // name: 'zce',
  4. // age: 20
  5. // }
  6. // target 代理对象
  7. // property 监听变化的属性名
  8. // const personProxy = new Proxy(person, {
  9. // // 监视属性读取
  10. // get (target, property) {
  11. // return property in target ? target[property] : 'default'
  12. // // console.log(target, property)
  13. // // return 100
  14. // },
  15. // // 监视属性设置
  16. // set (target, property, value) {
  17. // if (property === 'age') {
  18. // if (!Number.isInteger(value)) {
  19. // throw new TypeError(`${value} is not an int`)
  20. // }
  21. // }
  22. // target[property] = value
  23. // // console.log(target, property, value)
  24. // }
  25. // })
  26. // personProxy.age = 100
  27. // personProxy.gender = true
  28. // console.log(personProxy.name)
  29. // console.log(personProxy.xxx)

Proxy 对比 Object.defineProperty() ===============

  1. // 优势1:Proxy 可以监视读写以外的操作 --------------------------
  2. // const person = {
  3. // name: 'zce',
  4. // age: 20
  5. // }
  6. // const personProxy = new Proxy(person, {
  7. // deleteProperty (target, property) {
  8. // console.log('delete', property)
  9. // delete target[property]
  10. // }
  11. // })
  12. // delete personProxy.age
  13. // console.log(person)
  14. // 优势2:Proxy 可以很方便的监视数组操作 --------------------------
  15. // const list = []
  16. // const listProxy = new Proxy(list, {
  17. // set (target, property, value) {
  18. // console.log('set', property, value)
  19. // target[property] = value
  20. // return true // 表示设置成功
  21. // }
  22. // })
  23. // listProxy.push(100)
  24. // listProxy.push(100)
  25. // 优势3:Proxy 不需要侵入对象 --------------------------
  26. // const person = {}
  27. // Object.defineProperty(person, 'name', {
  28. // get () {
  29. // console.log('name 被访问')
  30. // return person._name
  31. // },
  32. // set (value) {
  33. // console.log('name 被设置')
  34. // person._name = value
  35. // }
  36. // })
  37. // Object.defineProperty(person, 'age', {
  38. // get () {
  39. // console.log('age 被访问')
  40. // return person._age
  41. // },
  42. // set (value) {
  43. // console.log('age 被设置')
  44. // person._age = value
  45. // }
  46. // })
  47. // person.name = 'jack'
  48. // console.log(person.name)
  49. // Proxy 方式更为合理
  50. const person2 = {
  51. name: 'zce',
  52. age: 20
  53. }
  54. const personProxy = new Proxy(person2, {
  55. get (target, property) {
  56. console.log('get', property)
  57. return target[property]
  58. },
  59. set (target, property, value) {
  60. console.log('set', property, value)
  61. target[property] = value
  62. }
  63. })
  64. personProxy.name = 'jack'
  65. console.log(personProxy.name)

Reflect 对象

  • 统一提供一套用于操作对象的api
  • proxy 内部get方法不设置默认用reflect.get
  1. // const proxy = new Proxy(obj, {
  2. // get (target, property) {
  3. // return Reflect.get(target, property)
  4. // }
  5. // })
  6. // console.log('name' in obj)
  7. // console.log(delete obj['age'])
  8. // console.log(Object.keys(obj))
  9. // 统一方法, 避免方法和操作符混用带来的不便
  10. // console.log(Reflect.has(obj, 'name'))
  11. // console.log(Reflect.deleteProperty(obj, 'age'))
  12. // console.log(Reflect.ownKeys(obj))

promise

class 关键字

  1. // class 关键词
  2. // function Person (name) {
  3. // this.name = name
  4. // }
  5. // 创建共享方法
  6. // Person.prototype.say = function () {
  7. // console.log(`hi, my name is ${this.name}`)
  8. // }
  9. class Person {
  10. constructor (name) {
  11. this.name = name
  12. }
  13. say () {
  14. console.log(`hi, my name is ${this.name}`)
  15. }
  16. }
  17. const p = new Person('tom')
  18. p.say()

static

实例方法 - 通过这个类型构造的实例对象去调用

静态方法 - 直接通过类型本身调用

  • 以前实现静态方法: 直接在构造函数对象上去挂载方法
  • ES2015中新增添加静态成员static的关键词

this问题: 因为static构建的静态方法是挂载在类上面的,所以静态方法的this就不会指向实例对象,而是指向当前的类型

  1. class Person {
  2. constructor (name) {
  3. this.name = name
  4. }
  5. say () {
  6. console.log(`hi, my name is ${this.name}`)
  7. }
  8. static create (name) {
  9. // this 指向当前类 person
  10. return new Person(name)
  11. }
  12. }
  13. const tom = Person.create('tom')
  14. tom.say()

extends 继承

  1. class Person {
  2. constructor (name) {
  3. this.name = name
  4. }
  5. say () {
  6. console.log(`hi, my name is ${this.name}`)
  7. }
  8. }
  9. class Student extends Person {
  10. constructor (name, number) {
  11. super(name) // 通过super调用父类构造函数
  12. this.number = number
  13. }
  14. hello () {
  15. super.say() // 通过super调用父类构造函数
  16. console.log(`my school number is ${this.number}`)
  17. }
  18. }
  19. const s = new Student('jack', '100')
  20. s.hello()

Set 数据结构

const newSet = new Set();

  • newSet.forEach(i => console.log(i))
  • for (let i of newSet) { console.log(i) }
  • newSet.has(value) // 集合中是否有特定值
  • newSet.size // 集合的长度
  • newSet.delete(value) // 删除特定值, 删除成功返回true
  • newSet.clear() // 清除集合中的全部内容

数组去重

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

  • const result = Array.from(new Set(arr))
  • const result = […new Set(arr)]

map 数据结构

  • 可以存储任意类型作为键
  • object - 字符串 / 值
  • map - 值 / 值
  1. const m = new Map()
  2. const tom = { name: 'tom' }
  3. m.set(tom, 90)
  4. console.log(m)
  5. console.log(m.get(tom))
  6. // m.has()
  7. // m.delete()
  8. // m.clear()
  9. m.forEach((value, key) => {
  10. console.log(value, key)
  11. })

Symbol 数据类型

  • 两个 Symbol 永远不会相等
  • Symbol 可以传入字符串作为描述文本
  • 使用 Symbol 为对象添加用不重复的键

    • object键类型 ( 字符串、 Symbol )
  • 也可以在计算属性名中使用
  • Symbol 模拟实现私有成员
  1. // 两个 Symbol 永远不会相等
  2. // console.log(
  3. // Symbol() === Symbol()
  4. // )
  5. // Symbol 描述文本
  6. // console.log(Symbol('foo'))
  7. // console.log(Symbol('bar'))
  8. // console.log(Symbol('baz'))
  9. // 使用 Symbol 为对象添加用不重复的键
  10. // const obj = {}
  11. // obj[Symbol()] = '123'
  12. // obj[Symbol()] = '456'
  13. // console.log(obj)
  14. // 也可以在计算属性名中使用
  15. // const obj = {
  16. // [Symbol()]: 123
  17. // }
  18. // console.log(obj)
  19. // =========================================================
  20. // 案例2:Symbol 模拟实现私有成员
  21. // a.js ======================================
  22. const name = Symbol()
  23. const person = {
  24. [name]: 'zce',
  25. say () {
  26. console.log(this[name])
  27. }
  28. }
  29. // 只对外暴露 person
  30. // b.js =======================================
  31. // 由于无法创建出一样的 Symbol 值,
  32. // 所以无法直接访问到 person 中的「私有」成员
  33. // person[Symbol()]
  34. person.say()

Symbol 补充

  1. // Symbol 补充
  2. // console.log(
  3. // // Symbol() === Symbol()
  4. // Symbol('foo') === Symbol('foo')
  5. // )
  6. // Symbol 全局注册表 ----------------------------------------------------
  7. // const s1 = Symbol.for('foo')
  8. // const s2 = Symbol.for('foo')
  9. // console.log(s1 === s2)
  10. // Symbol 内部维护了一个字符串对应Symbol的一个注册表, 是一一对应的
  11. // 对传入的值会默认转换成字符串类型
  12. // console.log(
  13. // Symbol.for(true) === Symbol.for('true')
  14. // )
  15. // 内置 Symbol 常量 ---------------------------------------------------
  16. // console.log(Symbol.iterator)
  17. // console.log(Symbol.hasInstance)
  18. // const obj = {
  19. // [Symbol.toStringTag]: 'XObject'
  20. // }
  21. // console.log(obj.toString())
  22. // Symbol 属性名获取 ---------------------------------------------------
  23. const obj = {
  24. [Symbol()]: 'symbol value',
  25. foo: 'normal value'
  26. }
  27. // 获取不到
  28. // for (var key in obj) {
  29. // console.log(key)
  30. // }
  31. // console.log(Object.keys(obj))
  32. // console.log(JSON.stringify(obj))
  33. // 获取的正确姿势
  34. console.log(Object.getOwnPropertySymbols(obj))

for…of 循环

  • for 适合遍历普通的数组
  • for…in 适合遍历键/值对
  • 数组对象的forEach 无法跳出循环, for…of 可以使用 break 关键字
  1. // 遍历 Map 可以配合数组结构语法,直接获取键值
  2. // const m = new Map()
  3. // m.set('foo', '123')
  4. // m.set('bar', '345')
  5. // for (const [key, value] of m) {
  6. // console.log(key, value)
  7. // }
  8. // 普通对象不能被直接 for...of 遍历
  9. // const obj = { foo: 123, bar: 456 }
  10. // for (const item of obj) {
  11. // console.log(item)
  12. // }

迭代器 ( Iterator )

总结

  • 所有可以直接被for…of遍历的数据类型,都必须实现了统一的Iterable接口( 内部必须挂载iterator方法, 这个方法会返回一个带有next方法的对象,不断调用这个next方法就可以实现遍历, next方法返回的对象内部的value 为当前遍历的键值对. done表示循环是否结束 )
  1. const set = new Set(['foo', 'bar', 'baz'])
  2. const iterator = set[Symbol.iterator]()
  3. // console.log(iterator.next())
  4. // console.log(iterator.next())
  5. // console.log(iterator.next())
  6. // console.log(iterator.next())
  7. // console.log(iterator.next())
  8. while (true) {
  9. const current = iterator.next()
  10. if (current.done) {
  11. break // 迭代已经结束了,没必要继续了
  12. }
  13. console.log(current.value)
  14. }

实现可迭代接口(Iterable)

  1. // const obj = {
  2. // [Symbol.iterator]: function () {
  3. // return {
  4. // next: function () {
  5. // return {
  6. // value: 'zce',
  7. // done: true
  8. // }
  9. // }
  10. // }
  11. // }
  12. // }
  13. const obj = {
  14. store: ['foo', 'bar', 'baz'],
  15. [Symbol.iterator]: function () {
  16. let index = 0
  17. const self = this
  18. return {
  19. next: function () {
  20. const result = {
  21. value: self.store[index],
  22. done: index >= self.store.length
  23. }
  24. index++
  25. return result
  26. }
  27. }
  28. }
  29. }
  30. for (const item of obj) {
  31. console.log('循环体', item)
  32. }

迭代器设计模式

  1. // 场景:你我协同开发一个任务清单应用
  2. // 我的代码 ===============================
  3. const todos = {
  4. life: ['吃饭', '睡觉', '打豆豆'],
  5. learn: ['语文', '数学', '外语'],
  6. work: ['喝茶'],
  7. // 提供统一遍历访问接口
  8. each: function (callback) {
  9. const all = [].concat(this.life, this.learn, this.work)
  10. for (const item of all) {
  11. callback(item)
  12. }
  13. },
  14. // 提供迭代器(ES2015 统一遍历访问接口)
  15. [Symbol.iterator]: function () {
  16. const all = [...this.life, ...this.learn, ...this.work]
  17. let index = 0
  18. return {
  19. next: function () {
  20. return {
  21. value: all[index],
  22. done: index++ >= all.length
  23. }
  24. }
  25. }
  26. }
  27. }
  28. // 你的代码 ===============================
  29. // for (const item of todos.life) {
  30. // console.log(item)
  31. // }
  32. // for (const item of todos.learn) {
  33. // console.log(item)
  34. // }
  35. // for (const item of todos.work) {
  36. // console.log(item)
  37. // }
  38. todos.each(function (item) {
  39. console.log(item)
  40. })
  41. console.log('-------------------------------')
  42. for (const item of todos) {
  43. console.log(item)
  44. }

Generator 函数

  • 在函数方法名前加星号( * )
  • yield会暂停执行, 后面的值会作为构造器next()方法返回
  1. // function * foo () {
  2. // console.log('zce')
  3. // return 100
  4. // }
  5. // const result = foo()
  6. // console.log(result.next())
  7. function * foo () {
  8. console.log('1111')
  9. yield 100
  10. console.log('2222')
  11. yield 200
  12. console.log('3333')
  13. yield 300
  14. }
  15. const generator = foo()
  16. console.log(generator.next()) // 第一次调用,函数体开始执行,遇到第一个 yield 暂停
  17. console.log(generator.next()) // 第二次调用,从暂停位置继续,直到遇到下一个 yield 再次暂停
  18. console.log(generator.next()) // 。。。
  19. console.log(generator.next()) // 第四次调用,已经没有需要执行的内容了,所以直接得到 undefined

Generator 应用

  1. // 案例1:发号器
  2. function * createIdMaker () {
  3. let id = 1
  4. while (true) {
  5. yield id++
  6. }
  7. }
  8. const idMaker = createIdMaker()
  9. console.log(idMaker.next().value)
  10. console.log(idMaker.next().value)
  11. console.log(idMaker.next().value)
  12. console.log(idMaker.next().value)
  13. // 案例2:使用 Generator 函数实现 iterator 方法
  14. const todos = {
  15. life: ['吃饭', '睡觉', '打豆豆'],
  16. learn: ['语文', '数学', '外语'],
  17. work: ['喝茶'],
  18. [Symbol.iterator]: function * () {
  19. const all = [...this.life, ...this.learn, ...this.work]
  20. for (const item of all) {
  21. yield item
  22. }
  23. }
  24. }
  25. for (const item of todos) {
  26. console.log(item)
  27. }

ECMAScript 2016

  • Array.prototype.includes
  • 指数运算符
  1. // Array.prototype.includes -----------------------------------
  2. // const arr = ['foo', 1, NaN, false]
  3. // 找到返回元素下标
  4. // console.log(arr.indexOf('foo'))
  5. // 找不到返回 -1
  6. // console.log(arr.indexOf('bar'))
  7. // 无法找到数组中的 NaN
  8. // console.log(arr.indexOf(NaN))
  9. // 直接返回是否存在指定元素
  10. // console.log(arr.includes('foo'))
  11. // 能够查找 NaN
  12. // console.log(arr.includes(NaN))
  13. // 指数运算符 ---------------------------------------------------
  14. // console.log(Math.pow(2, 10))
  15. console.log(2 ** 10)

ECMAScript 2017

  • Object.values (值数组)
  • Object.entries (键值对数组)
  • Object.getOwnPropertyDescriptors
  • String.prototype.padStart / String.prototype.padEnd
  • 在函数参数中添加尾逗号
  • async/await
  1. // const obj = {
  2. // foo: 'value1',
  3. // bar: 'value2'
  4. // }
  5. // Object.values -----------------------------------------------------------
  6. // console.log(Object.values(obj))
  7. // Object.entries ----------------------------------------------------------
  8. // console.log(Object.entries(obj))
  9. // for (const [key, value] of Object.entries(obj)) {
  10. // console.log(key, value)
  11. // }
  12. // console.log(new Map(Object.entries(obj)))
  13. // Object.getOwnPropertyDescriptors ----------------------------------------
  14. // const p1 = {
  15. // firstName: 'Lei',
  16. // lastName: 'Wang',
  17. // get fullName () {
  18. // return this.firstName + ' ' + this.lastName
  19. // }
  20. // }
  21. // // console.log(p1.fullName)
  22. // // const p2 = Object.assign({}, p1)
  23. // // p2.firstName = 'zce'
  24. // // console.log(p2)
  25. // const descriptors = Object.getOwnPropertyDescriptors(p1)
  26. // // console.log(descriptors)
  27. // const p2 = Object.defineProperties({}, descriptors)
  28. // p2.firstName = 'zce'
  29. // console.log(p2.fullName)
  30. // String.prototype.padStart / String.prototype.padEnd --------------------
  31. // const books = {
  32. // html: 5,
  33. // css: 16,
  34. // javascript: 128
  35. // }
  36. // // for (const [name, count] of Object.entries(books)) {
  37. // // console.log(name, count)
  38. // // }
  39. // for (const [name, count] of Object.entries(books)) {
  40. // console.log(`${name.padEnd(16, '-')}|${count.toString().padStart(3, '0')}`)
  41. // }
  42. // 在函数参数中添加尾逗号 -----------------------------------------------------
  43. // function foo (
  44. // bar,
  45. // baz,
  46. // ) {
  47. // }
  48. // const arr = [
  49. // 100,
  50. // 200,
  51. // 300,
  52. // ]
  53. // const arr = [
  54. // 100,
  55. // 200,
  56. // 300,
  57. // 400,
  58. // ]
  59. // const arr = [
  60. // 100,
  61. // 200,
  62. // 300
  63. // ]
  64. // const arr = [
  65. // 100,
  66. // 200,
  67. // 300,
  68. // 400
  69. // ]