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方法。