ES6常考面试题总结

184 阅读11分钟

1.let有什么用,有了var为什么还要用let?

ES6之前没有块级作用域会带来很多问题,比如for循环var变量泄露,变量覆盖等问题。let 声明的变量拥有自己的块级作用域,且修复了var声明变量带来的变量提升问题。

2.var、let、const之间的区别?

var声明变量可以重复声明,而let不可以重复声明 var是不受限于块级的,而let是受限于块级 var会与window相映射(会挂一个属性),而let不与window相映射 var可以在声明的上面访问变量,而let有暂存死区,在声明的上面访问变量会报错 const声明之后必须赋值,否则会报错 const定义不可变的量,改变了就会报错 const和let一样不会与window相映射、支持块级作用域、在声明的上面访问变量会报错

3.箭头函数

(1)用了箭头函数,this就不是指向window,而是父级(指向是可变的) (2)不能够使用arguments对象 (3)不能用作构造函数,这就是说不能够使用new命令,否则会抛出一个错误 (4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数

4.ES6的模板字符串

5.Set、Map的区别

Set 是一种叫做集合的数据结构,Map 是一种叫做字典的数据结构 Set 和 Map 主要的应用场景在于 数据重组数据储存 集合 与 字典 的区别:

  • 共同点:集合、字典 可以储存不重复的值
  • 不同点:集合 是以** [value, value]**的形式储存元素,字典 是以 **[key, value] **的形式储存

集合(Set): ES6 新增的一种新的数据结构,类似于数组,成员唯一(内部元素没有重复的值)。且使用键对数据排序即顺序存储。 Set 本身是一种构造函数,用来生成 Set 数据结构。 Set 对象允许你储存任何类型的唯一值,无论是原始值或者是对象引用。

const s = new Set()
[1, 2, 3, 4, 3, 2, 1].forEach(x => s.add(x))
for (let i of s) {
    console.log(i)	// 1 2 3 4
}
// 去重数组的重复对象
let arr = [1, 2, 3, 2, 1, 1]
[... new Set(arr)]	// [1, 2, 3]

操作方法:

  • add(value):新增,相当于 array里的push。
  • delete(value):存在即删除集合中value。
  • has(value):判断集合中是否存在 value。
  • clear():清空集合。

便利方法:遍历方法(遍历顺序为插入顺序)

  • keys():返回一个包含集合中所有键的迭代器。
  • values():返回一个包含集合中所有值得迭代器。
  • entries():返回一个包含Set对象中所有元素得键值对迭代器。
  • forEach(callbackFn, thisArg):用于对集合成员执行callbackFn操作,如果提供了 thisArg 参数,回调中的this会是这个参数,没有返回值。

WeakSet: WeakSet 对象允许你将弱引用对象储存在一个集合中。 WeakSet 与 Set 的区别:

  • WeakSet 只能储存对象引用,不能存放值,而 Set 对象都可以。
  • WeakSet 对象中储存的对象值都是被弱引用的,即垃圾回收机制不考虑 WeakSet 对该对象的应用,如果没有其他的变量或属性引用这个对象值,则这个对象将会被垃圾回收掉(不考虑该对象还存在于 WeakSet 中),所以,WeakSet 对象里有多少个成员元素,取决于垃圾回收机制有没有运行,运行前后成员个数可能不一致,遍历结束之后,有的成员可能取不到了(被垃圾回收了),WeakSet 对象是无法被遍历的(ES6 规定 WeakSet 不可遍历),也没有办法拿到它包含的所有元素。

方法:

  • add(value):在WeakSet 对象中添加一个元素value。
  • has(value):判断 WeakSet 对象中是否包含value。
  • delete(value):删除元素 value。

字典(Map): 是一组键值对的结构,具有极快的查找速度。

const m = new Map()
const o = {p: 'haha'}
m.set(o, 'content')
m.get(o)	// content

m.has(o)	// true
m.delete(o)	// true
m.has(o)	// false

操作方法:

  • set(key, value):向字典中添加新元素。
  • get(key):通过键查找特定的数值并返回。
  • has(key):判断字典中是否存在键key。
  • delete(key):通过键 key 从字典中移除对应的数据。
  • clear():将这个字典中的所有元素删除。

遍历方法:

  • keys():将字典中包含的所有键名以迭代器形式返回。
  • values():将字典中包含的所有数值以迭代器形式返回。
  • entries():返回所有成员的迭代器。
  • forEach():遍历字典的所有成员。

WeakMap: WeakMap 对象是一组键值对的集合,其中的键是弱引用对象,而值可以是任意。 WeakMap 中,每个键对自己所引用对象的引用都是弱引用,在没有其他引用和该键引用同一对象,这个对象将会被垃圾回收(相应的key则变成无效的),所以,WeakMap 的 key 是不可枚举的。 方法:

  • has(key):判断是否有 key 关联对象。
  • get(key):返回key关联对象(没有则则返回 undefined)。
  • set(key):设置一组key关联对象。
  • delete(key):移除 key 的关联对象。

6.ECMAScript 6 怎么写 class ,为何会出现 class?

class是一个语法糖,其底层还是通过构造函数去创建的。

//定义类
class Point { 
  constructor(x,y) { 
      //构造方法
       this.x = x; //this关键字代表实例对象
       this.y = y; 
  	} 
  toString() {
       return '(' + this.x + ',' + this.y + ')'; 
  }
}

//底层构造函数
function Point(x, y) {
  this.x = x;
  this.y = y;
}

Point.prototype.toString = function () {
  return '(' + this.x + ', ' + this.y + ')';
};

var p = new Point(1, 2);

7.Promise构造函数是同步执行还是异步执行,那么 then 方法呢

promise构造函数是同步执行的,then方法是异步执行

8.setTimeout、Promise、Async/Await 的区别

事件循环中分为宏任务队列微任务队列 其中setTimeout的回调函数放到宏任务队列里,等到执行栈清空以后执行。 promise.then里的回调函数会放到相应宏任务的微任务队列里,等宏任务里面的同步代码执行完再执行。 async函数表示函数里面可能会有异步方法,await后面跟一个表达式。async方法执行时,遇到await会立即执行表达式,然后把表达式后面的代码放到微任务队列里,让出执行栈让同步代码先执行。

9.promise有几种状态,什么时候会进入catch?

三个状态: pending、fulfilled、reject 两个过程: padding -> fulfilled、padding -> rejected 当pending为rejectd时,会进入catch

10.下面的输出结果是多少

const promise = new Promise((resolve, reject) => {
    console.log(1);
    resolve();
    console.log(2);
})


promise.then(() => {
    console.log(3);
})


console.log(4);

//1 2 4 3
//Promise 新建后立即执行,所以会先输出 1,2,
//而 Promise.then()内部的代码在 当次事件循环的结尾立刻执行 ,所以会继续输出4,最后输出3

11.使用结构赋值,实现两个变量的值的交换

let a = 1;let b = 2;
[a,b] = [b,a];

12.设计一个对象,键名的类型至少包含一个symbol类型,并且实现遍历所有key

let name = Symbol('name');
 let product = {
    [name]:"洗衣机",    
    "price":799
  };
//静态方法 Reflect.ownKeys() 返回一个由目标对象自身的属性键组成的数组
  Reflect.ownKeys(product);

13.下面Set结构,打印出的size值是多少

let s = newSet();
s.add([1]);s.add([1]);
console.log(s.size); //2
//两个数组[1]并不是同一个值,它们分别定义的数组,在内存中分别对应着不同的存储地址,
//因此并不是相同的值都能存储到Set结构中,所以size为2

14.Promise 中reject 和 catch 处理上有什么区别

reject 是用来抛出异常,catch 是用来处理异常 reject 是 Promise 的方法,而 catch 是** Promise 实例的方法** reject后的东西,一定会进入then中的第二个回调,如果then中没有写第二个回调,则进入catch 网络异常(比如断网),会直接进入catch而不会进入then的第二个回调

15.手写Promise

class Promise{
    constructor (executor){
        this.state = 'pending'
        this.value = undefined
        this.reason = undefined

        this.onResolvedCallbacks = []
        this.onRejectedCallbacks = []

        let resolve = (value) =>{
            if(this.state === 'pending'){
                this.state = 'fulfilled'
                this.value = value
                this.onResolvedCallbacks.forEach(fn=>fn())
            }
        }

        let reject = (reason) =>{
            if(this.state === 'pending'){
                this.state = 'rejected'
                this.reason = reason
                this.onRejectedCallbacks.forEach(fn=>fn())
            }
        }

        try {
            executor(resolve,reject)
        } catch (err) {
            reject(err)
        }
    }

    then(onFulfilled,onRejected) {
        if(this.state === 'pending'){
            this.onResolvedCallbacks.push(()=>{
                onFulfilled(this.value)
            })

            this.onRejectedCallbacks.push(()=>{
                onRejected(this.reason)
            })
        }
    }
}

16.如何使用Set去重

let arr = [12,43,23,43,68,12];
let item = [...new Set(arr)];
console.log(item);//[12, 43, 23, 68]

17.将下面for循环改成for of形式

let arr = [11,22,33,44,55];
let sum = 0;
for(let i=0;i<arr.length;i++){
    sum += arr[i];
}
//修改代码
for(value of arr){
    sum += value;
}

18.理解 async/await以及对Generator的优势

async await 是用来解决异步的,async函数是Generator函数的语法糖 使用关键字async来表示,在函数内部使用 await 来表示异步 async函数返回一个 Promise 对象可以使用then方法添加回调函数 当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句 async较Generator的优势: (1)内置执行器。Generator 函数的执行必须依靠执行器,而 Aysnc 函数自带执行器,调用方式跟普通函数的调用一样 (2)更好的语义。async 和 await 相较于 * 和 yield **更加语义化 ** (3)更广的适用性。yield命令后面只能是 Thunk 函数或 Promise对象,async函数的await后面可以是Promise也可以是原始类型的值 (4)返回值是 Promise。async 函数返回的是 Promise 对象,比Generator函数返回的Iterator对象方便,可以直接使用 then() 方法进行调用

19.forEach、for in、for of三者区别

forEach更多的用来遍历数组 for in 一般常用来遍历对象或json for of数组对象都可以遍历,遍历对象需要通过和Object.keys() for in循环出的是key,for of循环出的是value

20.说一下es6的导入导出模块

//导入通过import关键字

// 只导入一个
import {sum} from "./example.js"
// 导入多个
import {sum,multiply,time} from "./exportExample.js"
// 导入一整个模块
import * as example from "./exportExample.js"

//导出通过export关键字

//可以将export放在任何变量,函数或类声明的前面
export var firstName = 'Michael';
export var lastName = 'Jackson';
export var year = 1958;

//也可以使用大括号指定所要输出的一组变量
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;
export {firstName, lastName, year};

//使用export default时,对应的import语句不需要使用大括号
let bosh = function crs(){}
export default bosh;
import crc from 'crc';

//不使用export default时,对应的import语句需要使用大括号
let bosh = function crs(){}
export bosh;
import {crc} from 'crc';复制代码

21.Proxy是什么,有什么作用?

Proxy是ES6新增的一个构造函数,可以理解为JS语言的一个代理,用来改变JS默认的一些语言行为,包括拦截默认的get/set等底层方法,使得JS的使用自由度更高,可以最大限度的满足开发者的需求。

22.Reflect是什么,有什么作用?

Reflect是ES6引入的一个新的对象,他的主要作用有两点,一是将原生的一些零散分布在Object、Function或者全局函数里的方法(如apply、delete、get、set等等),统一整合到Reflect上,这样可以更加方便更加统一的管理一些原生API。其次就是因为Proxy可以改写默认的原生API,如果一旦原生API别改写可能就找不到了,所以Reflect也可以起到备份原生API的作用,使得即使原生API被改写了之后,也可以在被改写之后的API用上默认的API。

23.Iterator(迭代器)是什么,有什么作用?

Iterator 是一种接口目的是为不同的数据结构提供统一的数据访问机制。也可以理解为 Iterator 接口主要为 for of 服务的,供for...of进行消费。 并不是所有的对象都能使用 for of,只有实现了 Iterator接口的对象才能够使用 for of 来进行遍历取值

  • getIterator方法返回一个对象 - 可迭代对象
  • 对象具有一个next 方法,next 方法内部通过闭包来保存指针 i 的值,每次调用 next 方法 i 的值都会+1.
  • 然后根据 i 的值从数组内取出数据作为 value,然后通过索引判断得到 done的值。
  • 当 i=3的时候,超过数组的最大索引,无可用数据返回,此时 done 为true,遍历完成。

ES6给Set、Map、Array、String都加上了[Symbol.iterator]方法,且[Symbol.iterator]方法函数也符合标准的Iterator接口规范,所以Set、Map、Array、String默认都是可以遍历的。

24.日常前端代码开发中,有哪些值得用ES6去改进的编程优化或者规范?

1、常用箭头函数来取代var self = this;的做法。 2、常用let取代var命令。 3、常用数组/对象的结构赋值来命名变量,结构更清晰,语义更明确,可读性更好。 4、在长字符串多变量组合场合,用模板字符串来取代字符串累加,能取得更好地效果和阅读体验。 5、用Class类取代传统的构造函数,来生成实例化对象。 6、在大型应用开发中,要保持module模块化开发思维,分清模块之间的关系,常用import、export方法。