日常开发中的ES6+语法

1. 面试官:const、let、var区别

主要从以下几个方面区分:

  • 作用域不同(是否含有块级元素)
  • 是否存在暂时性死区
  • 是否存在变量提升
  • 能否重复声明
  • 变量能否被修改

1.作用域不同
块级作用域:声明的变量只在该代码块作用域内有效
var没有块级作用域,let、const有块级作用域

for (var i = 0; i < 5; i++) {
  console.log(i)
}
console.log('外层')
console.log(i)

在这里插入图片描述
以上代码var改为let

for (let i = 0; i < 5; i++) {
  console.log(i)
}
console.log('外层')
console.log(i)

在这里插入图片描述
有些人会有疑问,为什么日常开发中没有显式的声明块级作用域,let/const声明的变量却没有变为全局变量

在这里插入图片描述
这个其实也是let/const的特点,ES6规定它们不属于顶层全局变量的属性,这里用chrome调试一下。
在这里插入图片描述
可以看到使用let声明的变量x是在一个叫script作用域下的,而var声明的变量因为变量提升所以提升到了全局变量window对象中,这使我们能放心的使用新语法,不用担心污染全局的window对象

2.暂时性死区:只要块级作用域有let、const命令,他们所声明的变量就绑定这个区域,不受外部影响

var a = 10
if (true) {
  console.log(a)
  var a = 20
}
// 10

以上代码改为以下let或const均报错:

var a = 10
if (true) {
  console.log(a)
  let a = 20
}var a = 10
if (true) {
  console.log(a)
  const a = 20
}
// 报错:Uncaught ReferenceError: Cannot access 'a' before initialization

3.是否存在变量提升
var声明的变量存在变量提升,即变量可以在声明之前调用,值为undefined。
let和const不存在变量提升问题(注意这个‘问题’后缀,其实是有提升的,只不过是let和const具有一个暂时性死区的概念,即没有到其赋值时,之前就不能用),即它们所声明的变量一定要在声明后使用,否则报错。

console.log(a)
var a = 10
// undefined
console.log(a)
let a = 10 || const a = 10
// 报错:Uncaught ReferenceError: Cannot access 'a' before initialization

4.能否重复声明
var声明的可以重复声明, let和const在同一作用域不允许重复声明变量。其中const声明一个只读的常量(因为如此,其声明时就一定要赋值,不然报错)。一旦声明,常量的值就不能改变。

var a = 10
var a = 20
console.log(a) // 20
var a = 10
let a = 20 || const a = 20
console.log(a)  // 报错:Uncaught SyntaxError: Identifier 'a' has already been declared

5.变量能否被修改
①var、let声明的变量可以被修改,const声明的常量不可修改。当然如果声明的是一个引用类型(如对象),则不能改变它的内存地址,但是可以是对象内属性可变。 如何使const声明的对象内属性不可变,只可读呢?如果const声明了一个对象,对象里的属性是可以改变的。

var a = 10  || let a = 10
a = 20
console.log(a)  // 20
const a = 10
a = 20
console.log(a)  // 报错:Uncaught TypeError: Assignment to constant variable.

不能改变它的内存地址
在这里插入图片描述

//因为const声明的obj只是保存着其对象的引用地址,只要地址不变,就不会出错。
const obj={name:'蟹黄'};
obj.name='同学';
console.log(obj.name);//同学

使用Object.freeze(obj) 冻结obj,就能使其内的属性不可变,但它有局限,就是obj对象中要是有属性是对象,该对象内属性还能改变,要全不可变,就需要使用递归等方式一层一层全部冻结。

②const声明变量的时候必须赋值,否则会报错,同样使用const声明的变量被修改了也会报错
在这里插入图片描述

建议】 在日常开发中,我的建议是全面拥抱let/const,一般的变量声明使用let关键字,而当声明一些配置项(类似接口地址,npm依赖包,分页器默认页数等一些一旦声明后就不会改变的变量)的时候可以使用const,来显式的告诉项目其他开发者,这个变量是不能改变的(const声明的常量建议使用全大写字母标识,单词间用下划线),同时也建议了解var关键字的缺陷(变量提升,污染全局变量等),这样才能更好的使用新语法。

2. 面试官:你了解箭头函数吗?箭头函数和普通函数的有什么区别?

箭头函数的特点

(1) ES6 允许使用箭头 => 定义函数

var f = v => v

// 等同于 ES5 的
var f = function (v) {
  return v
}

(2) 如果箭头函数不需要参数或需要多个参数,就使用圆括号代表参数部分。

var f = () => 5
// 等同于 ES5 的
var f = function () {
  return 5
}

var sum = (numl, num2) => numl + num2
// 等同于 ES5 的
var sum = function (numl, num2) {
  return numl + num2
}

(3) 箭头函数使得表达更加简洁

const isEven = n => n % 2 === 0
const square = n => n * n

var result = values.sort((a, b) => a - b)
// 等同于 ES5 的
var result = values.sort(function (a, b) {
  return a - b
})

箭头函数的应用

应用一:替代ES5需要显示声明保存this的变量
箭头函数替代了以前需要显式的声明一个变量保存this的操作,使得代码更加的简洁。

ES5写法
在这里插入图片描述
ES6箭头函数:
在这里插入图片描述
应用二:数组链式方法迭代中

在数组的迭代中使用箭头函数更加简洁,并且省略了return关键字
在这里插入图片描述
应用三:箭头函数可以与解构结合使用

const full = ({ first , last }) => first + ' ' + last;
// 等同于 ES5 的
function full(person) {
  return person.first + ' ' + person.last;
}

应用四: React中的JSX语法:事件监听时传入箭头函数(推荐)
因为 onClick 中要求我们传入一个函数,那么我们可以直接定义一个箭头函数传入:

  • 传入的箭头函数的函数体是我们需要执行的代码,我们直接执行 this.btnClick();
  • this.btnClick()中通过this来指定会进行隐式绑定,最终this也是正确的;
class App extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      message: "你好啊,李银河"
    }
  }

  render() {
    return (
      <div>
        <button onClick={() => this.btnClick()}>点我一下(React)</button>
        <button onClick={() => this.btnClick()}>也点我一下(React)</button>
      </div>
    )
  }

  btnClick() {
    console.log(this);
    console.log(this.state.message);
  }
}

箭头函数与普通函数的主要区别

  1. 箭头函数和普通函数的样式不同,箭头函数语法更加简洁、清晰,箭头函数是=>定义函数,普通函数是function定义函数。

  2. 箭头函数会捕获其所在上下文的 this 值,作为自己的 this 值,定义的时候就确定并固定了。

  3. 箭头函数没有原型prototype,箭头函数不能作为构造函数使用,也不能使用new关键字(因为箭头函数没有自己的this,它的this其实是继承了外层执行环境中的this,且this指向永远不会改变,作为构造函数其的this要是指向创建的新对象)。

  4. 箭头函数没有自己的arguments。在箭头函数中访问arguments实际上获得的是外层局部(函数)执行环境中的值。(建议使用更好的语法,剩余运算符替代)

  5. call、apply、bind 并不会影响其 this 的指向。

  6. 箭头函数不能当作 Generator 函数,不能使用 yield 关键字。

[注意] 但是在某些情况下需要避免使用箭头函数,不要在可能改变this指向的函数中使用箭头函数

场景1
在这里插入图片描述
值得注意的是makeRequest后面的function不能使用箭头函数,因为这样它就会再使用上层的this,而再上层是全局的执行上下文,它的this的值会指向window,所以找不到变量a返回undefined。

场景2

类似Vue中的methods,computed中的方法,生命周期函数,Vue将这些函数的this绑定了当前组件的vm实例,如果使用箭头函数会强行改变this,因为箭头函数优先级最高(无法再使用call,apply,bind改变指向)
在这里插入图片描述

3.面试官:可以聊聊解构的日常使用场景吗?

解构 :是将一个数据结构分解为更小的部分的过程。ES6 中,从数组和对象中提取值,对变量进行赋值。

解构在日常开发中的使用场景

(1)简化变量声明操作

// ES5
var foo = 1
var bar = 2
var baz = 3

// ES6
let [foo, bar, baz] = [1, 2, 3]

(2)变量交换:
看起来如同镜像。赋值语句的左侧的解构模式,右侧是临时创建的数组字面量。x 被赋值为数组中的 y,y 被赋值为数组中的 x。

let x = 1;
let y = 2;
[x, y] = [y, x]
// x = 2, y = 1

(3)字符串解构

const [a, b, c, d, e] = 'hello'
// a => h
// b => e
// c => l
// d => l
// e => o

(4)对象解构

①常规解构

var obj = { x: 1, y: 2, c: 1 }
let { x, y } = obj
// x = 1
// y = 2

【注意】ES6的解构赋值虽然好用。但是要注意解构的对象不能为undefined、null,否则会报错。故在项目开发中最好给被解构的对象一个默认值。

const {a,b,c,d,e} = obj || {};

②动态对象键
假设您要解包其键是动态的对象。解构是不可能的,因为您不能确定键名的有效性吗?
不!对象键可以在解构时动态分配,提供额外的灵活性。
在这里插入图片描述

(5)函数参数解构

基础示例1

const xueyue = {
  name: '雪月',
  age: 18,
}

function getAge({ name, age }) {
  return `${name}今年${age}`
}

getAge(xueyue) // 雪月今年18岁

箭头函数版示例
在这里插入图片描述

①避免过多的函数参数

//错误示例
function myFunction(employeeName,jobTitle,yrExp,majorExp){
 return `${employeeName} is working as ${jobTitle} with ${yrExp}    years of experience in ${majorExp}`
}
//output be like John is working as Project Manager with 12 year of experience in Project Management
// you can call it via
console.log(myFunction("John","Project Manager",12,"Project Management"))
//    ***** PROBLEMS ARE *****
// Violation of 'clean code' principle
// Parameter sequencing is important
// Unused Params warning if not used
// Testing need to consider a lot of edge cases.
//正确示例
function myFunction({employeeName,jobTitle,yrExp,majorExp}){
 return `${employeeName} is working as ${jobTitle} with ${yrExp} years of experience in ${majorExp}`
}
//output be like John is working as Project Manager with 12 year of experience in Project Management
// you can call it via
const mockTechPeople = {
  employeeName:"John",
  jobTitle:"Project Manager",
  yrExp:12,
  majorExp:"Project Management"
}
console.log(myFunction(mockTechPeople))
// ES2015/ES6 destructuring syntax is in action
// map your desired value to variable you need.

②解构时重命名简化命名
有的后端返回的键名特别长,你可以这样干

// bad
setForm (data) {
    this.one = data.aaa_bbb_ccc_ddd
    this.two = data.eee_fff_ggg
}
// good
setForm ({aaa_bbb_ccc_ddd, eee_fff_ggg}) {
    this.one = aaa_bbb_ccc_ddd
    this.two = eee_fff_ggg
}

// best
setForm ({aaa_bbb_ccc_ddd: one, eee_fff_ggg: two}) {
    this.one = one
    this.two = two
}

③解构时设置默认值

// bad
setForm ({name, age}) {
    if (!age) age = 16
    this.name = name
    this.age = age 
}

// good
setForm ({name, age = 16}) {
    this.name = name
    this.age = age 
}

(6)忽略值
数组通常携带大量数据。很多时候,只需要部分数据进行进一步处理。因此,在解构数组时,您可以有选择地解压缩值,忽略不需要的值。如果您希望值保持不变,只需写一个逗号。
在这里插入图片描述
(7)分配剩余值
大多数开发人员都会知道 rest 参数。一个函数的参数以 3 个点为前缀,接受无数个参数并将它们解析为一个数组。
但是你知道休息模式也可以用于解构吗?通过在变量前加上 3 个点,您可以将所有剩余的值解包到其中。
这适用于对象和数组解构。尽管知道对于对象的提案目前处于第 4 阶段,这意味着它将正式包含在 ECMAScript 的下一次迭代中。
在这里插入图片描述
(8)组合数组和对象解构
对象和数组解构本身就很强大,但将两者结合起来可以为您提供开发超能力。如果您面对一个里面有对象的数组,您可以使用这种技术直接解包嵌套的对象。
在这里插入图片描述
(9)重命名变量

每个开发人员都见过至少可以说是模棱两可的对象键。键经常包含拼写错误或与它们的值没有明确的关系。为了克服这个问题,您可以在解构对象时设置一个新的变量名称。解压缩值时,只需写一个冒号,后跟新名称。
在这里插入图片描述
(10)默认值

如果在编写代码时有一个保证,那就是您不能信任数据。值可能会意外更改,并且无法避免边缘情况。

因此,提供默认值或回退值通常是一个好主意。解构时设置默认值非常简单。只需在变量名后写一个 = 符号,并提供默认值。当数组或对象中的值未定义时——任何另一个空值都将被解包——默认值被分配给变量。
在这里插入图片描述
(11)解构正则表达式
正则表达式用于定位字符串中的模式。当在 javascript 中执行正则表达式 (RegExp.exec() ) 时,匹配项将作为字符串数组返回。

使用数组解构,您可以直接将 RegExp 匹配解包到所需的变量。在下面的例子中,我们将一个邮件地址分解成不同的部分,并直接将它们分配给变量。
在这里插入图片描述
(12)嵌套解构

对象和数组并不总是平坦的。您知道您不需要单独的解构赋值来解压值吗?赋值可以嵌套在一个子句中以直接达到所需的级别。
在这里插入图片描述
1.10 个 JS 解构赋值相关的知识点
2.JavaScript 解构赋值实用指南

4.面试官:剩余参数 / 展开语法

剩余参数

ES6 引入了 rest 参数(形式为…变量名),用于获取函数的多余参数,这样就不需要使用 arguments 对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入其中。

比较下面的两种写法可以发现, rest 参数的写法更自然也更简洁。
例1:

function sortNumbers() {
  return Array.prototype.slice.call(arguments).sort()
}
// 使用 rest
const sortNumbers = (...numbers) => numbers.sort()

例2:
大家可能在实际开发中遇到过这种问题,一个函数,传入参数的个数是不确定的,这就可以用ES6的剩余参数

function fn (name, ...params) {
  console.log(name)
  console.log(params)
}
fn ('林三心', 1, 2) // 林三心 [ 1, 2 ]
fn ('林三心', 1, 2, 3, 4, 5) // 林三心 [ 1, 2, 3, 4, 5 ]

扩展运算符

扩展运算符( spread ) 是三个点(…) 如同 rest 参数的逆运算 将一个数组转为用逗号分隔的参数序列

console.log(...[1, 2, 3])
// 1 2 3

console.log(1, ...[2, 3, 4], 5)
// 1 2 3 4 5

(1)取代 apply 方法
由于扩展运算符可以展开数组,所以不再需要apply方法,将数组转为函数的参数了。

// ES5 的写法
function f(x, y, z) {
// ...
}
var args = [0, 1, 2];
f.apply(null, args);
// ES6 的写法
function f(x, y, z) {
// ...
}
var 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)

(2)合并数组
扩展运算符提供了数组合并的新写法。

//  ESS
[1, 2].concat(more)
// ES6
[1, 2, ...more]

(3)对象的拷贝
对象的扩展运算符(…)用于取出参数对象的所有可遍历属性,拷贝到当前对象之中。

let z = { a: 3, b: 'bb' }
let n = { ...z }
n // { a: 3, b: 'bb' }
n === z // false

【特别注意】: …扩展对象,只能做到当对象属性是 基本数据类型 才是 深拷贝,如果是 引用数据类型,那就是浅拷贝。

let z = { a: 3, b: 'bb', c: { name: 'ccc' } }
let n = { ...z }

n // { a: 3, b: 'bb', c: { name: 'ccc' } }
n === z // false
n.c === z.c // true
// n.c 跟 z.c 是同一个引用地址

5.面试官:操作对象的代码优化注意点

(1)对象字面量简写语法

const name = '雪月'

// ES5写法
const obj = {
  name: name,
  f: function () {
    console.log(this.name)
  },
}

// ES6简写
const obj2 = {
  name,
  f() {
    console.log(this.name)
  },
}

obj.f() // 雪月
obj2.f() // 雪月

使用 vue 的同学是不是感到很熟悉

new Vue({
  el: '#app',
  data() {
    return {
      list: [],
    }
  },
})

(2)关于获取对象属性值的吐槽

const name = obj && obj.name;

吐槽: ES6中的可选链操作符会使用么?

改进

const name = obj?.name;

(3)关于添加对象属性的吐槽

当给对象添加属性时,如果属性名是动态变化的,该怎么处理。

let obj = {};
let index = 1;
let key = `topic${index}`;
obj[key] = '话题内容';

吐槽: 为何要额外创建一个变量。不知道ES6中的对象属性名是可以用表达式吗?

改进

let obj = {};
let index = 1;
obj[`topic${index}`] = '话题内容';

7.面试官:模板字符串的使用

(1).关于拼接字符串的吐槽

const name = '小明';
const score = 59;
let result = '';
if(score > 60){
  result = `${name}的考试成绩及格`; 
}else{
  result = `${name}的考试成绩不及格`; 
}

吐槽
像你们这样用ES6字符串模板,还不如不用,你们根本不清楚在${}中可以做什么操作。在${}中可以放入任意的JavaScript表达式,可以进行运算,以及引用对象属性。

改进

const name = '小明';
const score = 59;
const result = `${name}${score > 60?'的考试成绩及格':'的考试成绩不及格'}`;

8.面试官:谈一谈Promise

参考阅读:
1.手写Promise:实现符合Promises/A+规范的Promise
2.【面试】1069- 前端必知必会的 10 道 Promise 面试题

一.简单介绍下Promise

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易。(当然了也可以简单介绍promise状态,有什么方法,callback存在什么问题等等,这个问题是比较开放的)

  • 提问概率:99%
  • 评分标准:人性化判断即可,此问题一般作为引入问题。
  • 加分项:熟练说出Promise具体解决了那些问题,存在什么缺点,应用方向等等

二.使用Promise进行顺序(sequence)处理。

  • 使用async函数配合await或者使用generator函数配合yield。
  • 使用promise.then通过for循环或者Array.prototype.reduce实现。
function sequenceTasks(tasks) {
    function recordValue(results, value) {
        results.push(value);
        return results;
    }
    var pushValue = recordValue.bind(null, []);
    return tasks.reduce(function (promise, task) {
        return promise.then(() => task).then(pushValue);
    }, Promise.resolve());
}
  • 提问概率:90%(我司提问概率极高的题目,即能考察面试者对promise的理解程度,又能考察编程逻辑,最后还有bind和reduce等方法的运用)
  • 评分标准:说出任意解决方法即可,其中只能说出async函数和generator函数的可以得到20%的分数,可以用promise.then配合for循环解决的可以得到60%的分数,配合Array.prototype.reduce实现的可以得到最后的20%分数。

三.Promise链上返回的最后一个Promise出错了怎么办?

catch在promise链式调用的末尾调用,用于捕获链条中的错误信息,但是catch方法内部也可能出现错误,所以有些promise实现中增加了一个方法done,done相当于提供了一个不会出错的catch方法,并且不再返回一个promise,一般用来结束一个promise链。

done() {
    this.catch(reason => {
      console.log('done', reason);
      throw reason;
    });
  }
  • 提问概率:90%(同样作为出题率极高的一个题目,充分考察面试者对promise的理解程度)
  • 加分项:给出具体的done()方法代码实现

四.Promise.then在Event Loop中的执行顺序。(可以直接问,也可以出具体题目让面试者回答打印顺序)

JS中分为两种任务类型:macrotask和microtask,其中macrotask包含:主代码块,setTimeout,setInterval,setImmediate等(setImmediate规定:在下一次Event Loop(宏任务)时触发);microtask包含:Promise,process.nextTick等(在node环境下,process.nextTick的优先级高于Promise)Event Loop中执行一个macrotask任务(栈中没有就从事件队列中获取)执行过程中如果遇到microtask任务,就将它添加到微任务的任务队列中,macrotask任务执行完毕后,立即执行当前微任务队列中的所有microtask任务(依次执行),然后开始下一个macrotask任务(从事件队列中获取) 浏览器运行机制可参考这篇文章

  • 提问概率:75%(可以理解为4次面试中3次会问到,顺便可以考察面试者对JS运行机制的理解)
  • 加分项:扩展讲述浏览器运行机制。

五.实现一个简单的,支持异步链式调用的Promise类。

这个答案不是固定的,可以参考最简实现 Promise,支持异步链式调用

  • 提问概率:50%(手撸代码题,因为这类题目比较耗费时间,一场面试并不会出现很多,所以出现频率不是很高,但却是必备知识)
  • 加分项:基本功能实现的基础上有onResolved/onRejected函数异步调用,错误捕获合理等亮点。

六.如何停止一个Promise链?

在要停止的promise链位置添加一个方法,返回一个永远不执行resolve或者reject的Promise,那么这个promise永远处于pending状态,所以永远也不会向下执行then或catch了。这样我们就停止了一个promise链。

Promise.cancel = Promise.stop = function() {
      return new Promise(function(){})
    }
  • 提问概率:50%(此问题主要考察面试者罗辑思维) (此题目,欢迎大家补充答案)

七.阐述Promise的一些静态方法。

Promise.deferred、Promise.all、Promise.race、Promise.resolve、Promise.reject等

  • 提问概率:25%(相对基础的问题,一般在其他问题回答不是很理想的情况下提问,或者为了引出下一个题目而提问)
  • 加分项:越多越好

Promise.allSettled 和 Promise.all有什么区别?

Promise.allSettled vs Promise.all探索异步操作

八.Promise存在哪些使用技巧或者最佳实践?

  1. 链式promise要返回一个promise,而不只是构造一个promise。
  2. 合理的使用Promise.all和Promise.race等方法。
  3. 在写promise链式调用的时候,then方法不传onRejected函数,只需要在最末尾加一个catch()就可以了,这样在该链条中的promise发生的错误都会被最后的catch捕获到。如果catch()代码有出现错误的可能,需要在链式调用的末尾增加done()函数。
  • 提问概率:10%(出题概率极低的一个题目)
  • 加分项:越多越好

九.Promise存在哪些缺点。

  1. 无法取消Promise,一旦新建它就会立即执行,无法中途取消。
  2. 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
  3. 吞掉错误或异常,错误只能顺序处理,即便在Promise链最后添加catch方法,依然可能存在无法捕捉的错误(catch内部可能会出现错误)
  4. 阅读代码不是一眼可以看懂,你只会看到一堆then,必须自己在then的回调函数里面理清逻辑。
  • 提问概率:25%(此问题作为提高题目,出现概率不高)
  • 加分项:越多越合理越好(网上有很多说法,不一一佐证) (此题目,欢迎大家补充答案)

9.面试官:async await对比promise的优缺点

async/await优缺点

async/await优点
a. 用同步的形式书写异步代码,代码阅读相对容易。
b. 对于条件语句和其他流程语句比较友好,可以直接写到判断条件里面。

function a() {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve(222)
      }, 2222)
    })
  };
async function f() {
    try {
      if ( await a() === 222) {
        console.log('yes, it is!') // 会打印
      }
    } catch (err) {
      // ...
    }
  }

c. 处理复杂流程时,在代码清晰度方面有优势

async/await缺点:

a. 无法直接处理promise返回的reject对象,要借助try…catch…
该缺点可以查看
1.如何优雅处理 async await 错误——解读小而美的 await-to-js 库

b. 用 await 可能会导致性能问题,因为 await 会阻塞代码, 也许之后的异步代码并不依赖于前者,但仍然需要等待前者完成,导致代码失去了并发性。

//promise
Promise.all([ajax1(), ajax2()])

c. try…catch…内部的变量无法传递给下一个try…catch…,Promise和then/catch内部定义的变量, 能通过then链条的参数传递到下一个then/catch,但是async/await的try内部的变量,如果用let和const定义则无法传递到下一个try…catch…,只能在外层作用域先定义好。

但async/await确确实实是解决了promise一些问题的。更加灵活的处理异步

推荐阅读7个Js async/await高级用法

promise的一些问题:

a. 一旦执行,无法中途取消,链式调用多个then中间不能随便跳出来。

b. 错误无法在外部被捕捉到,只能在内部进行预判处理.如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。

c. 吞掉错误或异常,错误只能顺序处理,即便在Promise链最后添加catch方法,依然可能存在无法捕捉的错误(catch内部可能会出现错误)
Promise内部如何执行,监测起来很难,当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
d.阅读代码不是一眼可以看懂,你只会看到一堆then,必须自己在then的回调函数里面理清逻辑。
1.如何优雅处理 async await 错误——解读小而美的 await-to-js 库

10.面试官:Set/Map/WeakSet/WeakMap

Map 和 Set 两种数据结构在ES6的作用

11.面试官: 前端实现如何列表搜索

在正式的项目开发中,前端一般负责实现一些没有分页的列表的搜索功能。
搜索一般分为精确搜索和模糊搜索,搜索也叫过滤。
一种是模糊搜索,一般用过滤器来实现:

const a = [1, 2, 3, 4, 5]
const result = a.filter((item) => {
  return item === 3
})
console.log('result', result)

但是,如果是精确搜索,则需要使用ES6中的find

const a = [1,2,3,4,5];
const result = a.find( 
  item =>{
    return item === 3
  }
)

推荐阅读:

  1. 90% 的前端都会使用 ES6 来简化代码,你都用过哪些?
  2. 近一万字的ES6语法知识点补充
  3. 我的代码简洁之道
  4. ES6 常用语法和开发总结(万字长文)
  5. 学习ES6笔记──工作中常用到的ES6语法
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值