持续创作,加快生长!这是我参与「日新计划 10 月更文挑战」的第5天,点击检查活动概况

前言

又到了金九银十季,最近我也是奔走于各种面试。我自己总结整理了很多方向的前端面试题。借着国庆这个假日,也把这些标题总结分享给大家,也祝正在面试的朋友们能够拿到满意的offer。

往期文章

(1) 2022年我的面试万字总结(浏览器网络篇)

(2) 2022年我的面试万字总结(CSS篇)

(3) 2022年我的面试万字总结(HTML篇)

(4) 2022年我的面试万字总结(JS篇上)

(6) 2022年我的面试万字总结(代码篇)

(7) 2022年我的面试万字总结(Vue上)

(8) 2022年我的面试万字总结(Vue下)

(9) 2022年我的面试万字总结(Vue3+TS)

(10) 2022年我的面试万字总结(Node、webpack、功用优化)

(11) 2022年我的面试万字总结(小程序、git)

四、原型与承继

4.1说说面向方针的特性与特点

  • 封装性
  • 承继性
  • 多态

面向方针编程具有灵敏、代码可复用、简略保护和开发的有点、更合适多人协作的大型软件项目

4.2 说说你对工厂办法的了解

工厂办法是用来创立方针的一种最常用的规划办法,不露出创立方针的详细逻辑,而是将将逻辑封装在一个函数中,那么这个函数就能够被视为一个工厂

其就像工厂一样重复的发生相似的产品,工厂办法只需求咱们传入正确的参数,就能生产相似的产品

4.3 创立方针有哪几种办法?

  1. 字面量的办法直接创立方针

  2. 函数办法

    1. 工厂办法,工厂办法的主要作业原理是用函数来封装创立方针的细节,从而经过调用函数来抵达复用的意图。
    2. 结构函数办法
    3. 原型办法
    4. 结构函数办法+原型办法,这是创立自界说类型的最常见办法。
    5. 动态原型办法
    6. 寄生结构函数办法
  3. class创立

4.4 JS宿主方针和原生方针的差异

原生方针

独立于宿主环境的 ECMAScript 完结供给的方针

包括:Object、Function、Array、String、Boolean、Number、Date、RegExp、Error、EvalError、RangeError、ReferenceError、SyntaxError、TypeError、URIError

内置方针

开发者不必清晰实例化内置方针,它已被内部实例化了

相同是“独立于宿主环境”。而 ECMA-262 只界说了两个内置方针,即 Global 和 Math

宿主方针

BOM 和 DOM 都是宿主方针。由于其关于不同的“宿主”环境所展现的内容不同。其实说白了便是,ECMAScript 官方未界说的方针都归于宿主方针,由于其未界说的方针大多数是自己经过 ECMAScript 程序创立的方针

4.5 JavaScript 内置的常用方针有哪些?并列举该方针常用的办法?

Number 数值方针,数值常用办法

  • Number.toFixed( ) 选用定点计数法格式化数字
  • Number.toString( ) 将—个数字转换成字符串
  • Number.valueOf( ) 回来原始数值

String 字符串方针,字符串常用办法

  • Length 获取字符串的长度
  • split()将一个字符串切开数组
  • concat() 衔接字符串
  • indexOf()回来一个子字符串在原始字符串中的索引值。假如没有找到,则回来固定值 -1
  • lastIndexOf() 从后向前检索一个字符串
  • slice() 抽取一个子串

Boolean 布尔方针,布尔常用办法

  • Boolean.toString() 将布尔值转换成字符串
  • Boolean.valueOf() Boolean 方针的原始值的布尔值

Array 数组方针,数组常用办法

  • join() 将一个数组转成字符串。回来一个字符串
  • reverse() 将数组中各元素颠倒次第
  • delete 运算符只能删去数组元素的值,而所占空间还在,总长度没变(arr.length)
  • shift()删去数组中第一个元素,回来删去的那个值,并将长度减 1
  • pop()删去数组中最终一个元素,回来删去的那个值,并将长度减 1
  • unshift() 往数组前面增加一个或多个数组元素,长度会改动
  • push() 往数组结束增加一个或多个数组元素,长度会改动
  • concat() 衔接数组
  • slice() 切开数组,回来数组的一部分
  • splice()刺进、删去或替换数组的元素
  • toLocaleString() 把数组转换成局部字符串
  • toString()将数组转换成一个字符串
  • forEach()遍历一切元素
  • every()判别一切元素是否都契合条件
  • sort()对数组元素进行排序
  • map()对元素从头拼装,生成新数组
  • filter()过滤契合条件的元素
  • find() 查找 回来满意供给的测试函数的第一个元素的值。不然回来 undefined。
  • some() 判别是否有一个满意条件 ,回来布尔值
  • fill() 填充数组
  • flat() 数组扁平化

Function 函数方针,函数常用办法

  • Function.arguments 传递给函数的参数
  • Function.apply() 将函数作为一个方针的办法调用
  • Function.call() 将函数作为方针的办法调用
  • Function.caller 调用当时函数的函数
  • Function.length 已声明的参数的个数
  • Function.prototype 方针类的原型
  • Function.toString() 把函数转换成字符串

Object 根底方针,方针常用办法

  • Object 含有一切 JavaScript 方针的特性的超类
  • Object.constructor 方针的结构函数
  • Object.hasOwnProperty( ) 检查特点是否被承继
  • Object.isPrototypeOf( ) 一个方针是否是另一个方针的原型
  • Object.propertyIsEnumerable( ) 是否能够经过 for/in 循环看到特点
  • Object.toLocaleString( ) 回来方针的本地字符串表明
  • Object.toString( ) 界说一个方针的字符串表明
  • Object.valueOf( ) 指定方针的原始值

Date 日期时刻方针,日期常用办法

  • Date.getFullYear() 回来 Date 方针的年份字段
  • Date.getMonth() 回来 Date 方针的月份字段
  • Date.getDate() 回来一个月中的某一天
  • Date.getDay() 回来一周中的某一天
  • Date.getHours() 回来 Date 方针的小时字段
  • Date.getMinutes() 回来 Date 方针的分钟字段
  • Date.getSeconds() 回来 Date 方针的秒字段
  • Date.getMilliseconds() 回来 Date 方针的毫秒字段
  • Date.getTime() 回来 Date 方针的毫秒表明

Math 数学方针,数学常用办法

  • Math 方针是一个静态方针
  • Math.PI 圆周率
  • Math.abs() 绝对值
  • Math.ceil() 向上取整(整数加 1,小数去掉)
  • Math.floor() 向下取整(直接去掉小数)
  • Math.round() 四舍五入
  • Math.pow(x,y) 求 x 的 y 次方
  • Math.sqrt() 求平方根

RegExp 正则表达式方针,正则常用办法

  • RegExp.exec() 检索字符串中指定的值。回来找到的值,并确定其方位。
  • RegExp.test( ) 检索字符串中指定的值。回来 true 或 false。
  • RegExp.toString( ) 把正则表达式转换成字符串
  • RegExp.globa 判别是否设置了 “g” 修饰符
  • RegExp.ignoreCase 判别是否设置了 “i” 修饰符
  • RegExp.lastIndex 用于规矩下次匹配的开端方位
  • RegExp.source 回来正则表达式的匹配办法

Error 反常方针

  • Error.message 设置或回来一个过错信息(字符串)
  • Error.name 设置或回来一个过错名
  • Error.toString( ) 把 Error 方针转换成字符串

4.6 说一下hasOwnProperty、instanceof办法

hasOwnProperty() 办法会回来一个布尔值,指示方针自身特点中是否具有指定的特点(也便是,是否有指定的键)。

instanceof 运算符用于检测结构函数的 prototype 特点是否出现在某个实例方针的原型链上。

4.7 什么是原型方针,说说对它的了解

结构函数的内部的 prototype 特点指向的方针,便是结构函数的原型方针。

原型方针包括了能够由该结构函数的一切实例同享的特点和办法。当运用结构函数新建一个实例方针后,在这个方针的内部将包括一个指针(proto),这个指针指向结构函数的 原型方针,在 ES5 中这个指针被称为方针的原型。

4.8 什么是原型链

原型链是一种查找规矩

当拜访一个方针的特点时,假如这个方针内部不存在这个特点,那么它就会去它的原型方针里找这个特点,这个原型方针又会有自己的原型,所以就这样一向找下去,这种链式查找过程称之为原型链

4.9 原型链的结束是什么?

原型链的尽头是null。也便是Object.prototype.proto

4.10 Js完结承继的办法

1.原型链承继

要害:子类结构函数的原型为父类结构函数的实例方针

缺陷:1、子类结构函数无法向父类结构函数传参。

   2、一切的子类实例同享着一个原型方针,一旦原型方针的特点发生改动,一切子类的实例方针都会收影响

   3、假如要给子类的原型上增加办法,有必要放在Son.prototype = new Father()句子后边

  function Father(name) {
   this.name = name
   }
  Father.prototype.showName = function () {
   console.log(this.name);
   }
  function Son(age) {
   this.age = 20
   }
  // 原型链承继,将子函数的原型绑定到父函数的实例上,子函数能够经过原型链查找到复函数的原型,完结承继
  Son.prototype = new Father()
  // 将Son原型的结构函数指回Son, 不然Son实例的constructor会指向Father
  Son.prototype.constructor = Son
  Son.prototype.showAge = function () {
   console.log(this.age);
   }
  let son = new Son(20, '刘逍') // 无法向父结构函数里传参
  // 子类结构函数的实例承继了父类结构函数原型的特点,所以能够拜访到父类结构函数原型里的showName办法
  // 子类结构函数的实例承继了父类结构函数的特点,可是无法传参赋值,所所以this.name是undefined
  son.showName() // undefined
  son.showAge() // 20

2.借用结构函数承继

要害:用 .call() 和 .apply()办法,在子类结构函数中,调用父类结构函数

缺陷:1、只承继了父类结构函数的特点,没有承继父类原型的特点。

   2、无法完结函数复用,假如父类结构函数里边有一个办法,会导致每一个子类实例上面都有相同的办法。

    function Father(name) {
   this.name = name
   }
  Father.prototype.showName = function () {
   console.log(this.name);
   }
  function Son(name, age) {
   Father.call(this, name) // 在Son中借用了Father函数,只承继了父类结构函数的特点,没有承继父类原型的特点。
   // 相当于 this.name = name
   this.age = age
   }
  let s = new Son('刘逍', 20) // 能够给父结构函数传参
  console.log(s.name); // '刘逍'
  console.log(s.showName); // undefined

3.组合承继

要害:原型链承继+借用结构函数承继

缺陷:1、运用组合承继时,父类结构函数会被调用两次,子类实例方针与子类的原型上会有相同的办法与特点,糟蹋内存。

  function Father(name) {
   this.name = name
   this.say = function () {
    console.log('hello,world');
    }
   }
  Father.prototype.showName = function () {
   console.log(this.name);
   }
  function Son(name, age) {
   Father.call(this, name) //借用结构函数承继
   this.age = age
   }
  // 原型链承继
  Son.prototype = new Father() // Son实例的原型上,会有相同的特点,父类结构函数相当于调用了两次
  // 将Son原型的结构函数指回Son, 不然Son实例的constructor会指向Father
  Son.prototype.constructor = Son
  Son.prototype.showAge = function () {
   console.log(this.age);
   }
  let p = new Son('刘逍', 20) // 能够向父结构函数里传参
  // 也承继了父函数原型上的办法
  console.log(p);
  p.showName() // '刘逍'
  p.showAge() // 20

4.原型式承继

要害:创立一个函数,即将承继的方针经过参数传递给这个函数,最终回来一个方针,它的隐式原型指向传入的方针。 (Object.create()办法的底层便是原型式承继)

缺陷:只能承继父类函数原型方针上的特点和办法,无法给父类结构函数传参

  function createObj(obj) {
   function F() { }  // 声明一个结构函数
   F.prototype = obj  //将这个结构函数的原型指向传入的方针
   F.prototype.construct = F  // construct特点指回子类结构函数
   return new F    // 回来子类结构函数的实例
   }
  function Father() {
   this.name = '刘逍'
   }
  Father.prototype.showName = function () {
   console.log(this.name);
   }
  const son = createObj(Father.prototype)
  son.showName() // undefined  承继了原型上的办法,可是没有承继结构函数里的name特点

5.寄生式承继

要害:在原型式承继的函数里,给承继的方针上增加特点和办法,增强这个方针

缺陷:只能承继父类函数原型方针上的特点和办法,无法给父类结构函数传参

  function createObj(obj) {
   function F() { }
   F.prototype = obj
   F.prototype.construct = F
   F.prototype.age = 20 // 给F函数的原型增加特点和办法,增强方针
   F.prototype.showAge = function () {
    console.log(this.age);
    }
   return new F
   }
  function Father() {
   this.name = '刘逍'
   }
  Father.prototype.showName = function () {
   console.log(this.name);
   }
  const son = createObj(Father.prototype)
  son.showName() // undefined
  son.showAge() // 20

6.寄生组合承继

要害:原型式承继 + 结构函数承继

Js最佳的承继办法,只调用了一次父类结构函数

  function Father(name) {
   this.name = name
   this.say = function () {
    console.log('hello,world');
    }
   }
  Father.prototype.showName = function () {
   console.log(this.name);
   }
  function Son(name, age) {
   Father.call(this, name)
   this.age = age
   }
  Son.prototype = Object.create(Father.prototype) // Object.create办法回来一个方针,它的隐式原型指向传入的方针。
  Son.prototype.constructor = Son
  const son = new Son('刘逍', 20)
  console.log(son.prototype.name); // 原型上现已没有name特点了,所以这里会报错

7.混入承继

要害:利用Object.assign的办法多个父类函数的原型复制给子类原型

 function Father(name) {
   this.name = name
   }
  Father.prototype.showName = function () {
   console.log(this.name);
   }
​
  function Mather(color) {
   this.color = color
   }
  Mather.prototype.showColor = function () {
   console.log(this.color);
   }
​
  function Son(name, color, age) {
   // 调用两个父类函数
   Father.call(this, name)
   Mather.call(this, color)
   this.age = age
   }
  Son.prototype = Object.create(Father.prototype)
  Object.assign(Son.prototype, Mather.prototype) // 将Mather父类函数的原型复制给子类函数
  const son = new Son('刘逍', 'red', 20)
  son.showColor() // red

8. class承继

要害:class里的extends和super要害字,承继效果与寄生组合承继一样

  class Father {
   constructor(name) {
    this.name = name
    }
   showName() {
    console.log(this.name);
    }
   }
  class Son extends Father { // 子类经过extends承继父类
   constructor(name, age) {
    super(name)  // 调用父类里的constructor函数,等同于Father.call(this,name)
    this.age = age
    }
   showAge() {
    console.log(this.age);
    }
   }
  const son = new Son('刘逍', 20)
  son.showName() // '刘逍'
  son.showAge()  // 20

五、异步与事情循环

5.1. 异步编程的完结办法?

JavaScript中的异步机制能够分为以下几种:

  • 回调函数 的办法,运用回调函数的办法有一个缺陷是,多个回调函数嵌套的时分会造成回调函数阴间,上下两层的回调函数间的代码耦合度太高,不利于代码的可保护。
  • Promise 的办法,运用 Promise 的办法能够将嵌套的回调函数作为链式调用。可是运用这种办法,有时会造成多个 then 的链式调用,或许会造成代码的语义不行清晰。
  • generator 的办法,它能够在函数的履行过程中,将函数的履行权搬运出去,在函数外部还能够将履行权搬运回来。当遇到异步函数履行的时分,将函数履行权搬运出去,当异步函数履行结束时再将履行权给搬运回来。因而在 generator 内部关于异步操作的办法,能够以同步的次第来书写。运用这种办法需求考虑的问题是何时将函数的操控权搬运回来,因而需求有一个主动履行 generator 的机制,比方说 co 模块等办法来完结 generator 的主动履行。
  • async 函数 的办法,async 函数是 generator 和 promise 完结的一个主动履行的语法糖,它内部自带履行器,当函数内部履行到一个 await 句子的时分,假如句子回来一个 promise 方针,那么函数将会等待 promise 方针的状况变为 resolve 后再持续向下履行。因而能够将异步逻辑,转化为同步的次第来书写,并且这个函数能够主动履行。

5.2 并发与并行的差异?

  • 并发是微观概念,我别离有使命 A 和使命 B,在一段时刻内经过使命间的切换完结了这两个使命,这种状况就能够称之为并发。
  • 并行是微观概念,假设 CPU 中存在两个中心,那么我就能够同时完结使命 A、B。同时完结多个使命的状况就能够称之为并行。

5.3 setTimeout、setInterval、requestAnimationFrame的差异

  • setTimeout

履行该句子时,是当即把当时定时器代码推入事情行列,当定时器在事情列表中满意设置的时刻值时将传入的函数参加使命行列,之后的履行就交给使命行列负责。可是假如此时使命行列不为空,则需等待,所以履行定时器内代码的时刻或许会大于设置的时刻。

回来值timeoutID是一个正整数,表明定时器的编号。这个值能够传递给clearTimeout()来撤销该定时器。

  • setInterval

重复调用一个函数或履行一个代码片段,每次都精确的隔一段时刻推入一个事情(可是,事情的履行时刻不一定就不精确,还有或许是这个事情还没履行结束,下一个事情就来了)。它回来一个 interval ID,该 ID 仅有地标识时刻间隔,因而你能够稍后经过调用 clearInterval() 来移除定时器。

技术上,clearTimeout()clearInterval()能够交流。可是,为了防止混杂,不要混用撤销定时函数。

  • requestAnimationFrame

是JS完结动画的一种办法,它告诉浏览器——你希望履行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该办法需求传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前履行

5.4. 什么是回调阴间?回调阴间会带来什么问题?

回调函数的层层嵌套,就叫做回调阴间。回调阴间会造成代码可复用性不强,可阅览性差,可保护性(迭代性差),扩展性差等等问题。

Promise语法

5.5. Promise是什么

Promise是异步编程的一种处理方案,它是一个方针,能够获取异步操作的音讯,他的出现大大改善了异步编程的窘境,防止了阴间回调,它比传统的处理方案回调函数和事情更合理和更强壮。

promise自身仅仅一个容器,实在异步的是它的两个回调resolve()和reject()

promise本质 不是操控 异步代码的履行次第(无法操控) , 而是操控异步代码成果处理的次第

5.6 promise实例有哪些状况,怎样改动状况

(1)Promise的实例有三个状况:

  • Pending(进行中)
  • Resolved(已完结)
  • Rejected(已回绝)

当把一件事情交给promise时,它的状况便是Pending,使命完结了状况就变成了Resolved、没有完结失利了就变成了Rejected。

怎样改动 promise 的状况

  • resolve(value): 假如当时是 pending 就会变为 resolved
  • reject(error): 假如当时是 pending 就会变为 rejected
  • 抛出反常: 假如当时是 pending 就会变为 rejected

留意:一旦从进行状况变成为其他状况就永久不能更改状况了。

5.7 创立Promise实例有哪些办法

  • new Promise((resolve,reject)=>{})

一般状况下都会运用new Promise()来创立promise方针,可是也能够运用promise.resolvepromise.reject这两个办法:

  • Promise.resolve

Promise.resolve(value)的回来值也是一个promise方针,能够对回来值进行.then调用,代码如下:

Promise.resolve(11).then(function(value){
  console.log(value); // 打印出11
});
  • Promise.reject

Promise.reject 也是new Promise的快捷办法,也创立一个promise方针。代码如下:

Promise.reject(new Error(“出错了!!”));

5.8 Promise有哪些实例办法

then

then办法能够承受两个回调函数作为参数。第一个回调函数是Promise方针的状况变为resolved时调用,第二个回调函数是Promise方针的状况变为rejected时调用。其间第二个参数能够省掉。 then办法回来的是一个新的Promise实例(不是本来那个Promise实例)。因而能够选用链式写法,即then办法后边再调用另一个then办法。

catch

该办法相当于then办法的第二个参数,指向reject的回调函数。不过catch办法还有一个效果,便是在履行resolve回调函数时,假如出现过错,抛出反常,不会中止运转,而是进入catch办法中。

finally

finally办法用于指定不论 Promise 方针最终状况怎样,都会履行的操作。该办法是 ES2018 引进规范的。

下面是一个比方,服务器运用 Promise 处理恳求,然后运用finally办法关掉服务器。

server.listen(port)
  .then(function () {
  // ...
  })
  .finally(server.stop);

finally办法的回调函数不承受任何参数,这意味着没有办法知道,前面的 Promise 状况到底是fulfilled仍是rejected。这表明,finally办法里边的操作,应该是与状况无关的,不依赖于 Promise 的履行成果。

5.9 Promise有哪些静态办法

all

all办法能够完结并发使命, 它接纳一个数组,数组的每一项都是一个promise方针,回来一个Promise实例。当数组中一切的promise的状况都抵达resolved的时分,all办法的状况就会变成resolved,假如有一个状况变成了rejected,那么all办法的状况就会变成rejected

race

race办法和all一样,承受的参数是一个每项都是promise的数组,可是与all不同的是,当最早履行完的事情履行完之后,就直接回来该promise方针的值。假如第一个promise方针状况变成resolved,那自身的状况变成了resolved;反之第一个promise变成rejected,那自身状况就会变成rejected

any

它接纳一个数组,数组的每一项都是一个promise方针,该办法会回来一个新的 promise,数组内的恣意一个 promise 变成了resolved状况,那么由该办法所回来的 promise 就会变成resolved状况。假如数组内的 promise 状况都是rejected,那么该办法所回来的 promise 就会变成rejected状况,

resolve、reject

用来生成对应状况的Promise实例

5.10 Promise.all、Promise.race、Promise.any的差异

all: 成功的时分回来的是一个成果数组,而失利的时分则回来最早被reject失利状况的值

race: 哪个成果取得的快,就回来那个成果,不论成果自身是成功状况仍是失利状况。

any: 回来最快的成功成果,假如悉数失利就回来失利成果。

5.11 一个promise指定多个回调函数, 都会调用吗?

都会调用,成功状况放在then的第一个参数里调用

let p2 = new Promise((resolve, reject) => {
  resolve(1)
})
p2.then(value => {
  console.log('第一个', value)
})
p2.then(value => {
  console.log('第二个', value)
})

失利状况放在then的第二个参数里调用

let p3 = new Promise((resolve, reject) => {
  reject(2)
})
p3.then(
     ()=>{},
    value => {console.log('第一个', value)}
   )
p3.then(
     ()=>{},
    value => {console.log('第二个', value)}
   )

5.12 改动 promise 状况和指定回调函数谁先谁后?

  1. 都有或许, 正常状况下是先指定回调再改动状况, 但也能够先改状况再指定回调

  2. 怎样先改状况再指定回调?

    • 在履行器中直接调用 resolve()/reject()
    • 推迟更长时刻才调用 then()
  3. 什么时分才干得到数据?

    • 假如先指定的回调, 那当状况发生改动时, 回调函数就会调用, 得到数据
    • 假如先改动的状况, 那当指定回调时, 回调函数就会调用, 得到数据

5.13 promise.then()回来的新 promise 的成果状况由什么决议?

  1. 简略表达: 由 then()指定的回调函数履行的成果决议

  2. 详细表达:

    • 假如抛出反常, 新 promise 变为 rejected, 参数为抛出的反常
    • 假如回来的对错 promise 的恣意值, 新 promise 变为 resolved, value 为回来的值
    • 假如回来的是另一个新 promise, 此 promise 的成果就会成为新 promise 的成果

5.14 promise 怎样串连多个操作使命?

  • promise 的 then()回来一个新的 promise, 能够开成 then()的链式调用
  • 经过 then 的链式调用串连多个同步/异步使命

5.15 promise 反常传透是什么?

Promise 方针的过错具有“冒泡”性质,会一向向后传递,直到被捕获为止。也便是说,过错总是会被下一个catch句子捕获。

  • 当运用 promise 的 then 链式调用时, 能够在最终指定失利的回调,
  • 前面任何操作出了反常, 都会传到最终失利的回调中处理

5.16 怎样中止 promise 链?

  • 当运用 promise 的 then 链式调用时, 在中心中止, 不再调用后边的回调函数。 在回调函数中回来一个 pendding 状况的 promise 方针

5.17 promise有什么缺陷

代码层面

  • 无法撤销Promise,一旦新建它就会当即履行,无法中途撤销。
  • 假如不设置回调函数,Promise内部抛出的过错,不会反响到外部。
  • 当处于pending状况时,无法得知目前发展到哪一个阶段(刚刚开端仍是即将完结)。

语法层面

  • Promise尽管摆脱了回调阴间,可是then的链式调⽤也会带来额定的阅览担负
  • Promise传递中心值⾮常费事
  • Promise的调试很差,由于没有代码块,你不能在⼀个回来表达式的箭头函数中设置断点,假如你在⼀个.then代码块中使⽤调试器的步进(step-over)功用,调试器并不会进⼊后续的.then代码块,由于调试器只能盯梢同步代码的每⼀步。

async/await语法

5.18 async 函数是什么

  • 一句话归纳: 它便是 Generator 函数的语法糖,也便是处理异步操作的另一种高档写法

5.19 async 函数的完结原理

async 函数的完结原理,便是将 Generator 函数和主动履行器,包装在一个函数里。

async function fn(args) {
 // ...
}
​
// 等同于function fn(args) {
 return spawn(function* () { // spawn函数便是主动履行器
  // ...
  });
}

5.20 async函数的回来值

async函数回来一个 Promise 方针。

async函数内部return句子回来的值,会成为then办法回调函数的参数。

async函数内部抛出过错,会导致回来的 Promise 方针变为reject状况。抛出的过错方针会被catch办法回调函数接纳到。

5.21 await 到底在等待什么?

await 等待的是一个表达式,这个表达式的核算成果是 Promise 方针或许其它值(换句话说,便是没有特别限定)。await 不仅仅用于等 Promise 方针,它能够等恣意表达式的成果,所以,await 后边实践是能够接一般函数调用或许直接量的。

await 表达式的运算成果取决于它等的是什么。

  • 假如它比及的不是一个 Promise 方针,那 await 表达式的运算成果便是它比及的东西。
  • 假如它比及的是一个 Promise 方针,await 就忙起来了,它会阻塞后边的代码,等着 Promise 方针 resolve,然后得到 resolve 的值,作为 await 表达式的运算成果。

5.22 什么是顶层await?

从 ES2022 开端,答应在模块的顶层独立运用await指令,使得上面那行代码不会报错了。它的主要意图是运用await处理模块异步加载的问题。

import { AsyncFun } from 'module'
await AsyncFun()
console.log(123)

5.23 怎样用await让程序中止指定的时刻(休眠效果)

JavaScript 一向没有休眠的语法,可是借助await指令就能够让程序中止指定的时刻

function sleep(interval) {
 return new Promise(resolve => {
  setTimeout(resolve, interval);
  })
}
​
// 用法
async function one2FiveInAsync() {
 for(let i = 1; i <= 5; i++) {
  console.log(i);
  await sleep(1000);
  }
}
​
one2FiveInAsync();

5.24 await的运用留意点

  1. await指令后边的Promise方针,运转成果或许是rejected,所以最好把await指令放在try...catch代码块中。
  2. 多个await指令后边的异步操作,假如不存在继发联系,最好让它们同时触发。
  3. await指令只能用在async函数之中,假如用在一般函数,就会报错。
  4. async 函数能够保存运转仓库。

5.25 async语法怎样捕获反常

async函数内部的反常能够经过 .catch()或许 try/catch来捕获,差异是

  • try/catch 能捕获一切反常,try句子抛出过错后会履行catch句子,try句子内后边的内容不会履行
  • catch()只能捕获异步办法中reject过错,并且catch句子之后的句子会持续履行
async函数过错捕获,以登录功用为例
   async function getCatch () {
    await new Promise(function (resolve, reject) {
     reject(new Error('登录失利'))
     }).catch(error => {
     console.log(error) // .catch()能捕获到过错信息
     })
    console.log('登录成功') //  可是成功信息也会履行
    }
   
   async function getCatch () {
    try {
     await new Promise(function (resolve, reject) {
      reject(new Error('登录失利'))
      })
     console.log('登录成功') // try抛出过错之后,就不会履行这条句子
     } catch (error) {
     console.log(error) //  catch句子能捕获到过错信息
     }
    }

5.26 async/await比照Promise的优势

  • 代码读起来愈加同步,Promise尽管摆脱了回调阴间,可是then的链式调⽤也会带来额定的阅览担负
  • Promise传递中心值⾮常费事,⽽async/await⼏乎是同步的写法,⾮常优雅
  • 过错处理友爱,async/await能够⽤成熟的try/catch,Promise的过错捕获⾮常冗余
  • 调试友爱,Promise的调试很差,由于没有代码块,你不能在⼀个回来表达式的箭头函数中设置断点,假如你在⼀个.then代码块中使⽤调试器的步进(step-over)功用,调试器并不会进⼊后续的.then代码块,由于调试器只能盯梢同步代码的每⼀步。

事情循环Event Loop

5.27 JS的履行机制(同步使命、异步使命)

JS是一门单线程言语,单线程就意味着,一切的使命需求排队,前一个使命结束,才会履行下一个使命。这样所导致的问题是:假如JS履行的时刻过长,这样就会造成页面的烘托不连贯,导致页面烘托加载阻塞的觉。为了处理这个问题,JS中出现了同步和异步。

同步使命:即主线程上的使命,按照次第由上⾄下顺次执⾏,当时⼀个使命执⾏结束后,才干执⾏下⼀个使命。

异步使命:不进⼊主线程,⽽是进⼊使命行列的使命,履行结束之后会发生一个回调函数,并且告诉主线程。当主线程上的使命履行完后,就会调取最早告诉自己的回调函数,使其进入主线程中履行。

5.28 什么是Event Loop

  • 事情循环Event Loop又名事情行列,两者是一个概念

事情循环指的是js代码所在运转环境(浏览器、nodejs)编译器的一种解析履行规矩。事情循环不归于js代码自身的领域,而是归于js编译器的领域,在js中讨论事情循环是没有意义的。换句话说,js代码能够了解为是一个人在公司中详细做的事情, 而 事情循环 相当所以公司的一种规章制度。 两者不是一个层面的概念。

5.29 宏使命与微使命的概念与差异

为了和谐使命有条有理地在主线程上履行,页面进程引进了 音讯行列事情循环机制,烘托进程内部也会保护多个音讯行列,比方推迟履行行列和一般的音讯行列。然后主线程选用一个 for 循环,不断地从这些使命行列中取出使命并履行使命。这些音讯行列中的使命就称为 宏使命

微使命是一个需求异步履行的回调函数,履行时机是在主函数履行结束之后、当时宏使命结束之前。当 JS 履行一段脚本(一个宏使命)的时分,V8 会为其创立一个大局履行上下文,在创立大局履行上下文的同时,V8 引擎也会在内部创立一个 微使命行列。也便是说 每个宏使命都关联了一个微使命行列

5.30 常见的宏使命与微使命别离有哪些

使命(代码) 宏/微 使命 环境
宏使命 浏览器
事情 宏使命 浏览器
网络恳求(Ajax) 宏使命 浏览器
setTimeout() 定时器 宏使命 浏览器/Node
fs.readFile() 读取文件 宏使命 Node
Promise.then() 微使命 浏览器/Node
async/await 微使命 浏览器/Node

5.31 事情循环Event Loop履行机制

1.进入到script标签,就进入到了第一次事情循环.

2.遇到同步代码,当即履行

3.遇到宏使命,放入到宏使命行列里.

4.遇到微使命,放入到微使命行列里.

5.履行完一切同步代码

6.履行微使命代码

7.微使命代码履行结束,本次行列清空

8.寻找下一个宏使命,重复过程1

5.32 为什么Js是单线程?

Js是单线程,可是浏览器是多线程。单线程是为了防止UI操作混乱,一切和UI操作相关的开发言语都应该是单线程。

5.33 代码题易考点

  1. promise自身是一个同步的代码,只需它后边调用的then()办法里边的回调才是微使命
  2. then办法需求Promise里的resolve传值才会履行
  3. await右边的表达式仍是会当即履行,表达式之后的代码才是微使命, await微使命能够转换成等价的promise微使命分析
  4. script标签自身是一个宏使命, 当页面出现多个script标签的时分,浏览器会把script标签作为宏使命来解析

六、 ES6-ES2022新语法

6.1 ES6(ES2015)

1. 说说let和const

let:

  • 声明变量
  • 没有变量提高
  • 不行重复声明
  • 具有块级效果域
  • 声明变量后能够在运用时赋值

const:

  • 只读常量
  • 没有变量提高
  • 不行重复声明
  • 具有块级效果域
  • 声明变量后有必要立马赋值

2. let、const、var的差异

(1)块级效果域: 块效果域由 { }包括,let和const具有块级效果域,var不存在块级效果域。块级效果域处理了ES5中的两个问题:

  • 内层变量或许掩盖外层变量
  • 用来计数的循环变量走漏为大局变量

(2)变量提高: var存在变量提高,let和const不存在变量提高,即在变量只能在声明之后运用,否在会报错。

(3)给大局增加特点: 浏览器的大局方针是window,Node的大局方针是global。var声明的变量为大局变量,并且会将该变量增加为大局方针的特点,可是let和const不会。

(4)重复声明: var声明变量时,能够重复声明变量,后声明的同名变量会掩盖之前声明的遍历。const和let不答应重复声明变量。

(5)暂时性死区: 在运用let、const指令声明变量之前,该变量都是不行用的。这在语法上,称为暂时性死区。运用var声明的变量不存在暂时性死区。

(6)初始值设置: 在变量声明时,var 和 let 能够不必设置初始值。而const声明变量有必要设置初始值。

(7)指针指向: let和const都是ES6新增的用于创立变量的语法。 let创立的变量是能够更改指针指向(能够从头赋值)。但const声明的变量是不答应改动指针的指向。

差异 var let const
是否有块级效果域 ✔️ ✔️
是否存在变量提高 ✔️
是否增加大局特点 ✔️
能否重复声明变量 ✔️
是否存在暂时性死区 ✔️ ✔️
是否有必要设置初始值 ✔️
能否改动指针指向 ✔️ ✔️

3. 解构赋值

方针解构

  • 办法:const { x, y } = { x: 1, y: 2 }
  • 默许:const { x, y = 2 } = { x: 1 }
  • 改名:const { x, y: z } = { x: 1, y: 2 }

数组解构

  • 规矩:数据结构具有Iterator接口可选用数组办法的解构赋值
  • 办法:const [x, y] = [1, 2]
  • 默许:const [x, y = 2] = [1]

函数参数解构

  • 数组解构:function Func([x = 0, y = 1]) {}
  • 方针解构:function Func({ x = 0, y = 1 } = {}) {}

应用场景:

  • 交流变量值:[x, y] = [y, x]
  • 回来函数多个值:const [x, y, z] = Func()
  • 界说函数参数:Func([1, 2])
  • 提取JSON数据:const { name, version } = packageJson
  • 界说函数参数默许值:function Func({ x = 1, y = 2 } = {}) {}
  • 遍历Map结构:for (let [k, v] of Map) {}
  • 输入模块指定特点和办法:const { readFile, writeFile } = require("fs")

留意点

  • 匹配办法:只需等号两边的办法相同,左面的变量就会被赋予对应的值
  • 解构赋值规矩:只需等号右边的值不是方针或数组,就先将其转为方针
  • 解构默许值收效条件:特点值严厉等于undefined
  • 解构遵循匹配办法
  • 解构不成功时变量的值等于undefined
  • undefinednull无法转为方针,因而无法进行解构

4. 字符串的新增办法

includes

回来布尔值,表明是否找到了参数字符串。

startsWith

回来布尔值,表明参数字符串是否在原字符串的头部。

endsWith

回来布尔值,表明参数字符串是否在原字符串的尾部。

repeat

repeat办法回来一个新字符串,表明将原字符串重复n

5. 打开运算符

(2)数组扩展运算符

数组的扩展运算符能够将一个数组转为用逗号分隔的参数序列,且每次只能打开一层数组。

const arr = [1, 2, 3, 4, 5, 6]
const newArr = [...arr] // 复制数组
const arr1 = ['two', 'three'];const arr2 = ['one', ...arr1] // 兼并数组
console.log(Math.max.call(null, ...arr)) // 将数组中的每一项作为参数运用

** (3) rest(剩下)参数**

扩展运算符被用在函数形参上时,它还能够把一个分离的参数序列整组成一个数组

function mutiple(...args) {
 let result = 1;
 for (var val of args) {
  result *= val;
  }
 return result;
}
mutiple(1, 2, 3, 4) // 24

6. 数组新增的办法

Array.from()

将类数组或许可迭代方针创立为一个新的数组,不改动原数组并回来这个新数组

Array.of()

创立一个具有可变数量参数的新数组实例,示例代码如下:

Array.of(1) // [1]
Array.of(true, 1, '刘逍') // [true, 1, '刘逍']

findIndex

根据给定的回调函数,找到匹配的第一个元素的索引,找不到回来-1

find

根据给定的回调函数,找到匹配的第一个元素,找不到回来undefined

fill

将给定值填充数组,示例代码如下:

const arr = [1, 2, 3, 4]
// 将给定值填充索引1-3
arr.fill('逍', 1, 3) // [ 1, '逍', '逍', 4 ]

keys

回来一个可迭代的方针,其内容为数组的key,示例代码如下:

const arr = [1, true, '逍']
const keys = arr.keys()
for (const i of keys) {
 console.log(i) // 遍历成果 0 1 2
}

values

回来一个可迭代的方针,其内容为数组的valu*,示例代码如下:

const arr = [1, true, '逍']
const values = arr.values()
for (const i of values) {
 console.log(i) // 遍历成果 1 true 逍
}

entries

回来一个可迭代的方针,其内容是一个数组,索引0为原数组的元素,1为原数组该方位的值,示例代码如下:

const arr = [1, true, '逍']
​
const iterator = arr.entries()
console.log(Array.from(iterator)) // [ [ 0, 1 ], [ 1, true ], [ 2, '逍' ] ]

7. 方针新增办法

Object.is() (用于处理NaN ≠= NaN,+0 === -0的问题)

用于比较两个值是否持平,用于处理NaN ≠= NaN,+0 === -0的问题,示例代码如下:

console.log(NaN === NaN) // false
console.log(+0 === -0) // trueconsole.log(Object.is(NaN, NaN)) // true
console.log(Object.is(+0, -0)) // false

Object.assign()

一切可枚举特点的值从一个或多个源方针复制到方针方针,并回来方针方针,示例代码如下:

const person = Object.assign({}, { name: '刘逍' }, { age: 18 })
console.log(person) // { name: '刘逍', age: 18 }

Object.getPrototypeOf()

获取原型方针;

Object.setPrototypeOf()

设置原型方针。

8.class类

JS里的类便是结构函数的语法糖

根本用法

  1. 类里边有个constructor函数,能够接纳传递过来的参数,同时回来实例方针
  2. constructor函数只需new生成实例时,就会主动调用这个函数,假如咱们不写这个函数,类也会主动生成这个函数
  3. 公共特点放在constructor中,公共办法直接在类里边写函数声明,会主动增加至原型方针中
  4. class类没有变量提高,所以有必要先界说类,才干经过类实例化方针
  5. super()调用父类里的constructor办法,能够向里边传参,就等于Father.call(this,x,y),super 有必要在子类的this前面调用
  6. class里边的办法的this指向的是调用者,假如调用者不是类的实例,就需求改动this的指向
class Person {
 constructor(age) {
  // 特点
  this.myName = '刘逍'
  this.age = age
  }
 // 静态办法
 static print() {
  console.log()
  }
 // 拜访器
 get myName() {
  console.log('getter')
  return this.myName
  }
 set myName(v) {
  console.log('setter' + v)
  }
 setName(v) {
  this.myName = v
  }
}

要害词

constructor

constructor()办法是类的默许办法,经过new指令生成方针实例时,主动调用该办法。一个类有必要有constructor()办法,假如没有显式界说,一个空的constructor()办法会被默许增加。

super

super这个要害字,既能够当作函数运用,也能够当作方针运用。

super作为函数调用时,代表父类的结构函数。子类的结构函数有必要履行一次super函数。super尽管代表了父类A的结构函数,可是回来的是子类B的实例

super作为方针时,在一般办法中,指向父类的原型方针;在静态办法中,指向父类。

getter、setter

在“类”的内部能够运用getset要害字,对某个特点设置存值函数和取值函数,阻拦该特点的存取行为。

static

假如在一个办法前,加上static要害字,就表明该办法不会被实例承继,而是直接经过类来调用,这就称为“静态办法”。

# 私有特点

ES2022正式为class增加了私有特点,办法是在特点名之前运用#表明。私有特点只能在类的内部运用(this.#count)。假如在类的外部运用,就会报错。

9. 模块化

ES6中答应咱们运用export导出模块,运用import引进模块

10. Symbol

ES6 引进了一种新的原始数据类型Symbol,表明绝无仅有的值。它归于 JavaScript 言语的原生数据类型之一

Symbol 值经过Symbol()函数生成。这便是说,方针的特点名现在能够有两种类型,一种是本来就有的字符串,另一种便是新增的 Symbol 类型。但凡特点名归于 Symbol 类型,就都是绝无仅有的,能够确保不会与其他特点名发生冲突。

11. Iterator

Iterator即迭代器,它是一种接口,为各种不同的数据结构供给了共同的拜访机制,换句话说,只需有任何数据结构布置了迭代接口,就能够运用共同的办法的来遍历它。

完结可迭代接口的数据结构,一般都自身完结或承继了以Symbol.iterator特点的,就归于可迭代方针。Symbol.iterator特点自身是一个函数,便是当时数据结构默许的遍历器生成函数。

一个包括next()办法的方针,才干够称为一个迭代方针。next()方针的会有回来一个方针,方针中包括两个值,如下所示:

  • value:迭代器回来的任何JavaScript值。donetrue时可省掉。
  • done:一个布尔值,为false时表明迭代未中止,为true时当即中止迭代器,且能够省掉value的值。

Iterator 的效果有三个:

  1. 为各种数据结构,供给一个共同的、简洁的拜访接口;
  2. 使得数据结构的成员能够按某种次第摆放;
  3. ES6 发明了一种新的遍历指令for...of循环,Iterator 接口主要供for...of消费。

12. for…of..循环

for...of循环,作为遍历一切数据结构的共同的办法。

一个数据结构只需布置了Symbol.iterator特点,就被视为具有 iterator 接口,就能够用for...of循环遍历它的成员。也便是说,for...of循环内部调用的是数据结构的Symbol.iterator办法。

for...of循环能够运用的规模包括数组、Set 和 Map 结构、某些相似数组的方针(比方arguments方针、DOM NodeList 方针)、后文的 Generator 方针,以及字符串。

13. Generator

Generator是ES2015中供给的一种异步编程处理方案,界说Generator函数在function要害字和函数名中心运用*星号,函数内部运用yield要害字界说不同的状况。

async的底层便是Generator函数

14. Proxy和Reffect

Proxy 能够了解成,在方针方针之前架设一层“阻拦”,外界对该方针的拜访,都有必要先经过这层阻拦,因而供给了一种机制,能够对外界的拜访进行过滤和改写。Proxy 这个词的原意是署理,用在这里表明由它来“署理”某些操作,能够译为“署理器”。

Proxy方针用于创立一个署理方针,从而完结根本操作的阻拦和自界说,根本操作包括13种,如下表所示:

阻拦 ⽅法 触发⽅式
get(target, propKey, receiver) 读取某个特点
set(target, propKey, value, receiver) 写⼊某个特点
has(target, propKey) in操作符
deleteProperty(target, propKey) delete操作符
getPrototypeOf(target) Object.getPropertypeOf()
setPrototypeOf(target, proto) Object.setPrototypeOf()
isExtensible(target) Object.isExtensible()
preventExtensions(target) Object.preventExtensions()
getOwnPropertyDescriptor(target, propKey) Object.getOwnPropertyDescriptor()
defineProperty(target, propKey, propDesc) Object.defineProperty()
ownKeys(target) Object.keys()Object.getOwnPropertyNames()Object.getOwnPropertySymbols()
apply(target, thisArg, args) 调⽤⼀个函数
construct(target, args) ⽤ new 调⽤⼀个函数

Vue3便是根据Proxy进行编写的

Reflect是ECMAScript2015供给的一个方针,它供给了一些阻拦JavaScript操作的静态办法,这些办法与Proxy中的handlers中的办法共同。

Reflect并不是一个结构函数,也便是说它不能够被实例化。

Proxy方针中的每一个阻拦操作(例如:getdelete等),内部都对应的调用了Reflect的办法。它供给的静态办法与Proxy中的handlers中的办法称号都共同

15. Set、Map、WeakSet、WeakMap

SetMapWeakSetWeakMap是ES2015中新增的几个方针:

set相似于数组,可是成员的值都是仅有的,没有重复的值。

Set自身是一个结构函数,用来生成 Set 数据结构。

SetWeakSet与数组相似,精确的它他们是调集,这两者的差异便是Set能够存储任何数据类型,而WeakSet只能存储方针的引证,并且是弱引证;

Set方针在实践开发中最常见的便是完结数据去重,示例代码如下:

const arr = [1, 2, 2, 3, 4, 3, 5]
const set = new Set(arr)
// set方针能够运用 ... 打开 一切项
console.log([...set]) // [ 1, 2, 3, 4, 5 ]

map相似于方针,也是键值对的调集,可是“键”的规模不限于字符串,各种类型的值(包括方针)都能够当作键。也便是说,Object 结构供给了“字符串—值”的对应,Map 结构供给了“值—值”的对应,是一种更完善的 Hash 结构完结。假如你需求“键值对”的数据结构,Map 比 Object 更适宜。

MapWeakMap与方针相似,存储办法是键值对办法的,这两者的差异Map的键值对都是能够是恣意的而WeakMap键有必要是方针的引证而值能够是恣意类型的。

6.2 ES2016

1. 指数运算符

ES2016中新增指数**,也叫幂运算符,与Math.pow()有着一样的功用,示例代码如下:

console.log(2 ** 10 === Math.pow(2, 10)) // true

2. Array.prototype.includes()办法

在ES2016中在数组原型上增加了includes()办法,该办法用于判别一个数组中是否包括指定的值,回来一个布尔值,示例代码如下:

const arr = [1, 2, 3, 4, 5, NaN]
console.log(arr.indexOf(NaN)) // -1
console.log(arr.includes(NaN)) // true

值得留意的是运用includes()NaNNaN+0-0是持平的。

6.3 ES2017

1. 方针新增办法

  • Object.values():回来一个给定方针自身的一切可枚举特点值的数组;
  • Object.entries():回来一个给定方针自身可枚举特点的键值对数组;
  • Object.getOwnPropertyDescriptors():回来给定方针一切自有特点的特点描述符。

2. 字符串新增办法

  • padStart():在字符串开头填充空格;
  • padEnd():在字符串结束填充空格;

6.4 ES2018

1. 异步迭代

在ES2018中新增了for await...of句子,该用于能够遍历异步可迭代方针

2.方针扩展运算符

方针的扩展运算符(…)用于取出参数方针中的一切可遍历特点,复制到当时方针之中。

let bar = { a: 1, b: 2 };
let baz = { ...bar }; // { a: 1, b: 2 }

上述办法实践上等价于:

let bar = { a: 1, b: 2 };
let baz = Object.assign({}, bar); // { a: 1, b: 2 }

Object.assign办法用于方针的兼并,将源方针(source)的一切可枚举特点,复制到方针方针(target)Object.assign办法的第一个参数是方针方针,后边的参数都是源方针。(假如方针方针与源方针有同名特点,或多个源方针有同名特点,则后边的特点会掩盖前面的特点)

3. Promise.prototype.finally

finally()办法会回来一个Promise方针,当promise的状况改动,不论是变成rejected或许fulfilled,最终都会履行finally()的回调。

6.5 ES2019

1. try...catch:句子中的catch答应不运用参数

2. trimStart、trimLeft、trimEnd、trimRight

  • String.prototype.trimStart:用于去除字符串左面的空格;
  • String.prototype.trimLeft:它是trimStart的别号
  • String.prototype.trimEnd:用于去除字符串右边的空格;
  • String.prototype.trimRight:它是trimEnd的别号

3. Object.fromEntries

Object.fromEntries()办法把键值对列表转换为一个方针,是Object.entries()办法的反操作

6.6 ES2020

1. 动态导入

动态导入,也便是咱们需求该模块的时分才会进行加载,这能够削减开销和页面加载时刻,示例代码如下:

import('/modules/my-module.js').then(module => {
 // Do something with the module.
})

动态导入运用import()办法,它回来一个Promise。

在ES2020中,还为import增加一个meta方针,该方针给JavaScript模块露出了特定上下文的元数据特点的方针。

2. BigInt数据类型

BigInt的出现时处理JavaScript中答应的最大数字是2**53-1的问题,BigInt 能够表明恣意大的整数。

3. 空值兼并运算符 ??

该运算符与逻辑或运算符相似。其核算规矩为,只需左运算元为null或许undefined,则回来右运算元,不然回来左运算元。而逻辑或运算符只需左运算元转换为boolean类型后为false,就回来右运算元。

4. 可选链操作符 ?.

?. 操作符的功用相似于 . 链式操作符,不同之处在于,在引证为空 (nullish ) (null 或许 undefined) 的状况下不会引起过错,该表达式短路回来值是 undefined

当咱们拜访某个特点时,只需有一处不存在,就会回来undefind,不会报错。

var A = {}
​
// console.log(A.a.b) // 报错
​
console.log(A.a?.b) // undefined

可选链操作符也可用于方针下办法的调用,示例代码如下:

var obj = {}
​
// 假如存在 obj.fun() 这个办法,下面则会直接调用,假如不存在则会回来undefined
obj.fun?.A()

6.7 ES2021

1. String.prototype.replaceAll

replaceAll()办法回来一个新字符串,新字符串的内容是经过替换的,实例代码如下:

const str = '刘逍'
const newStr = str.replaceAll('逍', '小')
console.log(newStr) // 刘小

2. 数值分隔符 _

严厉意义上讲数值分隔符(_)并不归于一个运算符,其效果便是使数字愈加利于阅览,例如下面的代码

console.log(1_0000_0000) // 100000000

3. Promise.any()

ES2021中新增的Promise.any()办法,它承受的参数和与promise.all()是共同的,仅有不同的是,Promise.any()办法承受的可迭代方针中没有一个promise成功(即一切的promises都失利/回绝),就回来一个失利的promise和AggregateError类型的实例。

4. 逻辑赋值操作符 &&= 、||= 、??=

const [f1, f2, f3] = [true, false]
f1 &&= '逍' // 等同于 str = str && '逍'
f2 ||= '逍' // 等同于 str = str || '逍'
f3 ??= '逍' // 等同于 str = str ?? '逍'

6.8 ES2022

1. class的扩展

在ES2022中答应咱们并不在constructor中界说类的成员,示例代码如下:

class C {
 myName = '刘逍'
}
/* 两者是共同的 */
class C {
 constructor() {
  myName = '刘逍'
  }
}

ES2022中答应咱们运用#开头命名的变量作为类的私有成员

2. await在顶层运用

在ES2022中新增了答应在顶层运用await,在顶层能够不适用async函数进行包裹,示例代码如下:

import { AsyncFun } from 'module'
await AsyncFun()
console.log(123)

3. Object.hasOwn()

Object.hasOwn()办法用于判别某个方针上是否具有某个特点,示例代码如下:

const person = {
 name: '刘逍',
 age: 18,
}
console.log(Object.hasOwn(person, 'name')) // true
console.log(Object.hasOwn(person, 'sex')) // false

4. Array.prototype.at()

ES2022中新增的at()办法,它的效果是获取数组中的某个成员,它的参数是数组的索引,与直接运用索引的办法不同,它答应咱们传递负值,等同于从后边倒数,示例代码如下:

const arr = [1, 2, 3, 4, 5, 6]
console.log(arr.at(-1)) // 6
// 等同于 arr[arr.length - 1]

七、 DOM

7.1说一下 DOM 事情流

⼜称为事情传播,是⻚⾯中接纳事情的次第。DOM2级事情规矩的事情流包括了3个阶段:

  • 事情捕获阶段(capture phase)
  • 处于⽬标阶段(target phase)
  • 事情冒泡阶段(bubbling phase)

2022年我的面试万字总结(JS篇下)

如上图所示,事情流的触发次第是:

  1. 事情捕获阶段,为截获事情供给了机会
  2. 实践的⽬标元素接纳到事情
  3. 事情冒泡阶段,可在这个阶段对事情做出呼应

7.2 什么是事情冒泡(Event Bubbling)

事情开端由最详细的元素(⽂档中嵌套层次最深的那个节点)接纳到后,开端逐级向上传播到较为不详细的节点。

<html>
 
 <head> 
  <title>Document</title> 
 </head>
 
 <body> 
  <button>按钮</button> 
 </body> 
 
</html>

假如点击了上面页面代码中的 <button> 按钮,那么该 click 点击事情会沿着 DOM 树向上逐级传播,在途经的每个节点上都会发生,详细次第如下:

  1. button 元素
  2. body 元素
  3. html 元素
  4. document 方针

7.3 什么是事情捕获(Event Capturing)

事情开端由较为不详细的节点接纳后,然后开端逐级向下传播到最详细的元素上。

事情捕获的最大效果在于:事情在抵达预定⽬标之前就能够捕获到它。

假如仍以上面那段 HTML 代码为例,当点击按钮后,在事情捕获的过程中,document 方针会首先接纳到这个 click 事情,然后再沿着 DOM 树顺次向下,直到 <button>。详细次第如下:

  1. document 方针
  2. html 元素
  3. body 元素
  4. button 元素

7.4 什么是事情托付

事情托付,便是利用了事情冒泡的机制,在较上层方位的元素上增加一个事情监听函数,来管理该元素及其一切后代元素上的某一类的一切事情。

适用场景:在绑定很多事情的时分,能够选择事情托付

长处

  • 事情托付能够削减事情注册数量,节省内存占⽤!
  • 当新增⼦元素时,⽆需再次做事情绑定,因而非常合适动态增加元素 (vue解析模板时, 会对新创立的元素, 额定进行绑定的)

7.5 什么是DOM

DOM便是文档方针模型 ,是用来出现以及与恣意 HTML 和或XML 文档交互的API

它供给了对文档的结构化的表述,并界说了一种办法能够使从程序中对该结构进行拜访,从而改动文档的结构,款式和内容

7.6 DOM的常用操作

(1)创立节点

createElement

创立新元素,承受一个参数,即要创立元素的标签名

const divEl = document.createElement("div");

createTextNode

创立一个文本节点

const textEl = document.createTextNode("content");

createDocumentFragment

用来创立一个文档碎片,它表明一种轻量级的文档,主要是用来存储暂时节点,然后把文档碎片的内容一次性增加到DOM

const fragment = document.createDocumentFragment();

当恳求把一个DocumentFragment 节点刺进文档树时,刺进的不是 DocumentFragment自身,而是它的一切后代节点

createAttribute

创立特点节点,能够是自界说特点

const dataAttribute = document.createAttribute('custom');
consle.log(dataAttribute);

(2)获取节点

querySelector

传入任何有用的css 选择器,即可选中单个 DOM元素(首个):

document.querySelector('.element')
document.querySelector('#element')
document.querySelector('div')
document.querySelector('[name="username"]')
document.querySelector('div + p > span')

假如页面上没有指定的元素时,回来 null

querySelectorAll

回来一个包括节点子树内一切与之相匹配的Element节点列表,假如没有相匹配的,则回来一个空节点列表

const notLive = document.querySelectorAll("p");

需求留意的是,该办法回来的是一个 NodeList的静态实例,它是一个静态的“快照”,而非“实时”的查询

关于获取DOM元素的办法还有如下,就不一一述说

document.getElementById('id特点值');回来具有指定id的方针的引证
document.getElementsByClassName('class特点值');回来具有指定class的方针调集
document.getElementsByTagName('标签名');回来具有指定标签名的方针调集
document.getElementsByName('name特点值'); 回来具有指定称号的方针结合
document/element.querySelector('CSS选择器'); 仅回来第一个匹配的元素
document/element.querySelectorAll('CSS选择器');  回来一切匹配的元素
document.documentElement; 获取页面中的HTML标签
document.body; 获取页面中的BODY标签
document.all['']; 获取页面中的一切元素节点的方针调集型

(3)更新节点

innerHTML

不光能够修正一个DOM节点的文本内容,还能够直接经过HTML片段修正DOM节点内部的子树

// 获取<p id="p">...</p >
var p = document.getElementById('p');
// 设置文本为abc:
p.innerHTML = 'ABC'; // <p id="p">ABC</p >
// 设置HTML:
p.innerHTML = 'ABC <span>RED</span> XYZ';
// <p>...</p >的内部结构已修正

innerText、textContent

主动对字符串进行HTML编码,确保无法设置任何HTML标签

// 获取<p id="p-id">...</p >
var p = document.getElementById('p-id');
// 设置文本:
p.innerText = '<script>alert("Hi")</script>';
// HTML被主动编码,无法设置一个<script>节点:
// <p id="p-id">&lt;script&gt;alert("Hi")&lt;/script&gt;</p >

两者的差异在于读取特点时,innerText不回来隐藏元素的文本,而textContent回来一切文本

style

DOM节点的style特点对应一切的CSS,能够直接获取或设置。遇到-需求转化为驼峰命名

// 获取<p id="p-id">...</p >
const p = document.getElementById('p-id');
// 设置CSS:
p.style.color = '#ff0000';
p.style.fontSize = '20px'; // 驼峰命名
p.style.paddingTop = '2em';

(4)增加节点

innerHTML

假如这个DOM节点是空的,例如,<div></div>,那么,直接运用innerHTML = '<span>child</span>'就能够修正DOM节点的内容,相当于增加了新的DOM节点

假如这个DOM节点不是空的,那就不能这么做,由于innerHTML会直接替换掉本来的一切子节点

appendChild

把一个子节点增加到父节点的最终一个子节点

假如是获取DOM元素后再进行增加操作,这个js节点是现已存在当时文档树中,因而这个节点首先会从原先的方位删去,再刺进到新的方位

假如动态增加新的节点,则先创立一个新的节点,然后刺进到指定的方位

insertBefore

把子节点刺进到指定的方位,运用办法如下:

parentElement.insertBefore(newElement, referenceElement)

子节点会刺进到referenceElement之前

setAttribute

增加一个特点节点,假如元素中已有该特点改动特点值

const div = document.getElementById('id')
div.setAttribute('class', 'white');//第一个参数特点名,第二个参数特点值。

(5)删去节点

removeChild

删去一个节点,首先要取得该节点自身以及它的父节点,然后,调用父节点的removeChild把自己删掉

// 拿到待删去节点:
const self = document.getElementById('to-be-removed');
// 拿到父节点:
const parent = self.parentElement;
// 删去:
const removed = parent.removeChild(self);
removed === self; // true

删去后的节点尽管不在文档树中了,但其实它还在内存中,能够随时再次被增加到其他方位

7.7 什么是DOM树

以 HTMLDocument 为根节点,其余节点为子节点,组织成一个树的数据结构的表明便是 DOM树。

DOM树直接体现了标签与标签之间的联系

7.8 什么是DOM方针,什么是document方针

DOM方针是浏览器根据html标签生成的Js方针

docement方针是DOM里供给的一个方针,它供给的特点和办法都是用来拜访和操作网页内容的

7.9 L0和L2注册事情的差异

2022年我的面试万字总结(JS篇下)

7.10 怎样解绑事情

2022年我的面试万字总结(JS篇下)

7.11 addEventListener 的第三个参数有什么用

addEventListener有三个参数:

 element.addEventListener(event, function, useCapture)
参数 描述
event 有必要。字符串,指定事情名。 留意: 不要运用 “on” 前缀。 例如,运用 “click” ,而不是运用 “onclick”。 提示: 一切 HTML DOM 事情,能够检查咱们完整的 HTML DOM Event 方针参考手册。
function 有必要。指定要事情触发时履行的函数。 当事情方针会作为第一个参数传入函数。 事情方针的类型取决于特定的事情。例如, “click” 事情归于 MouseEvent(鼠标事情) 方针。
useCapture 可选。布尔值,指定事情是否在捕获或冒泡阶段履行。 或许值:true – 事情句柄在捕获阶段履行(即在事情捕获阶段调用处理函数)false- false- 默许。事情句柄在冒泡阶段履行(即表明在事情冒泡的阶段调用事情处理函数)

7.12 常用DOM事情

  • 鼠标事情

    • 鼠标单击 click

    • 鼠标双击 dblclick

    • 鼠标移入/移出

      • mouseover/mouseout : 支撑冒泡
      • mouseenter/mouseleave: 不支撑冒泡
    • 鼠标移动 mousemove

    • 鼠标按下 mousedown

    • 鼠标松开 mouseup

  • 键盘事情

    • input : 键盘输入
    • keydwon : 键盘按下
    • focus: 成为焦点
    • blur: 失去焦点
  • 页面事情

    • scroll : 页面滚动
    • resize:页面大小变化

7.13 怎样阻挠事情冒泡、阻挠默许事情?

阻挠事情冒泡

e.stopPropagation**()

阻挠默许事情,3种办法

e.preventDefault();//谷歌及IE8以上
window.event.returnValue = false; //IE8及以下
return false; //无兼容问题(但不能用于节点直接onclick绑定函数)

7.14 DOM的类型有哪几种?

12种

元素节点         Node.ELEMENT_NODE(1)
特点节点         Node.ATTRIBUTE_NODE(2)
文本节点         Node.TEXT_NODE(3)
CDATA节点       Node.CDATA_SECTION_NODE(4)
实体引证称号节点      Node.ENTRY_REFERENCE_NODE(5)
实体称号节点       Node.ENTITY_NODE(6)
处理指令节点       Node.PROCESSING_INSTRUCTION_NODE(7)
注释节点         Node.COMMENT_NODE(8)
文档节点         Node.DOCUMENT_NODE(9)
文档类型节点       Node.DOCUMENT_TYPE_NODE(10)
文档片段节点       Node.DOCUMENT_FRAGMENT_NODE(11)
DTD声明节点      Node.NOTATION_NODE(12)

7.15 DOM种获取坐标的特点有哪些,它们有什么不同?

特点 阐明 兼容性
offsetX 以当时的方针元素左上角为原点,定位x轴坐标 除Mozilla外都兼容
offsetY 以当时的方针元素左上角为原点,定位y轴坐标 除Mozilla外都兼容
clientX 以浏览器可视窗口左上角为原点,定位x轴坐标 都兼容
clientY 以浏览器可视窗口左上角为原点,定位y轴坐标 都兼容
pageX 以doument方针左上角为原点,定位x轴坐标 除IE外都兼容
pageY 以doument方针左上角为原点,定位y轴坐标 除IE外都兼容
screenX 以核算机屏幕左上顶角为原点,定位x轴坐标(多屏幕会影响) 全兼容
screenY 以核算机屏幕左上顶角为原点,定位y轴坐标 全兼容
layerX 最近的绝对定位的父元素(假如没有,则为 document 方针)左上顶角为元素,定位 x 轴坐标 Mozilla 和 Safari
layerY 最近的绝对定位的父元素(假如没有,则为 document 方针)左上顶角为元素,定位 y 轴坐标 Mozilla 和 Safari

7.16 DOM种元素视图尺度的特点有哪些?

特点 阐明
offsetLeft 获取当时元素到定位父节点的left方向的间隔
offsetTop 获取当时元素到定位父节点的top方向的间隔
offsetWidth 获取当时元素 width + 左右padding + 左右border-width
offsetHeight 获取当时元素 height + 上下padding + 上下border-width
clientWidth 获取当时元素 width + 左右padding
clientHeight 获取当时元素 height + 上下padding
scrollWidth 当时元素内容实在的宽度,内容不超出盒子宽度时为盒子的clientWidth
scrollHeight 当时元素内容实在的高度,内容不超出盒子高度时为盒子的clientHeight

7.17 怎样判别元素是否在可视区域

getBoundingClientRect

Element.getBoundingClientRect() 办法回来元素的大小及其相关于视口的方位。回来的是一个方针,方针里有这8个特点:left,right,top,bottom,width,height,x,y

7.18 IntersectionObserver

IntersectionObserver接口 供给了一种异步观察方针元素与其先人元素或尖端文档视窗(viewport)穿插状况的办法。先人元素与视窗(viewport)被称为根(root)

通俗点说便是:IntersectionObserver是用来监听某个元素与视口穿插状况的。

7.19 怎样遍历输出页面中的一切元素

createNodeIterator

运用createNodeIterator对页面中一切元素进行遍历输出呢?

const body = document.getElementsByTagName('body')[0]
  const it = document.createNodeIterator(body)
  let root = it.nextNode()
  while(root) {
    console.log(root)
    root = it.nextNode()
   }

7.20 什么是BOM方针

2022年我的面试万字总结(JS篇下)

(1) location方针的常用办法

2022年我的面试万字总结(JS篇下)

(2) navigator方针 (获取浏览器平台和版本数据)

2022年我的面试万字总结(JS篇下)

(3) histroy方针 (管理浏览器历史记载)

2022年我的面试万字总结(JS篇下)

八、其他类型问题弥补

8.1 简略说说你对观察者办法的了解

观察者办法界说了方针间的一种一对多的依赖联系,当一个方针的状况发生改动时,一切依赖于它的方针都将得到告诉,并主动更新

观察者办法归于行为型办法,行为型办法关注的是方针之间的通讯,观察者办法便是观察者和被观察者之间的通讯

2022年我的面试万字总结(JS篇下)

例如生活中,咱们能够用报纸期刊的订阅来形象的阐明,当你订阅了一份报纸,每天都会有一份最新的报纸送到你手上,有多少人订阅报纸,报社就会发多少份报纸

报社和订报纸的客户就形成了一对多的依赖联系

8.2 简略说说你对发布订阅办法的了解

发布-订阅是一种音讯范式,音讯的发送者(称为发布者)不会将音讯直接发送给特定的接纳者(称为订阅者)。而是将发布的音讯分为不同的类别,无需了解哪些订阅者(假如有的话)或许存在

相同的,订阅者能够表达对一个或多个类其他爱好,只接纳感爱好的音讯,无需了解哪些发布者存在

8.3 观察者办法与发布订阅的差异

  • 在观察者办法中,观察者是知道Subject的,Subject一向坚持对观察者进行记载。但是,在发布订阅办法中,发布者和订阅者不知道对方的存在。它们只需经过音讯署理进行通讯。
  • 在发布订阅办法中,组件是松懈耦合的,正好和观察者办法相反。
  • 观察者办法大多数时分是同步的,比方当事情触发,Subject就会去调用观察者的办法。而发布-订阅办法大多数时分是异步的(运用音讯行列)

8.4 说说你对正则表达式的了解

正则表达式是一种用来匹配字符串的强有力的兵器

它的规划思想是用一种描述性的言语界说一个规矩,但凡契合规矩的字符串,咱们就认为它“匹配”了,不然,该字符串便是不合法的

JavaScript中,正则表达式也是方针,构建正则表达式有两种办法:

  1. 字面量创立,其由包括在斜杠之间的办法组成
const re = /d+/g;
  1. 调用RegExp方针的结构函数
const re = new RegExp("d+","g");
​
const rul = "d+"
const re1 = new RegExp(rul,"g");

运用构建函数创立,第一个参数能够是一个变量,遇到特别字符“需求运用进行转义

8.5 怎样判别当时的Js代码是否在浏览器环境中运转

假如Javascript在浏览器环境中运转,则会有一个大局方针:window。因而,能够经过以下办法判别环境:

 typeof window.self !== "undefined";
 // 在web worker或许sevice worker下是无法获取到windows大局变量, 所以需求经过self变量判别