ES6基础知识

280 阅读25分钟

1.异步编程的实现方式?

异步编程的实现可以分为以下几种:

  1. 回调函数形式:通过回调函数来实现异步编程,让不同的任务按照顺序执行,前一个执行完毕才会执行下一个,但缺点是当任务量比较大时会产生层层嵌套的回调地狱问题,造成代码冗余复杂,难以维护。
  2. Promise对象:Promise可以解决回调地狱问题,通过new创建一个promise实例对象,接受一个回调函数作为参数,函数中接收两个状态函数分别为resolve和reject,代表状态由等待状态进入执行成功和执行失败状态。返回的promise对象可通过.then进行链式调用来实现后序的函数操作。
  3. generator+yield,*的函数执行后拥有了 next 函数,也就是说函数执行后返回了一个对象。每次调用 next 函数可以继续执行被暂停的代码。以下是 Generator 函数的简单实现,它可以在函数的执行过程中将执行权转移出去,在函数外部还可以将执行权转移回来。当我们遇到异步函数的时候将执行权转移出去,当异步函数执行完毕之后将执行权转移回来。这样我们在函数内部可以将异步语句以同步的方式来书写。
  4. async和await:普通函数前加上async可以将函数转化为异步函数,返回promise对象,函数内部执行到await语句时,如果语句返回promise对象则等当前语句执行完毕之后再向下执行。--遇到错误的时候用try-catch来捕获错误;

2.同步和异步?

同步就相当于是 当客户端发送请求给服务端,在等待服务端响应的请求时,客户端不做其他的事情。当服务端做完了才返回到客户端。这样的话客户端需要一直等待。用户使用起来会有不友好。---同步就是按照顺序执行任务,这段代码没执行完后面的代码就不会执行;

异步就是,当客户端发送给服务端请求时,在等待服务端响应的时候,客户端可以做其他的事情,这样节约了时间,提高了效率。

---扩展:promise和async函数是优化异步任务执行的操作;异步任务还没执行完就执行下面的代码了,那么如果你需要异步任务的一些执行结果,进行后续的操作,那要怎么办呢?于是就有了Promise、async这样的解决方案。

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

async await 是用来解决异步的,async函数是Generator函数的语法糖,async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await.

使用关键字async来表示,在函数内部使用 await 来表示异步

async函数返回一个 Promise 对象,可以使用then方法添加回调函数

当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句

async较Generator的优势:

(1)内置执行器。Generator 函数的执行必须依靠执行器,所以才有了co模块,而 Aysnc 函数自带执行器,调用方式跟普通函数的调用一样,async函数的执行只需要一行;

(2)更好的语义。async 和 await 相较于 * 和 yield 更加语义化;async表示函数里面有异步操作,await表示紧跟在后面的表达式需要等待结果;

(3)更广的适用性。co模块约定,yield命令后面只能是 Thunk 函数或 Promise对象,async函数的await后面可以是Promise也可以是原始类型的值

(4)返回值是 Promise。async 函数返回的是 Promise 对象,比Generator函数返回的Iterator对象方便,可以直接使用 then() 方法进行调用

await命令后面的 Promise 对象如果变为reject状态,则reject的参数会被catch方法的回调函数接收到。

async function f() {
  await Promise.reject('出错了');
}

f()
.then(v => console.log(v))
.catch(e => console.log(e))
// 出错了

注意,上面代码中,await语句前面没有return,但是reject方法的参数依然传入了catch方法的回调函数。这里如果在await前面加上return,效果是一样的。

任何一个await语句后面的 Promise 对象变为reject状态,那么整个async函数都会中断执行。

async function f() {
  await Promise.reject('出错了');
  await Promise.resolve('hello world'); // 不会执行
}

上面代码中,第二个await语句是不会执行的,因为第一个await语句状态变成了reject

有时,我们希望即使前一个异步操作失败,也不要中断后面的异步操作。这时可以将第一个await放在try...catch结构里面,这样不管这个异步操作是否成功,第二个await都会执行。

async function f() {
  try {
    await Promise.reject('出错了');
  } catch(e) {
  }
  return await Promise.resolve('hello world');
}

f()
.then(v => console.log(v))
// hello world

另一种方法是await后面的 Promise 对象再跟一个catch方法,处理前面可能出现的错误。

async function f() {
  await Promise.reject('出错了')
    .catch(e => console.log(e));
  return await Promise.resolve('hello world');
}

f()
.then(v => console.log(v))
// 出错了 
// hello world

以上是async await的使用简介。

4.什么是 Promise 对象

1.promise的含义:

Promise对象是异步编程的一种解决方法。Promise是一个构造函数,创建promise实例对象,接收一个回调函数作为参数,返回一个promise实例,该实例有三种状态,分别是pending、resolved(成功之后的回调函数)和rejected(失败之后的回调函数),状态只能由pending转变为resolved或者pending转变为rejected,状态改变之后就不会转变为其他状态。在异步任务之后通过调用resolved或rejected方法来转变状态,返回一个promise对象,通过.then链式调用的方式来定义resolved和rejected的回调函数。

在js里面,经常用异步的是ajax,比如sucess:一个回调,error一个回调。但是如果一次请求需要多个接口的时候就产生了回调地狱,promise可以用then来处理,它可以在一个then里面再写一个promise对象;如果成功就运行then方法处理函数,如果失败,会执行catch中的错误处理函数;

promise本身有race,all,reject,resolve这几个方法,原型上有then和catch方法;

Promise.prototype.then()

then的作用是为 Promise 实例添加状态改变时的回调函数。then方法的第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数。

then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。

Promise.prototype.catch()

Promise.prototype.catch()方法是.then(null, rejection).then(undefined, rejection)的别名,用于指定发生错误时的回调函数

方法返回一个 Promise 对象,如果该对象状态变为resolved,则会调用then()方法指定的回调函数;如果异步操作抛出错误,状态就会变为rejected,就会调用catch()方法指定的回调函数,处理这个错误。另外,then()方法指定的回调函数,如果运行中抛出错误,也会被catch()方法捕获。

promise.all

Promise.all可以将多个Promise实例包装成一个新的Promise实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值。

promise.race

返回最先执行结束的任务结果,Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。

promise的缺点:

  1. 无法取消promise,一旦新建它就会立即执行,无法中途取消;
  2. 如果不设置回调函数,Promise内部会抛出错误,不会反应到外部
  3. 处于pending状态时,不知道进展到哪个阶段;

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

reject 是用来抛出异常,catch 是用来处理异常

reject 是 Promise 的方法,而 catch 是 Promise 实例的方法

reject后的东西,一定会进入then中的第二个回调,如果then中没有写第二个回调,则进入catch

网络异常(比如断网),会直接进入catch而不会进入then的第二个回调

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

三个状态:

pending、fulfilled、reject

两个过程:

padding -> fulfilled、padding -> rejected当pending为rejectd时,会进入catch

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

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

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

console.log(4);

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

6.es5和es6的区别,说一下你所知道的es6

1)let声明变量和const声明常量,两个都有块级作用域ES5中是没有块级作用域的,并且var有变量提升,在let中,使用的变量一定要进行声明

2)箭头函数ES6中的函数定义不再使用关键字function(),而是利用了()=>来进行定义

3)模板字符串模板字符串是增强版的字符串,用反引号(`)标识,可以当作普通字符串使用,也可以用来定义多行字符串

4)解构赋值ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值

5)for of循环for...of循环可以遍历数组、Set和Map结构、某些类似数组的对象、对象,以及字符串

6)import、export导入导出ES6标准中,Js原生支持模块(module)。将JS代码分割成不同功能的小块进行模块化,将不同功能的代码分别写在不同文件中,各模块只需导出公共接口部分,然后通过模块的导入的方式可以在其他地方使用

7)set数据结构Set数据结构,类似数组。所有的数据都是唯一的,没有重复的值。它本身是一个构造函数

8)... 展开运算符可以将数组或对象里面的值转为用逗号分隔的参数列表;使用展开运算符可以展开数组,不需要用apply方法,将数组转化为函数的参数,还可以将多个值收集为一个变量

// ES5 的写法
function f(x, y, z) {
  // ...
}
var args = [0, 1, 2];
f.apply(null, args);

// ES6的写法
function f(x, y, z) {
  // ...
}
let args = [0, 1, 2];
f(...args);

下面是扩展运算符取代apply方法的一个实际的例子,应用Math.max方法,简化求出一个数组最大元素的写法。// ES5 的写法
Math.max.apply(null, [14, 3, 77])

// ES6 的写法
Math.max(...[14, 3, 77])

// 等同于
Math.max(14, 3, 77);

展开运算符的应用:可以复制数组;合并数组(都是浅拷贝);与解构赋值结合生成数组;将字符串转为真正的数组;(Array.from方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)

11)async、await使用 async/await, 搭配promise,可以通过编写形似同步的代码来处理异步流程, 提高代码的简洁性和可读性async 用于申明一个 function 是异步的,而 await 用于等待一个异步方法执行完成

12)Promise是异步编程的一种解决方案,比传统的解决方案(回调函数和事件)更合理、强大

7.var、let、const之间的区别

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

let 和 const 的注意点?

  1. let和const声明变量时不能变量提升。
  2. let和const有自己单独的作用域。
  3. 不允许重复声明,重复声明会报错。
  4. const声明的变量不允许修改其值,const声明的为常量;如果const后面是一个对象,那么改变对象的值,const的值也会改变,不会报错;


8.箭头函数:

使用箭头函数需要注意的地方

当只有一条语句的时候,{}和return可以省略掉;多条语句不能省略;

省略{}和return时,如果返回的内容是一个对象,对象要用()括号;

// 报错
let getTempItem = id => { id: id, name: "Temp" };

// 不报错
let getTempItem = id => ({ id: id, name: "Temp" });

(1)用了箭头函数,this就不是指向window,而是父级(this对象的指向是可变的,但是在箭头函数中是固定的,由于箭头函数没有自己的this,所以使用call,apply,bind都是无效的)

在箭头函数中,this的指向是固定的;this总是指向函数定义生效时所在的对象

(2)不能够使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。

(arguments该对象是类数组对象,引用着函数的实参,arguments.length是函数实参的个数,
arguments.callee引用函数自身)
arguments对象不是数组,而是一个类似数组的对象。所以为了使用数组的方法,
必须使用Array.prototype.slice.call,将其转为数组;

rest 参数就不存在这个问题,它就是一个真正的数组,数组特有的方法都可以使用---(ES6新增的rest参数:)
rest参数:形式是(...变量名),用来获取函数的多余的参数,这样不需要使用arguments对象;
rest参数的变量就是一个数组,该变量将多余的参数放入到了数组中去;

---类数组对象如何转换为数组对象?

1.array.from()方法
2.Array.prototype.slice.call(elems)方法转化为数组;
3.[…elems](展开运算符)方法转化为数组;
4.Array.prototype.forEach.call(elem,callback)方法

(3)不能用作构造函数,这就是说不能够使用new命令,否则会抛出一个错误

new操作符干了些什么?// (1)首先创建了一个新的空对象
// (2)设置原型,将对象的原型设置为函数的 prototype 对象。
// (3)让函数的 this 指向这个对象,那么熟属性和方法都被加入到this引用的对象中
// (4)新创建的独享由this引用,并且最后隐式的返回this;

(4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数

箭头函数不能用于构造函数,没有prototype属性,不能绑定arguments对象,不能用call和apply;


9.Symbol类型的注意点和Symbol值的强制类转换?

ES6 允许从符号到字符串的显式强制类型转换,然而隐式强制类型转换会产生错误。

Symbol 值不能够被强制类型转换为数字(显式和隐式都会产生错误),但可以被强制类型转换为布尔值
(显式和隐式结果都是 true )。

Symbol 类型的值不能转换为数字,会报错

Symbol 类型的注意点?

  • 1.Symbol 函数前不能使用 new 命令,否则会报错。
  • 2.Symbol 函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分。
  • 3.Symbol 作为属性名,该属性不会出现在 for...in、for...of 循环中,也不会被 Object.keys()、Object.getOwnPropertyNames()、JSON.stringify() 返回。
  • 4.Object.getOwnPropertySymbols 方法返回一个数组,成员是当前对象的所有用作属性名的 Symbol 值。
  • 5.Symbol.for 接受一个字符串作为参数,然后搜索有没有以该参数作为名称的 Symbol 值。如果有,就返回这个 Symbol 值,否则就新建并返回一个以该字符串为名称的 Symbol 值。

Symbol.for()与Symbol()这两种写法,都会生成新的 Symbol。它们的区别是,前者会被登记在全局环境中供搜索,
后者不会。Symbol.for()不会每次调用就返回一个新的 Symbol 类型的值,而是会先检查给定的key是否已经存在,
如果不存在才会新建一个值。比如,如果你调用Symbol.for("cat")30 次,每次都会返回同一个 Symbol 值,但是
调用Symbol("cat")30 次,会返回 30 个不同的 Symbol 值。Symbol.for("bar") === Symbol.for("bar")
// true

Symbol("bar") === Symbol("bar")
// false
上面代码中,由于Symbol()写法没有登记机制,所以每次调用都会返回一个不同的值

  • 6.Symbol.keyFor 方法返回一个已登记的 Symbol 类型值的 key。

let s1 = Symbol.for("foo");
Symbol.keyFor(s1) // "foo"

let s2 = Symbol("foo");
Symbol.keyFor(s2) // undefined

10.ES6的模板字符串有哪些新特性?并实现一个类模板字符串的功能

基本的字符串格式化。

将表达式嵌入字符串中进行拼接。

用${}来界定在ES5时我们通过反斜杠()来做多行字符串或者字符串一行行拼接。

ES6反引号(``)就能解决类模板字符串的功能

let name = 'web';
let age = 10;
let str = '你好,${name} 已经 ${age}岁了'
str = str.replace(/\$\{([^}]*)\}/g,function(){
return eval(arguments[1]);
})
console.log(str);//你好,web 已经 10岁了

11.介绍下 Set、Map、WeakSet、WeakMap的区别?

应用场景Set用于数据重组,Map用于数据储存,

Set:

set本身是一个构造函数,用来生成Set数据结构;向 Set 加入值的时候,不会发生类型转换,所以5"5"是两个不同的值。

Set内部使用Object.is()方法来判断两个数据项是否相等,唯一不同的是+0和-0在Set中被判断为是相等的。

// 去除数组的重复成员
[...new Set(array)]

Array.from方法可以将 Set 结构转为数组。这就提供了去除数组重复成员的另一种方法。

function dedupe(array) {
  return Array.from(new Set(array));
}

dedupe([1, 1, 2, 3]) // [1, 2, 3]

上面的方法也可以用于,去除字符串里面的重复字符。

[...new Set('ababbc')].join('')
// "abc"
  • Set.prototype.add(value):添加某个值,返回 Set 结构本身。
  • Set.prototype.delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
  • Set.prototype.has(value):返回一个布尔值,表示该值是否为Set的成员。
  • Set.prototype.clear():清除所有成员,没有返回值。

如果想在遍历操作中,同步改变原来的 Set 结构,目前没有直接的方法,但有两种变通方法。一种是利用原 Set 结构映射出一个新的结构,然后赋值给原来的 Set 结构;另一种是利用Array.from方法。

// 方法一
let set = new Set([1, 2, 3]);
set = new Set([...set].map(val => val * 2));
// set的值是2, 4, 6

// 方法二
let set = new Set([1, 2, 3]);
set = new Set(Array.from(set, val => val * 2));
// set的值是2, 4, 6

(1)成员不能重复

(2)只有键值没有键名,类似数组

(3)可以遍历,方法有add, delete,has

Map:

(1)相同的值的两个实例,在Map结构中被看成是两个键;

(2)本质上是键值对的集合,类似集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键

(3)可以遍历,可以跟各种数据格式转换

(4)使用set()方法可以给Map添加键值对;通过get()方法可以从Map中提取值;has(),delete()以及clear()方法


Set 和 WeakSet 结构?

  • 1.ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。
  • 2.WeakSet 结构与 Set 类似,也是不重复的值的集合。WeakSet 是一个构造函数,可以使用new命令,创建 WeakSet 数据结构。但是 WeakSet 的成员只能是对象,而不能是其他类型的值。WeakSet 中的对象都是弱引用,随时可能消失,遍历机制无法保证成员的存在,即垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说Weak Map之外不存在引用的时候,这个键值对会被移除;
  • Weak Set没有forEach()方法,不可以迭代,不能用于for-of循环;
  • Weak Set没有size属性;

【WeakSet 只能放置对象。

其次,WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中。

这是因为垃圾回收机制依赖引用计数,如果一个值的引用次数不为0,垃圾回收机制就不会释放这块内存。结束使用该值之后,有时会忘记取消引用,导致内存无法释放,进而可能会引发内存泄漏。WeakSet 里面的引用,都不计入垃圾回收机制,所以就不存在这个问题。因此,WeakSet 适合临时存放一组对象,以及存放跟对象绑定的信息。只要这些对象在外部消失,它在 WeakSet 里面的引用就会自动消失。

由于上面这个特点,WeakSet 的成员是不适合引用的,因为它会随时消失。另外,由于 WeakSet 内部有多少个成员,取决于垃圾回收机制有没有运行,运行前后很可能成员个数是不一样的,而垃圾回收机制何时运行是不可预测的,因此 ES6 规定 WeakSet 不可遍历】

Map 和 WeakMap 结构?

  • 1.Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。
  • 2.WeakMap 结构与 Map 结构类似,也是用于生成键值对的集合。但是 WeakMap 只接受对象作为键名( null 除外),不接受其他类型的值作为键名,使用非对象的键会抛出错误,而且这些对象都是弱引用,不会干扰到垃圾回收;Weak Map中的键在Weak Map之外不存在引用时,该键值对就会被移除;

12.什么是尾调用,使用尾调用有什么好处?

尾调用指的是函数的最后一步调用另一个函数。我们代码执行是基于执行栈的,所以当我们在一个函数里调用另一个函数时,我们会保留当前的执行上下文,然后再新建另外一个执行上下文加入栈中。使用尾调用的话,因为已经是函数的最后一步,所以这个时候我们可以不必再保留当前的执行上下文,从而节省了内存,这就是尾调用优化。但是 ES6 的尾调用优化只在严格模式下开启,正常模式是无效的。

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

class的引入:

js中没有明显类的面向对象方法,所以ES6中引入Class(类的概念),通过class关键字可以定义类;

es5的写法:

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

ES6的class可以看作是一个语法糖,它的绝大部分功能ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法

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

上面代码定义了一个“类”,可以看到里面有一个constructor方法,这就是构造方法,而this关键字则代表实例对象,使用时也是直接对类使用new命令,和构造函数用法一样;类的所有方法都是定义在类的prototype属性上面的;

注意:

(1)严格模式

类和模块的内部,默认就是严格模式,所以不需要使用use strict指定运行模式。只要你的代码写在类或模块之中,就只有严格模式可用。考虑到未来所有的代码,其实都是运行在模块之中,所以 ES6 实际上把整个语言升级到了严格模式。

(2)不存在提升

类不存在变量提升(hoist),这一点与 ES5 完全不同。

(3)this 的指向

类的方法内部如果含有this,它默认指向类的实例。

ES6与ES5继承的区别:

es6的写法:Class可以通过extends关键字实现继承,在子类的构造函数中只有调用super之后,才可以使用this关键字,否则会报错。这是因为子类实例的构建,是基于对父类实例加工,只有super方法才能返回父类实例。

//class实现类的继承  
class Point{
    constructor(x,y){
      this.x=x;
      this.y= y;
    }
  }
  class ColorPoint extends Point{
    constructor(x,y,color){
      super(x,y);//调用父类的constructor(x,y)
      this.color = color;
    }
    toString(){
      return this.color + '' + super.toString();//调用父类的toString;
    }
  }

ES5的继承是通过prototype或构造函数机制来实现。实质上是先创建子类的实例对象,然后再将父类的方法添加到this上(Parent.apply(this))。

ES5中,每一个对象都有__proto__属性,指向对应构造函数的prototype属性Class作为构造函数的语法糖,同时有prototype属性和__proto__属性,因此同时存在两条继承链;

  1. 子类的__proto__属性,表示构造函数的继承,总是指向父类的;
  2. 子类prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性;

ES6的继承机制实质上是先创建父类的实例对象this(所以必须先调用父类的super()方法,然后再用子类的构造函数修改this。具体为ES6通过class关键字定义类,里面有构造方法,类之间通过extends关键字实现继承。子类必须在constructor方法中调用super方法,否则新建实例报错。因为子类没有自己的this对象,而是继承了父类的this对象,然后对其调用。如果不调用super方法,子类得不到this对象。

注意:super关键字指代父类的实例,即父类的this对象。在子类构造函数中,调用super后,才可使用this关键字,否则报错。

具体参考:es6.ruanyifeng.com/#docs/class


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

事件循环中分为宏任务队列和微任务队列

其中setTimeout的回调函数放到宏任务队列里,等到执行栈清空以后执行

promise.then里的回调函数会放到相应宏任务的微任务队列里,等宏任务里面的同步代码执行完再执行

async函数表示函数里面可能会有异步方法,await后面跟一个表达式

async方法执行时,遇到await会立即执行表达式,然后把表达式后面的代码放到微任务队列里,让出执行栈让同步代码先执行


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

let name = Symbol('name');
let product = {
[name]:"洗衣机",
"price":799
};
Reflect.ownKeys(product);

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

let s = newSet();
s.add([1]);s.add([1]);
console.log(s.size);

答案:2

两个数组[1]并不是同一个值,它们分别定义的数组,在内存中分别对应着不同的存储地址,因此并不是相同的值都能存储到Set结构中,所以size为2

17.使用class 手写一个promise

//创建一个Promise的类
class Promise{
constructor(executer){//构造函数constructor里面是个执行器
this.status = 'pending';//默认的状态 pending
this.value = undefined//成功的值默认undefined
this.reason = undefined//失败的值默认undefined
//状态只有在pending时候才能改变
let resolveFn = value =>{
//判断只有等待时才能resolve成功
if(this.status == pending){
this.status = 'resolve';
this.value = value;
}
}
//判断只有等待时才能reject失败
let rejectFn = reason =>{
if(this.status == pending){
this.status = 'reject';
this.reason = reason;
}
}
try{
//把resolve和reject两个函数传给执行器executer
executer(resolve,reject);
}catch(e){
reject(e);//失败的话进catch
}
}
then(onFufilled,onReject){
//如果状态成功调用onFufilled
if(this.status = 'resolve'){
onFufilled(this.value);
}
//如果状态失败调用onReject
if(this.status = 'reject'){
onReject(this.reason);
}
}
}

18.如何使用Set去重

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

Var array = Array.from(new Set(array));

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];
}

答案:

let arr = [11,22,33,44,55];
let sum = 0;
for(value of arr){
sum += value;
}

19.说一下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';


参考:推荐阮一峰ES6教程:es6.ruanyifeng.com/#docs/funct…

www.jianshu.com/p/bf7565548…