拉勾大前端培训笔记--ES6

93 阅读3分钟

ES6相关知识

由于很多开发者并没有理解语言和平台之间的关系,并且市面上ES相关的资料比较零散,并且我们开发者对ES的理解和掌握程度不尽相同,后续大前端的趋势和高阶课程都必须用到这些内容,所以学习ES很重要。 后续面试中面试闻到ES6新特性请参考以下知识。 具体知识如下:

  1. ECMAScript概述: JS是ECMAScript的扩展,实现了它的标准,并进行扩展,保证真实项目中能使用。 在浏览器环境:js相当于ES+WebAPI(DOM+BOM) 在node环境下:js相当于ES+node API(fs+net+etc..)
  2. ES2015
  • a. 解决原有语法的问题和不足
  • b. 对原有语法进行增强
  • c. 全新的对象,方法,功能
  • d. 全新的数据类型和数据结构
  1. let与块级作用域
// let 声明的成员只会在所声明的块中生效 -------------------------------------------
if (true) {
  // var foo = 'zce'
  let foo = 'zce'
  console.log(foo)
}
// 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()

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

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

// 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 = 'zce'

console.log(foo)
let foo = 'zce'

  • a. 花括号包含起来的范围就叫做块级作用域
  • b. 通过let声明的变量就是块级作用域的变量,外部是无法访问这个变量的
  • c. let并不会出现声明提升的问题
  1. const
  • a. const声明后不可被修改
const name = 'zce'
// 恒量声明过后不允许重新赋值
name = 'jack'

// 恒量要求声明同时赋值
const name
name = 'zce'

// 恒量只是要求内层指向不允许被修改
const obj = {}
// 对于数据成员的修改是没有问题的
obj.name = 'zce'

obj = {}

最佳实践:不用var,主用const,配合let 5. 数组的解构

// 数组的解构

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)

  • b. 通过[...rest]能获取当前元素后面的所有值
  1. 对象的解构
  • a. 通过这种方式const { name } = obj可以直接获取name的值
  • b. 其他的用法和数组相同
// 对象的解构

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

  1. 模板字符串
  • a. 使用反引号定义
  • b. 通过hey, ${name}这种方式可以将name变量直接添加到字符串中
  • c. 前面可以定义标签函数,可以对模板字符串进行加工
// 模板字符串

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

// 带标签的模板字符串

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

  1. 字符串的扩展方法
  • a. startsWith() 判断字符串是以什么开头的
  • b. incluses() 判断是否包含什么
  • c. endsWith() 判断是以什么结尾的
// 字符串的扩展方法

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

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

  1. 参数默认值
  • a. 直接实参后面 = 默认值,带默认值的参数放在最后
// 函数参数的默认值

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)

  1. 剩余参数
  • a. 之前使用的arguments,es6中可以直接使用...args这种方式定义。只能在最后一位,只能使用一次。
// 剩余参数

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

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

foo(1, 2, 3, 4)
  1. 数组展开
  • a. 通过apply传递第一个参数是this的指向,第二个就是要展开的数组
  • b. 也可以通过...的方式直接展开
// 展开数组参数
const arr = ['foo', 'bar', 'baz']

console.log(
  arr[0],
  arr[1],
  arr[2],
)
console.log.apply(console, arr)
console.log(...arr)
  1. 箭头函数
  • a. 使用=>来创建函数
// 箭头函数

// 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)
  • b. 箭头函数不会改变this的指向
  • c. 箭头函数中的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()
  1. 对象字面量的增强
  • a. 计算属性名是使用[]包起来
// 对象字面量

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)
obj.method1()
  1. 对象新增方法
  • a. assign方法是用后面对象中的属性去覆盖目标对象中的属性
  • b. is判断是否相等
  • c. definePropProperty
// 应用场景

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)
  1. proxy
  • a. 代理相当于门卫,监听对象的访问读取和设置
  • b. 其中包含get和set方法 image.png
// 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)
  }
})

c. 16. Reflect

  • a. 静态类,封装了一系列对对象的底层操作
// Reflect 对象

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))
  1. Promise
  • a. 全新的异步编程解决方案
  1. class
  • a. 独立声明类型的语法
class Person {
  constructor (name) {
    this.name = name
  }
  say () {
    console.log(`hi, my name is ${this.name}`)
  }
}

const p = new Person('tom')
p.say()
  1. 类的继承
// 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()

  1. 新增的set数据结构
  • a. 和数组非常类似,但是set内部的成员是不允许重复的
  • b. 其中包含has方法,判断集合中是否存在某个值
  1. Map数据结构
    • a. 和对象很像,严格意义上的键值对集合,可以用任意类型作为键值
  1. Symbol
  • a. 全新的数据类型,定义一个唯一的值
  • b. 用在对象的键上,避免对象的键重复
  • c. 用在对象的私有成员上,避免外部访问这个成员
  • d. 可以通过Symbol.for('')传进一个字符串标识,两个相同标识的Symbol的值是相同的。但是传进去的值都会自动转成字符串,传进去boolean的true和传进去字符串的true返回的值也是相同的
  1. for of
  • a. 可以通过break终止循环
  • b. 可以遍历set
  • c. 遍历map之后得到的是包含两个成员的数组
// 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
  }
}
  1. 可迭代接口
  • a. 是for of遍历的前提
  • b. 所有能被for of遍历的数据类型都需要实现这个Iterable,内部都会挂载一个iterator的方法
// 迭代器(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)
}
  1. 生成器
  • a. yeild函数不会直接执行,通过调用next方法会执行yeild
  • b. 会自动返回生成器对象,遇到yeild值会停止执行
  • c. 再次调用next会继续执行
  • d. 惰性执行
  1. 生成器函数案例
  • a. 发号器
// Generator 应用

// 案例1:发号器

function * createIdMaker () {
  let id = 1
  while (true) {
    yield id++
  }
}

const idMaker = createIdMaker()

console.log(idMaker.next().value)
console.log(idMaker.next().value)
console.log(idMaker.next().value)
console.log(idMaker.next().value)

// 案例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)
}
  • b. 实现iterator方法
  1. ES Modules
  2. ES2016
  • a. 新增includes方法
  • b. 指数运算符
  1. ES2017
  • a. Object的扩展方法
  • b. 函数参数末尾可以添加逗号
  • c. Async/Await