js面试题之ES6新特性(2)

120 阅读7分钟

这是我参与11月更文挑战的第10天,活动详情查看:2021最后一次更文挑战

箭头函数Arrow functions

  • 语法上:简化函数的定义
// 箭头函数
const arr = [1,2,3,4,5,6]
// filter方法,接收回调函数
// const arr1 = arr.filter(function (item) {
//   return item % 2
// })
// 使用箭头函数简化
const arr1 = arr.filter(item => item % 2)
console.log(arr1) // 输出:[1,3,5]
  • 箭头函数与this
    • 箭头函数的this内部并没有,需要去外部找
  • setTimeout是在全局执行的,其中的thiswindow
// 箭头函数适用场景
const person = {
  name: 'zs',
  // sayHi方法内部的this,哪个对象调用就是哪个对象
  sayHi: function () {
    // 第1种. 存储外部的this,供setTimeout函数内部使用
    const _this = this
    // 因为setTimeout内部的this指向的是window,而window并没有name属性,而出错
    setTimeout(function () {
      // 输出:hi, my name is zs
      console.log(`hi, my name is ${_this.name}`)
    })
    // 第2种. 使用箭头函数
    setTimeout(() => {
      // 输出:hi, my name is zs
      console.log(`hi, my name is ${this.name}`)
    })
  }
}
// 调用
person.sayHi()
  • 适用:遇到需要_this来保存this的时候,就可以使用箭头函数来解决

对象字面量增强Enhanced object literals

  • 属性名和变量名相同时,可以省略变量名不写
  • 函数简化
  • 设置动态属性名(计算属性名)
// 字面量增强
// 1. 属性名和变量名相同时,可以简写
const age = 18
const sex = 'sex'
const obj = {
  name: 'zs',
  age,
  // 原来的写法
  // sayHi: function () {
  //   console.log('hi')
  // }
  // 2. 简化对象内部的函数书写
  sayHi () {
    console.log('hi')
  },
  // 3. 计算属性名
  [sex]: 'woman',
  [1 + 2]: 3
}
// 原来的写法:给对象设置动态属性,即计算属性名
obj[Math.random()] = 'num'

console.log(obj) // 输出:[3: 3, name: 'zs', age: 18, sayHi: ƒ, sex: 'woman', 0.9758498740468395: 'num']

对象扩展方法

  • Object.assign方法
    • 将多个源对象中的属性复制、覆盖(同名会覆盖目标对象中的属性值)到一个目标对象中
  • 适用场景(2种):简化对象属性的复制过程
// 第1种:希望内部更改时,不要改外部的对象
function fun(obj) {
  const newObj = Object.assign({},obj)
  newObj.age = 18
  console.log(newObj) // 输出:{name: 'zs', age: 18}
}

fun({
  name: 'zs'
})

// 第2种:在options对象参数接收时,进行简化
function Block(options) {
  Object.assign(this,options)
}
const block = new Block({
  width: 100,
  height: 100,
  x: 50,
  y: 50
})
console.log(block) // 输出:{width: 100, height: 100, x: 50, y: 50}
  • Object.is方法(不推荐使用)
// 原来的判断等于的方法
console.log(NaN === NaN) // false
// 使用object.is
console.log(Object.is(NaN,NaN)) // true

class类

  • 比之前通过“构造函数”方法创建更简洁
// 构造函数
function Person (name, age) {
  this.name = name,
  this.age = age
}
// 将方法设置在对象原型上
Person.prototype.sayHi = function () {
  console.log(`hi, my name is ${this.name}`)
}
const p = new Person('zs',18)
p.sayHi() // 输出:hi, my name is zs

// class类
class Person {
  constructor (name, age) {
    this.name = name,
    this.age = age
  }
  sayHi () {
    console.log(`hi, my name is ${this.name}`)
  }
}
const p1 = new Person('ls',20)
p1.sayHi() // 输出:hi, my name is ls

静态成员static

  • 类中的方法和属性都称之为成员,成员分为“实例成员”和“静态成员”
    • 实例成员,需要通过构造函数生成的对象去调用
    • 静态成员,直接通过类型本身去调用
  • ES2015中新增添加静态方法的关键词static
  • 静态方法中的this指向的不是某一个实例,而是这个类型整体
// class类
class Person {
  constructor (name, age) {
    this.name = name,
    this.age = age
  }
  sayHi () {
    console.log(`hi, my name is ${this.name}`)
  }
  // 使用静态方法
  static create (name, age) {
    // 静态方法中的this,指向的是Person这个类
    console.log(this) // 输出: class Person
    return new Person(name, age)
  }
}
const p1 = Person.create("mz", 45)
p1.sayHi() // 输出:hi, my name is mz
  • 注意:使用class类,内部多个之间不需要写逗号

class类静态方法.png

类的继承extends

// 类的继承
class Teacher {
  constructor (name, age) {
    this.name = name,
    this.age = age
  }
  sayHi () {
    console.log(`hi, my name is ${this.name}`)
  }
}

// 学生类 继承 教师父类
class Student extends Teacher{
  constructor (name, age, number) {
    // 继承父类的name,age
    super(name, age)
    // 设置自己独有的学号
    this.number = number
  }
  hello () {
    // 调用父类的方法
    super.sayHi()
    // 执行自己的代码
    console.log(`我的学号是: ${this.number}`)
  }
}

const wmz = new Student('wmz',13,20206789)
console.log(wmz) // 输出:{name: 'wmz', age: 13, number: 20206789}

set数据结构

  • 可以理解为“集合”,存放不重复的数据
  • s.size与数组的length是相同的原理,输出集合的数据长度
  • s.has()判断集合中是否存在某个值
  • s.delete()有返回值,表示是否删除成功
  • s.clear()删除集合中所有的数据

适用场景

  • 给数组中的数据进行元素去重
// 对数组进行去重
const arr = [4,1,5,7,3,5,9,10,4]
// 将数组进行去重操作,返回一个集合
const a = new Set(arr) // {size: 7, 4, 1, 5, 7, 3, 9, 10}
// 如果想要返回的是一个数组而不是集合
// 方法1:使用Array.from()方法
const a = Array.from(new Set(arr))  //[4, 1, 5, 7, 3, 9, 10]
// 方法2:使用展开操作符
const a = [...new Set(arr)] //[4, 1, 5, 7, 3, 9, 10]
console.log(a)

map数据结构

  • 本质上是键值对的集合
  • map.set():设置键和值
  • map.get():通过键获取其对应的值
  • map.has():通过键判断是否有对应的值,返回值true或false
  • map.delete():通过键删除某个数据,有返回值(删除不存在的数据,就会删除错误)
  • map.clear():清空整个map集合
  • 循环遍历
// map循环遍历
map.forEach((value, key) => {
  console.log(key, value)
})
// for..in遍历
for (const key in map) {
  console.log(key, map.get(key))
}

联系讲解

  • 键:是以“字符串”的形式存储的,因此当使用动态变量定义的键,最终会转换成字符串进行存储
const age = 18
const obj = {
  name: 'zs',
  age,
  sayHi () {
    console.log('hi')
  }
}
// 键:是一个对象
obj[{a : 1}] = "object"
// 调用对象的keys方法,获取当前对象的所有键
// 通过输出的结果,可以看出来 对象的键 都是以字符串形式进行保存
console.log(Object.keys(obj)) // 输出:(7) ['name', 'age', 'sayHi', '[object Object]']
  • 由上例可知,对象的键都会被转换成字符串类型,即对其执行toString方法,因此当对象的键为对象时,会被执行toString方法(所有的对象执行toString都会输出Object object),这样就出现了错误(多个键可以对应一个值)

    • 也就是 任意一个对象都可以来调用属性
    // 执行下列两个语句,都输出了object
    console.log(obj[{}])
    console.log(obj[{xx: 1}])
    
  • map为了解决上述问题,实现了键和值的一一映射

  • 与对象比较相似,最大的区别是:可以使用任意类型的数据当作键,而对象中只能使用字符串当作键

const map = new Map()
// 给map设置
const a = {a : 1}
map.set(a, 1)
map.set(1 + 2, 18)
console.log(map) // 输出:Map(2) {size: 2, {a: 1} => 1, 3 => 18}
console.log(map.get(a)) // 输出:1
console.log(map.get({})) // 输出: undeined

Symbol

  • 一种全新的原始数据类型
  • 最主要的作用:为对象添加独一无二的属性标识符
  • 通过symbol创建的元素都是唯一的

详细解析

  • 现在对象的属性名有两种类型:**String类型、Symbol**类型
  • 避免对象名重复
// 这样声明的Symbol不能修改
console.log(Symbol('foo')) // 输出:Symbol('foo')
console.log(Symbol('bar')) // 输出:Symbol('bar')
// 维护字符串与其的对应关系,非字符串会转成字符串
// for方法可以给Symbol类型的实例设置名字,可以修改
const a = Symbol.for(true)
const b = Symbol.for('true')
console.log(a === b) // 输出:true
  • 模拟实现对象的私有成员
const person = {
  name: 'xc',
  [Symbol()]: "Symbol value"
}
console.log(person.name) // 输出:xc
// 模拟实现对象的私有属性,私有属性不能被访问
console.log(person[Symbol()]) // 输出:undefined
  • 内置Symbol的常量,可以用来做内部方法的标识,通过这些标识可以让自定义的对象实现一些内置的接口
  • 经常会被忽视
const obj = {
  [Symbol()]: "Symbol value",
  foo: "foo value"
}
// 使用循环遍历,无法打印Symbol
for (var k in obj) {
  console.log(k) // 输出:foo
}
console.log(Object.keys(obj)) // 输出:['foo']
console.log(JSON.stringify(obj)) // 输出:{"foo":"foo value"}

// 只有一种方法可以输出Symbol,缺点:只能输出Symbol
console.log(Object.getOwnPropertySymbols(obj)) // 输出:[Symbol()]

ES2015其他内容

  • 可迭代接口
  • 迭代器模式
  • 生成器
  • Proxy代理对象
  • Reflect统一的对象操作API
  • Promise异步解决方案
  • ES Modules语言层面的模块化标准

ES2016概述

  • 小版本,2个更新

includes方法

  • 元素查找,推荐使用includes方法
// 元素查找includes
const arr = [1, true, NaN, 23, 'hi']
// 使用indexof 查找不到数组中的NaN
console.log(arr.indexOf(NaN))  // -1
console.log(arr.includes(NaN)) // true
  • 由上述实例可以看出:使用indexof方法时,不能检测出数组中有NaN。此时使用includes就可以解决。

指数运算符**

console.log(2 ** 3) // 输出:8