JS ES6学习总结 拉钩模块二

目前大多数人还没搞明白语言和平台之间的关系。

ECMAScript 概述

ES是JS的扩展语言,也可以说ES就是JS。
在这里插入图片描述
在这里插入图片描述

ES2015 概述

ES2015 let 与块级作用域

ES5前只有俩种作用域,全局和函数作用域,ES5后多了块级作用域。
在这里插入图片描述
但使用let就不能获取到了。

1不能let重复声明

if (true) {
  var foo = 'zce'
  var foo = 'zce'
  console.log(foo)
}
// OK

if (true) {
    var foo = 'zce'
    let foo = 'zce'
    console.log(foo)
  }
  // let 不能重复声明

2 使用嵌套for,本应该执行九次的循环,执行三次就停止了,是因为内嵌的for i 覆盖了父级的 i 所以就打印了三次

在这里插入图片描述

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

在这里插入图片描述
闭包可以解决这个问题,本质也就是把i变量,摆脱全局后赋值。
在这里插入图片描述
for 循环会产生两层作用域

// for (let i = 0; i < 3; i++) {
//   let i = 'foo'
//   console.log(i)
// }

// let i = 0

// if (i < 3) {
//   let i = 'foo'
//   console.log(i)
// }

// i++

// if (i < 3) {
//   let i = 'foo'
//   console.log(i)
// }

// i++

// if (i < 3) {
//   let i = 'foo'
//   console.log(i)
// }

// i++

let 修复了变量声明提升现象

console.log(foo);
var foo = '2'
// 已声明但未赋值,这个叫变量提升
console.log(foo2);
let foo2 = '2'
//  error  before initialization

ECMA不升级var而用新的let是因为 升级var会导致之前的项目不能用。

ES2015 let 与块级作用域结束·······································

ES2015 const

在这里插入图片描述
也就是不能被修改,不能声明过后再重新指向新的内存地址,但能修改内部属性成员。
在这里插入图片描述

ES2015 const 结束

ES2015 数组的解构

// 数组的解构

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 [, rootdir] = path.split('/')
console.log(rootdir)

ES2015 对象的解构

// 对象的解构

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

解构结束

ES2015 模板字符串

// 模板字符串

// 反引号包裹
// 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)

ES2015 带标签的模板字符串

// 带标签的模板字符串

// 模板字符串的标签就是一个特殊的函数,
// 使用这个标签就是调用这个函数
// 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)

可用于文本多语言化,检查是否包含不安全的字符。

模板字符串END

字符串扩展方法

// 字符串的扩展方法

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

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

ES2015 参数默认值

// 函数参数的默认值

// function foo (enable) {
//   // 短路运算很多情况下是不适合判断默认参数的,例如 0 '' false null
//   // enable = enable || true
//   enable = enable === undefined ? true : enable
//   console.log('foo invoked - enable: ')
//   console.log(enable)
// }

// 默认参数一定是在形参列表的最后
function foo (enable = true) {
  console.log('foo invoked - enable: ')
  console.log(enable)
}

foo(false)

ES2015 剩余参数

// 剩余参数

// function foo () {
//   console.log(arguments)
// }

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

foo(1, 2, 3, 4)

ES2015 展开数组

// 展开数组参数

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

// console.log(
//   arr[0],
//   arr[1],
//   arr[2],
// )

// console.log.apply(console, arr)

console.log(...arr)

ES2015 箭头函数

// 箭头函数

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

ES2015 箭头函数与 this

不会改变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()

function test (){
  console.log(this); //windows
}
let test2 = ()=>{
  console.log(this); //{}
}
test()
test2()

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 // 不允许
  // 通过 [] 让表达式的结果作为属性名 es5后
  [bar]: 123
}

// obj[Math.random()] = 123

console.log(obj)
obj.method1()

ES2015 Object.assign

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

ES2015 Object.is

==运算符会在比较前转换类型,也就会导致0 ==false,成立,===是比较数值是否都相等,但严格相等无法判断-0和0

// Object.is

console.log(
  // 0 == false              // => true
  // 0 === false             // => false
  // +0 === -0               // => true
  // NaN === NaN             // => false  es5后提出nan应该相等,之前因为可能是随机数才不相等
  // Object.is(+0, -0)       // => false
  // Object.is(NaN, NaN)     // => true
)

ES2015 Proxy

用于监控某对象的读写

// Proxy 对象

// Proxy 对象

const person = {
  name: 'zce',
  age: 20
}
// 第二个是代理对象
const personProxy = new Proxy(person, {
  // 监视属性访问
  //1.target is the person  property is the property will be obeserved
  get(target,property){
    // console.log(target, property) // { name: 'zce', age: 20 } 

    // get的返回值就是访问的这个prop
    // return 110
    return property in target ? property :'default'
  },
  // observe set property
  set(target,property,value){
    if(property === 'age'){
     if(!  Number.isInteger(value)){
       throw TypeError(`${value} is not int`)
     }
    }
    target[property] = value

  }

})

// console.log(personProxy.name); //110
personProxy.age = 1
console.log(personProxy);

ES2015 Proxy 对比 defineProperty

在这里插入图片描述

Proxy能监控更多对象操作(例如delete或方法调用,Proxy 可以监视读写以外的操作,Proxy 可以很方便的监视数组操作,优势3:Proxy 不需要侵入对象),defineProperty只能监听读取或写入。

// Proxy 对象

// const person = {
//   name: 'zce',
//   age: 20
// }
// // 第二个是代理对象
// const personProxy = new Proxy(person, {
//   // 监视属性访问
//   //1.target is the person  property is the property will be obeserved
//   get(target,property){
//     // console.log(target, property) // { name: 'zce', age: 20 } 

//     // get的返回值就是访问的这个prop
//     // return 110
//     return property in target ? property :'default'
//   },
//   // observe set property
//   set(target,property,value){
//     if(property === 'age'){
//      if(!  Number.isInteger(value)){
//        throw TypeError(`${value} is not int`)
//      }
//     }
//     target[property] = value

//   }

// })

// console.log(personProxy.name); //110
// personProxy.age = 1
// console.log(personProxy);


// Proxy 对比 defineProperty
// 优势1:Proxy 可以监视读写以外的操作 --------------------------
// const person = {
//   name: 'zce',
//   age: 20
// }

// const personProxy = new Proxy(person,{
//   deleteProperty(target,property){
//     delete target[property]
//     console.log('-log delete', property)
//   }
// })

// delete personProxy.age
// console.log(person)

// 优势2:Proxy 可以很方便的监视数组操作 --------------------------
// const list = []
// const listProxy = new Proxy(list,{
//   set(target,property,value){
//     target[property] = value
//     console.log('set', property, value)
//     return true
//   }
// })
// 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)

ES2015 Reflect

Reflect共有 14个方法,现在废弃了1个,Reflect的成员方法都是基于proxy默认实现的。
Reflect的意义就是提供了统一一套的操作对象的API

// Reflect 对象

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

// const proxy = new Proxy(obj, {
//   get (target, property) {
//     console.log('watch logic~')
        // 如果没有定义get内部方法就默认返回 Reflect.get(target, property)
//     return Reflect.get(target, property)
//   }
// })

// console.log(proxy.foo)

// Reflect的意义就是提供了统一一套的操作对象的API

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

Promise

之前有写过详细的Promise源码分析文章这里就不多赘述了手写promise源码

ES2015 class 类

// class 关键词
// es6前用function代替
// 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()

ES2015 静态方法

class 方法中的this 指向之个class 而不是指向实例出来的对象

// static 方法

class 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()

ES2015 类的继承

// extends 继承

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

ES2015 Set

一个不重复的集合。add只有普通值不重复,重复对象和数组无法判断

// 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 的特点就是不会产生引用,
// 一旦数据销毁,就可以被回收,所以不会产生内存泄漏问题。

ES2015 Map

最大的差异就是 对象只能用字符串作为key 而map可以用任意类型作为key

// Map 数据结构

// const obj = {}
// obj[true] = 'value'
// obj[123] = 'value'
// obj[{ a: 1 }] = 'value'
//这里打印出来的[ '123', 'true', '[object Object]' ] 都变成字符串了
// console.log(Object.keys(obj))
//value
// 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 的特点就是不会产生引用,
// 一旦数据销毁,就可以被回收,所以不会产生内存泄漏问题。

ES2015 Symbol

es6前 对象的key都是字符串,字符串容易存在重复的可能。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()

ES2015 Symbol 补充

即使描述相同 但每一个symbol都是不一样的。Symbol.for方法可以使Symbol相同(内部是维护了一张全局注册表,提供了一一对应关系)

// Symbol 补充

// console.log(
//   // Symbol() === Symbol()
//   Symbol('foo') === Symbol('foo')
// )

// Symbol 全局注册表 ----------------------------------------------------

// const s1 = Symbol.for('foo')
// const s2 = Symbol.for('foo')
// console.log(s1 === s2)
//注意 由于内部会转换成字符串  即使传boolean也是true
// console.log(
//   Symbol.for(true) === Symbol.for('true')
// )

// 内置 Symbol 常量 ---------------------------------------------------

// console.log(Symbol.iterator)
// console.log(Symbol.hasInstance)

// const obj = {
//   [Symbol.toStringTag]: 'XObject'
// }
//如果直接从写toString可能会重复(其他地方用了 也会被影响),可以用symbol重写
// console.log(obj.toString())


// Symbol 属性名获取 ---------------------------------------------------

const obj = {
  [Symbol()]: 'symbol value',
  foo: 'normal value'
}
//无法获取symbol的名称,所有特别适合成为私有属性
// for (var key in obj) {
//   console.log(key)
// }
// console.log(Object.keys(obj))
// console.log(JSON.stringify(obj))
//只能获取symbol属性名
console.log(Object.getOwnPropertySymbols(obj))

ES2015 for…of 循环

for适合数组 for in 适合键值对 还有函数式的forEach等,但这些都有一定的局限性,for…of 循环以后会作为遍历所有数据类型的统一方式,理解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)
// }

ES2015 可迭代接口

// 迭代器(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)
}

ES2015 实现可迭代接口


const todos = {
  life: ['吃饭', '睡觉', '打豆豆'],
  learn: ['语文', '数学', '外语'],
  work: ['喝茶'],
  each: function(call){
    const all = [].concat(this.life, this.learn, this.work)
    for(const item of all){
      call(item)
    }
  },
  // 最外层实现iterable接口 返回迭代器的方法
  [Symbol.iterator]:function(){
    const all = [].concat(this.life, this.learn, this.work)
    let index = 0
    // 实现迭代器接口 iterator  内部需要有一个next方法
    return {
        next: function(){
          // IteratorResult 实现迭代结果接口
            return {
              value:all[index],
             done:index++ >= all.length
            }
        }
    }
  }
}

// todos.each(v=>{
//   console.log(v);
// })
console.log('-------------------------------')

for (const item of todos) {
  console.log(item)
}

ES2015 迭代器模式

// 迭代器设计模式

// 场景:你我协同开发一个任务清单应用

// 我的代码 ===============================

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

console.log('-------------------------------')

for (const item of todos) {
  console.log(item)
}

ES2015 生成器

// Generator 函数

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

// const result = foo()
// console.log(result) // 返回Object [Generator] {}
// console.log(result.next()) // 返回{ value: 100, done: true }
// 生成器对象也实现了iterator接口


function * foo(){
  console.log(1);
  yield 100 
  // yield不会结束方法的执行  yeild值作为next结果返回
  console.log(2);
  yield 200 
  console.log(3);
  yield 300 
}
console.log(foo().next());
// while(!foo().next().done){
//   foo().next()
//   console.log(1);
// }

ES2015 生成器应用

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

ES2015 ES Modules

ES2016 概述

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

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

// 指数运算符 ---------------------------------------------------

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

console.log(2 ** 10)

ES2017 概述

// 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)
// // console.log(descriptors)
// 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
// ]

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值