2023.03.26 更新各大厂前端面试问题总结(22道题)

2023.03.26 更新前端面试问题总结(22道题)
获取更多面试问题可以访问
github 地址: https://github.com/pro-collection/interview-question/issues
gitee 地址: https://gitee.com/yanleweb/interview-question/issues

目录:

  • 初级开发者相关问题【共计 1 道题】

    • 185.TypeScript is 这个关键字是做什么呢?【TypeScript】
  • 中级开发者相关问题【共计 9 道题】

    • 182.http3 QUIC 是什么协议?【网络】
    • 188.前端如何实现截图?【web应用场景】
    • 189.当QPS达到峰值时, 该如何处理?【web应用场景】
    • 190.js 超过 Number 最大值的数怎么处理?【web应用场景】
    • 191.[‘1’, ‘2’, ‘3’].map(parseInt) 结果是啥,为什么?【JavaScript】
    • 192.介绍下深度优先遍历和广度优先遍历,如何实现?【JavaScript】
    • 193.请分别用深度优先思想和广度优先思想实现一个拷贝函数?【JavaScript】
    • 195.Promise 构造函数是同步执行还是异步执行,那么 then 方法呢?【JavaScript】
    • 199.手写 观察者模式【JavaScript】【出题公司: 网易、阿里巴巴】
  • 高级开发者相关问题【共计 11 道题】

    • 2.async/await 原理, 手写 async 函数?【JavaScript】
    • 180.HTTP协议的不同版本的主要特点有哪些?【网络】
    • 181.http1.1 持久连接 和 http2 的多路复用有什么区别?【网络】
    • 184.前端如何防止加载外域脚本?【网络】
    • 186.中间人攻击是什么?【网络】
    • 187.前端单页应用 History 路由模式, 需要如何配置 nginx?【工程化】
    • 194.JavaScript 异步解决方案的发展历程主要有哪些阶段?【JavaScript】
    • 196.如何从 http1.1 迁移到 http2 ?【JavaScript】
    • 197.A、B 机器正常连接后,B 机器突然重启,问 A 此时处于 TCP 什么状态?(了解即可)【网络】【出题公司: 网易】
    • 198.介绍下观察者模式和订阅-发布模式的区别?【JavaScript】【出题公司: 网易、阿里巴巴】
    • 200.手写订阅-发布模式【JavaScript】【出题公司: 网易、阿里巴巴】
  • 资深开发者相关问题【共计 1 道题】

    • 183.HTTP/3 是基于 UDP 的协议, 那么他是如何保障安全性的?【网络】

初级开发者相关问题【共计 1 道题】

185.TypeScript is 这个关键字是做什么呢?【TypeScript】

is 是 TypeScript 中的一个关键字,用于创建类型保护。在 TypeScript 中,类型保护是一种用于确定变量是否符合某种类型的方法。当我们使用 is 关键字创建一个类型保护时,它会在运行时对变量进行判断,然后返回一个布尔值。

具体来说,我们可以通过定义一个返回值为布尔类型的函数,并在函数内部进行类型判断来创建类型保护。比如:

csharpCopy codefunction isString(value: any): value is string {
  return typeof value === 'string';
}

在这个例子中,我们定义了一个名为 isString 的函数,它接收一个任意类型的参数 value,并通过 typeof 运算符判断 value 是否为字符串。如果是字符串,函数返回 true,否则返回 false

使用时,我们可以通过将变量传递给 isString 函数来判断变量是否为字符串类型:

rustCopy codeconst str = 'hello';
if (isString(str)) {
  console.log(str.length);
}

在这个例子中,由于 str 是字符串类型,所以 isString(str) 返回 trueif 语句内的代码会被执行,输出字符串的长度。如果 str 不是字符串类型,isString(str) 返回 falseif 语句内的代码不会被执行。

这样,在使用变量之前进行类型保护,可以避免在运行时出现类型错误,提高代码的健壮性。

中级开发者相关问题【共计 9 道题】

182.http3 QUIC 是什么协议?【网络】

HTTP/3(或称为HTTP-over-QUIC)是一个基于QUIC协议的新版本的HTTP协议。QUIC(Quick UDP Internet Connections)是由Google设计的一个基于UDP协议的传输层协议,旨在解决HTTP/2协议存在的一些问题。

HTTP/3中引入了QUIC的一些特性,如0-RTT连接建立、基于UDP的传输、数据流多路复用和快速恢复等,这些特性有助于提高性能和安全性。与HTTP/2相比,HTTP/3采用了新的二进制编码协议(QUIC Crypto)来加密和验证数据,以提供更好的安全性。

此外,HTTP/3还可以更好地适应现代网络环境下的多元化应用需求。由于QUIC协议基于UDP协议,因此可以更好地适应移动网络和高丢包率网络等不稳定的网络环境。同时,HTTP/3可以更好地支持多媒体内容和实时通信等应用场景。

188.前端如何实现截图?【web应用场景】

前端实现截图需要使用 HTML5 的 Canvas 和相关 API,具体步骤如下:

  1. 首先在页面中创建一个 Canvas 元素,并设置其宽高和样式。
  2. 使用 Canvas API 在 Canvas 上绘制需要截图的内容,比如页面的某个区域、某个元素、图片等。
  3. 调用 Canvas API 中的 toDataURL() 方法将 Canvas 转化为 base64 编码的图片数据。
  4. 将 base64 编码的图片数据传递给后端进行处理或者直接在前端进行显示。

以下是一个简单的例子,实现了对整个页面的截图:

htmlCopy codeDOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>截图示例title>
    <style>
      #canvas {
        position: fixed;
        left: 0;
        top: 0;
        z-index: 9999;
      }
    style>
  head>
  <body>
    <h1>截图示例h1>
    <p>这是一个简单的截图示例。p>
    <button id="btn">截图button>
    <canvas id="canvas">canvas>
    <script>
      const canvas = document.getElementById('canvas');
      const ctx = canvas.getContext('2d');
      const btn = document.getElementById('btn');
      canvas.width = window.innerWidth;
      canvas.height = window.innerHeight;

      btn.addEventListener('click', () => {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.drawImage(document.documentElement, 0, 0);
        const imgData = canvas.toDataURL();
        console.log(imgData);
      });
    script>
  body>
html>

这个例子中,在页面中创建了一个 canvas 元素,并设置其宽高和样式,将其放在页面最上方。在点击“截图”按钮时,通过 toDataURL() 方法将整个页面的截图转换为 base64 编码的图片数据,并打印到控制台上。

189.当QPS达到峰值时, 该如何处理?【web应用场景】

当QPS达到峰值时,可以从以下几个方面来进行优化:

  1. 数据库优化:数据库的优化包括优化SQL语句、使用索引、避免全表扫描、分表分库等措施,以提高数据库的读写性能。

  2. 缓存优化:缓存可以降低对数据库的访问频率,提高响应速度。可以使用Redis、Memcached等缓存技术,减轻服务器负载。

  3. 代码优化:优化代码可以提高代码的执行效率,减少不必要的开销。可以通过一些优化手段,如减少不必要的代码执行、避免循环嵌套、避免不必要的递归调用等来提高代码的性能。

  4. 负载均衡:负载均衡可以将请求分发到多个服务器上,减少单个服务器的负载,提高整个系统的性能和可用性。

  5. 异步处理:将一些计算量大、耗时长的操作异步处理,减少对主线程的阻塞,提高响应速度。

  6. CDN加速:使用CDN技术可以将静态资源缓存到CDN节点上,提高资源的加载速度,减少服务器的负载。

  7. 硬件升级:可以通过升级服务器硬件,增加带宽等方式来提高系统的处理能力。

以上是一些常见的优化手段,需要根据具体情况进行选择和实施。

190.js 超过 Number 最大值的数怎么处理?【web应用场景】

js 超过 Number 最大值的数怎么处理

在 JavaScript 中,超过 Number.MAX_VALUE 的数值被认为是 Infinity(正无穷大)。如果要处理超过 Number.MAX_VALUE 的数值,可以使用第三方的 JavaScript 库,如 big.jsbignumber.js,这些库可以处理任意精度的数值。

例如,使用 big.js 库可以将两个超过 Number.MAX_VALUE 的数相加:

const big = require('big.js');

const x = new big('9007199254740993');
const y = new big('100000000000000000');

const result = x.plus(y);

console.log(result.toString()); // 输出:100009007194925474093

这里创建了两个 big.js 对象 xy,分别存储超过 Number.MAX_VALUE 的数值。通过 plus 方法将它们相加,得到了正确的结果。最后,通过 toString 方法将结果转换为字符串。

如果不依赖外部库,咋处理

JavaScript 中,数值超过了 Number 最大值时,可以使用 BigInt 类型来处理,它可以表示任意精度的整数。

使用 BigInt 类型时,需要在数值后面添加一个 n 后缀来表示 BigInt 类型。例如:

const bigNum = 9007199254740993n; // 注意:数字后面添加了 'n' 后缀

注意,BigInt 类型是 ECMAScript 2020 新增的特性,因此在某些浏览器中可能不被支持。如果需要在不支持 BigInt 的环境中使用 BigInt,可以使用 polyfill 或者第三方库来实现。

191.[‘1’, ‘2’, ‘3’].map(parseInt) 结果是啥,为什么?【JavaScript】

执行 ['1', '2', '3'].map(parseInt) 会得到 [1, NaN, NaN],这个结果可能和人们预期的不一样。

这是因为 map 方法会传入三个参数:当前遍历到的元素、当前遍历到的索引、原数组本身。而 parseInt 函数则接受两个参数:需要被解析的值、用于解析的进制数。在执行 ['1', '2', '3'].map(parseInt) 时,实际传入 parseInt 的参数如下:

  • '1'0(表示解析为十进制):解析后得到数字 1
  • '2'1(表示解析为一进制):解析后得到 NaN
  • '3'2(表示解析为二进制):解析后得到 NaN

所以结果为 [1, NaN, NaN]

192.介绍下深度优先遍历和广度优先遍历,如何实现?【JavaScript】

深度优先遍历(Depth-First-Search,DFS)和广度优先遍历(Breadth-First-Search,BFS)是图和树的两种遍历方式。

深度优先遍历(DFS)

深度优先遍历采用深度优先的策略遍历整张图或树,即从当前节点开始,先访问其所有子节点,再依次访问子节点的子节点,直到遍历完整张图或树。

DFS 可以使用递归或栈来实现。

递归实现:

function dfsRecursive(node, visited) {
  if (!node || visited.has(node)) {
    return;
  }
  visited.add(node);
  console.log(node.value);
  for (let i = 0; i < node.children.length; i++) {
    dfsRecursive(node.children[i], visited);
  }
}

栈实现:

function dfsStack(node) {
  const visited = new Set();
  const stack = [node];
  while (stack.length > 0) {
    const current = stack.pop();
    if (!current || visited.has(current)) {
      continue;
    }
    visited.add(current);
    console.log(current.value);
    for (let i = current.children.length - 1; i >= 0; i--) {
      stack.push(current.children[i]);
    }
  }
}

广度优先遍历(BFS)

广度优先遍历采用广度优先的策略遍历整张图或树,即从当前节点开始,先访问所有相邻节点,再访问所有相邻节点的相邻节点,以此类推,直到遍历完整张图或树。

BFS 可以使用队列来实现。

队列实现:

function bfsQueue(node) {
  const visited = new Set();
  const queue = [node];
  while (queue.length > 0) {
    const current = queue.shift();
    if (!current || visited.has(current)) {
      continue;
    }
    visited.add(current);
    console.log(current.value);
    for (let i = 0; i < current.children.length; i++) {
      queue.push(current.children[i]);
    }
  }
}

总的来说,深度优先遍历和广度优先遍历都有自己的应用场景,比如:

  • 深度优先遍历通常用于寻找一条路径,或者对树的节点进行递归操作。
  • 广度优先遍历通常用于寻找最短路径,或者对图进行层级遍历操作。

193.请分别用深度优先思想和广度优先思想实现一个拷贝函数?【JavaScript】

深度优先思想实现拷贝函数可以采用递归的方式遍历对象或数组,对每个元素进行复制。如果当前元素是一个对象或数组,则递归调用拷贝函数,如果是基本数据类型则直接进行复制。以下是一个用深度优先思想实现拷贝函数的示例代码:

function deepClone(obj) {
  // 如果obj是基本数据类型或null,则直接返回
  if (typeof obj !== 'object' || obj === null) {
    return obj;
  }

  let result;
  // 判断obj是数组还是对象
  if (Array.isArray(obj)) {
    result = [];
  } else {
    result = {};
  }

  // 递归遍历obj的每个属性或元素,并进行拷贝
  for (let key in obj) {
    result[key] = deepClone(obj[key]);
  }

  return result;
}

广度优先思想实现拷贝函数可以使用队列的方式,将每个元素放入队列中,然后循环遍历队列。如果当前元素是一个对象或数组,则将其属性或元素放入队列中,然后继续循环遍历队列。如果是基本数据类型则直接进行复制。以下是一个用广度优先思想实现拷贝函数的示例代码:

function breadthClone(obj) {
  // 如果obj是基本数据类型或null,则直接返回
  if (typeof obj !== 'object' || obj === null) {
    return obj;
  }

  let result;
  // 判断obj是数组还是对象
  if (Array.isArray(obj)) {
    result = [];
  } else {
    result = {};
  }

  let queue = [obj];
  let resQueue = [result];

  // 循环遍历队列
  while (queue.length > 0) {
    let curObj = queue.shift();
    let curRes = resQueue.shift();

    // 遍历当前元素的每个属性或元素,并进行拷贝
    for (let key in curObj) {
      let val = curObj[key];
      if (typeof val === 'object' && val !== null) {
        // 如果当前属性或元素是一个对象或数组,则将其放入队列中
        let newVal = Array.isArray(val) ? [] : {};
        curRes[key] = newVal;
        queue.push(val);
        resQueue.push(newVal);
      } else {
        // 如果是基本数据类型则直接进行复制
        curRes[key] = val;
      }
    }
  }

  return result;
}

195.Promise 构造函数是同步执行还是异步执行,那么 then 方法呢?【JavaScript】

Promise 构造函数是同步执行的,而 then 方法是异步执行的。

在 Promise 构造函数中,Promise 的状态(pending/resolved/rejected)是同步确定的。但是 Promise 中的异步操作可能还没有完成,因此 Promise 对象本身的值可能还没有可用的值。所以,当我们在构造函数中使用 resolve/reject 时,它们并不会立即触发 then 中注册的回调函数执行。

而 then 方法则是异步执行的。当我们在一个 Promise 对象上调用 then 方法并注册了回调函数时,这些回调函数并不会立即执行。相反,它们会被添加到一个任务队列中,等到当前 JavaScript 上下文中的所有同步代码执行完成后再执行。

这也是 Promise 非常重要的特性之一,即能够在异步任务完成后执行回调函数,避免了回调地狱等问题。

199.手写 观察者模式【JavaScript】【出题公司: 网易、阿里巴巴】

观察者模式(又称发布-订阅模式)是一种行为型设计模式,它定义了对象之间的一对多依赖关系,使得当一个对象的状态发生改变时,其相关的依赖对象都能够得到通知并被自动更新。

在 JavaScript 中实现观察者模式,可以分为以下几个步骤:

  1. 创建一个主题对象(Subject),用来存储观察者对象,并提供添加、删除、通知观察者的接口。

  2. 创建观察者对象(Observer),它有一个 update 方法,用来接收主题对象的通知,并进行相应的处理。

下面是一个简单的示例:

class Subject {
  constructor() {
    this.observers = [];
  }

  // 添加观察者
  addObserver(observer) {
    this.observers.push(observer);
  }

  // 删除观察者
  removeObserver(observer) {
    const index = this.observers.indexOf(observer);
    if (index !== -1) {
      this.observers.splice(index, 1);
    }
  }

  // 通知观察者
  notifyObservers() {
    this.observers.forEach(observer => observer.update());
  }
}

class Observer {
  constructor(name) {
    this.name = name;
  }

  update() {
    console.log(`${this.name} received the notification.`);
  }
}

const subject = new Subject();
const observer1 = new Observer('Observer 1');
const observer2 = new Observer('Observer 2');

subject.addObserver(observer1);
subject.addObserver(observer2);

subject.notifyObservers();
// Output:
// Observer 1 received the notification.
// Observer 2 received the notification.

在这个示例中,Subject 是主题对象,Observer 是观察者对象。Subject 提供了添加、删除、通知观察者的接口,Observer 有一个 update 方法,用来接收主题对象的通知,并进行相应的处理。在使用时,我们可以通过调用 Subject 的 addObserver 方法,将 Observer 对象添加到主题对象中。当主题对象的状态发生改变时,我们可以调用 notifyObservers 方法,通知所有的观察者对象进行更新。

以上仅是一个简单的示例,实际应用中还需要考虑更多的细节问题。

高级开发者相关问题【共计 11 道题】

2.async/await 原理, 手写 async 函数?【JavaScript】

async/await 的本质

async/await 是 ECMAScript 2017(ES8)中引入的一个语言特性,用于处理异步编程。async/await 实际上是对 Promise 的封装,通过让开发者以同步的方式编写异步代码,使得代码更加易读和易于维护。

async/await 是一种更加高级的异步编程方式,它使用了 Promise 作为底层实现,可以更好地处理异步编程中的错误和异常,避免了回调地狱和代码可读性差的问题。

手写 async/await 实现

async/await 的实现可以通过封装 Promise 和 Generator 函数来实现,下面是一个简单的手写实现示例:

function delay(ms) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve();
    }, ms);
  });
}

function* generator() {
  console.log("start");
  yield delay(1000);
  console.log("after 1 second");
  yield delay(2000);
  console.log("after 2 more seconds");
}

function async(generatorFunc) {
  const iterator = generatorFunc();

  function handle(iteratorResult) {
    if (iteratorResult.done) {
      return Promise.resolve(iteratorResult.value);
    }

    return Promise.resolve(iteratorResult.value).then((res) => {
      return handle(iterator.next(res));
    });
  }

  return handle(iterator.next());
}

async(function () {
  return generator();
}).then(() => {
  console.log("all done");
});

180.HTTP协议的不同版本的主要特点有哪些?【网络】

HTTP协议的不同版本的主要特点如下表所示:

版本 发布时间 主要特点
HTTP/0.9 1991年 只支持GET方法,没有Header和Body
HTTP/1.0 1996年 引入Header、POST方法、响应码、缓存等特性
HTTP/1.1 1999年 引入持久连接、管道化请求、分块传输编码、Host头、缓存控制等特性
HTTP/2 2015年 引入二进制分帧、头部压缩、流量控制、多路复用等特性
HTTP/3 2021年 引入QUIC协议,改进网络传输性能

需要注意的是,HTTP/1.x和HTTP/2都支持TLS加密传输,即HTTPS协议。HTTP/3则是基于QUIC协议的,使用TLS 1.3进行加密传输。

181.http1.1 持久连接 和 http2 的多路复用有什么区别?【网络】

HTTP/1.1和HTTP/2都是HTTP协议的不同版本,在网络传输和性能方面有很大的差别。

HTTP/1.1使用的是“管线化请求”和“持久连接”来提高性能,而HTTP/2则引入了更多的特性,其中最重要的特性是“多路复用”。

“管线化请求”是HTTP/1.1提出的一种优化方法,它可以让浏览器同时发出多个请求,从而避免了HTTP/1.1中因为请求阻塞导致的性能问题。但是,由于HTTP/1.1的“管线化请求”存在“队头阻塞”(head-of-line blocking)问题,即前面一个请求没有得到响应时,后面的请求必须等待,导致性能并没有得到很大提升。

“持久连接”是HTTP/1.1中另一种提高性能的方法,它可以在一个TCP连接中传输多个HTTP请求和响应,避免了每个请求都需要建立和关闭连接的开销。但是,由于HTTP/1.1中的“持久连接”是按顺序发送请求和响应的,所以依然存在“队头阻塞”的问题。

HTTP/2则引入了“多路复用”(multiplexing)这一特性,可以在一个TCP连接上同时传输多个HTTP请求和响应,避免了“队头阻塞”问题。它使用二进制分帧(binary framing)技术将HTTP请求和响应分成多个帧(frame),并使用流(stream)来标识不同的请求和响应,从而实现了更高效的网络传输和更低的延迟。此外,HTTP/2还引入了头部压缩(header compression)和服务器推送(server push)等特性。

因此,HTTP/2的多路复用比HTTP/1.1的管线化请求和持久连接更为高效、灵活,能够更好地支持现代Web应用的性能要求。

184.前端如何防止加载外域脚本?【网络】

前端可以通过以下方式防止加载外域脚本:

  1. 使用 Content Security Policy (CSP):CSP 是一个 HTTP 头,可以限制页面可以从哪些源加载资源。通过 CSP,可以禁止加载外域脚本,从而防止 XSS 攻击等安全问题。

  2. 使用 Subresource Integrity (SRI):SRI 是一个浏览器功能,可以确保在加载外部资源时,它们的内容没有被篡改过。通过在 script 标签中添加 integrity 属性,可以指定资源的校验和,浏览器会校验资源是否与 integrity 值匹配,从而确保资源没有被篡改过。

  3. 避免使用动态脚本加载:使用 document.createElement(‘script’) 创建 script 元素,并手动设置其 src 属性,可以避免使用 eval() 动态执行脚本。动态加载脚本的方式可能会受到中间人攻击,从而加载恶意脚本。

  4. 避免使用不安全的协议加载资源:使用 HTTPS 加载资源可以确保资源在传输过程中不会被篡改。避免使用 HTTP 或者 file 协议加载资源,这些协议容易受到中间人攻击。

综上,前端可以通过以上方式防止加载外域脚本,提高应用程序的安全性。

186.中间人攻击是什么?【网络】

中间人攻击(Man-in-the-Middle Attack, MITM),也称中间攻击,是指黑客通过拦截通信过程中的数据,然后在两个通信的用户之间进行欺骗和篡改的攻击方式。在 HTTPS 中,如果黑客能够截获用户和服务器之间的通信,那么他就可以使用类似于伪造证书、中断连接、插入恶意代码等方式,对数据进行篡改或窃取用户敏感信息。

为了防止中间人攻击,在 HTTPS 中通常采用数字证书认证机制,即服务器会向可信的第三方机构申请数字证书,证明其身份的真实性。当用户访问该服务器时,服务器会把自己的数字证书发送给用户,用户的浏览器会验证该证书是否由可信的第三方机构签发,是否过期等,如果验证通过,则可以建立安全的 HTTPS 连接。如果数字证书无效,则会弹出警告页面,提示用户存在安全风险。

187.前端单页应用 History 路由模式, 需要如何配置 nginx?【工程化】

前端单页应用使用 History 路由模式,需要将路由的控制交给前端,而不是后端处理,因此需要配置 nginx 让其将所有请求都指向 index.html 页面,让前端来处理路由。

具体配置可以参考以下 nginx 配置:

bashCopy codeserver {
    listen       80;
    server_name  example.com; # 域名或 IP 地址

    location / {
        root   /path/to/your/app; # 项目根目录
        index  index.html;
        try_files $uri $uri/ /index.html; # 将所有请求都指向 index.html 页面
    }
}

以上配置中,try_files 指令会尝试将请求映射到对应的文件,如果找不到,则会将请求转发到 /index.html 页面,由前端来处理路由。同时,root 指令需要指向前端应用的根目录。

194.JavaScript 异步解决方案的发展历程主要有哪些阶段?【JavaScript】

JavaScript异步解决方案的发展历程主要有以下几个阶段:

  1. 回调函数

最初,JavaScript采用回调函数的方式来解决异步编程问题。回调函数即在异步任务完成后调用的回调函数。例如,setTimeout函数就是一个使用回调函数的例子。

setTimeout(() => {
  console.log('Hello, world!');
}, 1000);

回调函数的优点是简单易懂,缺点是嵌套层次多、代码难以维护。

  1. jQuery.Deferred()

jQuery.Deferred()是jQuery提供的一种异步编程解决方案。它是一种Promise风格的API,使得异步操作可以更加简单和可读性更高。

jQuery.Deferred()可以用于串行和并行异步操作的组织和控制,避免了回调地狱和代码复杂性。

在使用过程中,通过使用jQuery.Deferred()的resolve()和reject()方法来决定异步操作的成功或失败,并且可以使用then()方法添加成功和失败的回调函数。

jQuery.Deferred()主要的优点包括:

  • 简单易用:可以通过链式操作来组织和控制异步操作。
  • 可读性高:可以使用then()方法添加成功和失败的回调函数,使代码的意图更加明确。
  • 良好的兼容性:jQuery.Deferred()已经成为了jQuery的一部分,可以与其他jQuery的功能和插件良好地协作。

而缺点则包括:

  • jQuery.Deferred()不能被取消,且对于异步操作的结果状态只能被设置一次。
  • 依赖于jQuery库:因为jQuery.Deferred()是jQuery的一部分,所以需要依赖于jQuery库,不适合非jQuery项目。
  1. Promise

Promise是ES6引入的一种异步编程解决方案,用于解决回调函数的嵌套问题。Promise是一个对象,表示异步操作的最终完成或失败。它有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。

Promise的优点是解决了回调函数嵌套的问题,使得代码可读性和可维护性更好。缺点是语法相对复杂。

// Promise示例
function fetchData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('Hello, world!');
    }, 1000);
  });
}

fetchData().then((data) => {
  console.log(data);
}).catch((error) => {
  console.log(error);
});
  1. Generator

Generator 可以使用 yield 语句来暂停函数执行,并返回一个 Generator 对象,通过这个对象可以控制函数的继续执行和结束。

  1. Async/Await

ES8引入了Async/Await语法,使得异步编程更加简单和可读。Async/Await是基于Promise实现的,可以看作是对Promise的一种封装。Async/Await语法可以让异步代码像同步代码一样书写,让代码的可读性更高。

// Async/Await示例
async function fetchData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('Hello, world!');
    }, 1000);
  });
}

async function run() {
  try {
    const data = await fetchData();
    console.log(data);
  } catch (error) {
    console.log(error);
  }
}

run();

Async/Await 的优点是语法简单易懂、可读性好,缺点是需要掌握Promise的基本用法。

综上,JavaScript 异步编程方案的发展历程从最初的回调函数到Promise再到Async/Await,每个阶段都解决了前一阶段存在的问题,使得异步编程更加方便和易读。但是,不同方案都有自己的优缺点,需要根据实际情况选择使用。

196.如何从 http1.1 迁移到 http2 ?【JavaScript】

从 HTTP 1.1 迁移到 HTTP/2 通常需要进行以下步骤:

  1. 升级服务器:首先,你需要将你的服务器升级到支持 HTTP/2。大多数主流服务器,如Apache、Nginx等,都已经支持 HTTP/2。

  2. 使用 HTTPS:HTTP/2 只支持加密连接,因此需要使用 HTTPS。所以,你需要获得一个 SSL 证书,并使用 HTTPS 连接来替代原来的 HTTP 连接。

  3. 修改网页代码:为了利用 HTTP/2 的多路复用特性,你需要将网页中的多个小文件(例如 CSS、JavaScript、图像等)合并为一个文件,以减少请求的数量。此外,你还需要避免在一个请求中同时传输大量数据,以免阻塞其他请求的传输。

  4. 配置服务器:为了使 HTTP/2 能够充分发挥性能,你需要进行一些服务器配置,例如启用 HTTP/2、调整 TLS 版本和密码套件等。

需要注意的是,HTTP/2 是一个复杂的协议,迁移过程中需要仔细审查每一个步骤,并且对性能进行监测和测试,以确保迁移后的网站性能更好。

197.A、B 机器正常连接后,B 机器突然重启,问 A 此时处于 TCP 什么状态?(了解即可)【网络】【出题公司: 网易】

当 B 机器重启时,TCP 连接会被断开,此时 A 机器会检测到 TCP 连接异常断开,将 TCP 状态修改为 FIN_WAIT_1 状态。A 机器会继续等待来自 B 机器的响应,如果等待的时间超过了一定时间(通常为几分钟),A 机器会放弃等待并关闭 TCP 连接,将 TCP 状态修改为 CLOSED 状态。

198.介绍下观察者模式和订阅-发布模式的区别?【JavaScript】【出题公司: 网易、阿里巴巴】

观察者模式和订阅-发布模式都属于事件模型,它们都是为了解耦合而存在,但是它们之间还是有一些不同之处的:

  1. 观察者模式中,主题(被观察者)和观察者之间是直接联系的,观察者订阅主题,主题状态发生变化时会直接通知观察者;而订阅-发布模式中,发布者和订阅者之间没有直接的联系,发布者发布消息到消息中心,订阅者从消息中心订阅消息。

  2. 在观察者模式中,主题和观察者是一对多的关系,一个主题可以有多个观察者,而在订阅-发布模式中,发布者和订阅者是多对多的关系,一个发布者可以有多个订阅者,一个订阅者也可以订阅多个发布者。

  3. 在观察者模式中,主题状态发生变化时,观察者会被直接通知,通知的方式可以是同步或异步的,观察者可以决定如何处理通知;而在订阅-发布模式中,消息是通过消息中心进行传递的,订阅者从消息中心订阅消息,发布者发布消息到消息中心,消息中心再将消息发送给订阅者,这个过程是异步的,订阅者不能决定何时接收消息。

  4. 在观察者模式中,主题和观察者之间存在强耦合关系,如果一个观察者被移除,主题需要知道这个观察者的身份;而在订阅-发布模式中,发布者和订阅者之间没有强耦合关系,发布者不需要知道订阅者的身份,订阅者也不需要知道发布者的身份。

综上所述,观察者模式和订阅-发布模式都是事件模型,但它们之间的区别在于关注点的不同,观察者模式更关注主题和观察者之间的交互,而订阅-发布模式更关注发布者和订阅者之间的交互。

200.手写订阅-发布模式【JavaScript】【出题公司: 网易、阿里巴巴】

订阅-发布模式是一种常用的设计模式,它可以实现对象间的解耦,让它们不需要相互知道对方的存在,只需要关注自己需要订阅的事件即可。当一个对象的状态发生变化时,它可以发布一个事件通知其他对象,其他对象可以订阅该事件,当事件发生时得到通知并执行相应的处理。

在 JavaScript 中,订阅-发布模式也被称为事件模型。事件模型由两个主要组件组成:事件触发器和事件监听器。事件触发器负责触发事件,而事件监听器则负责监听事件并执行相应的回调函数。

下面是一个简单的实现订阅-发布模式的例子:

class EventEmitter {
  constructor() {
    this._events = {};
  }

  on(event, listener) {
    if (!this._events[event]) {
      this._events[event] = [];
    }
    this._events[event].push(listener);
  }

  emit(event, ...args) {
    if (this._events[event]) {
      this._events[event].forEach((listener) => listener(...args));
    }
  }

  off(event, listener) {
    if (this._events[event]) {
      this._events[event] = this._events[event].filter((l) => l !== listener);
    }
  }
}

这个实现包括三个方法:

  • on(event, listener):订阅事件,当事件被触发时执行监听器 listener
  • emit(event, ...args):触发事件,并将参数 ...args 传递给监听器;
  • off(event, listener):取消订阅事件,不再执行监听器 listener

使用方法如下:

const emitter = new EventEmitter();

// 订阅事件
emitter.on("event", (arg1, arg2) => {
  console.log(`event: ${arg1}, ${arg2}`);
});

// 触发事件
emitter.emit("event", "hello", "world");

// 取消订阅事件
emitter.off("event");

以上代码将输出:

csharpCopy codeevent: hello, world

订阅-发布模式在事件驱动的系统中非常常见,例如浏览器中的 DOM 事件、Node.js 中的异步 IO 事件等。

资深开发者相关问题【共计 1 道题】

183.HTTP/3 是基于 UDP 的协议, 那么他是如何保障安全性的?【网络】

HTTP/3是基于UDP的协议,因此在设计时需要考虑安全性问题。为了保障安全性,HTTP/3使用了一个新的加密协议——QUIC Crypto。

QUIC Crypto使用了一种名为"0-RTT安全连接"的机制,允许客户端在第一次请求时就可以建立安全连接,从而减少连接建立的延迟。此外,HTTP/3还使用了数字证书来验证服务器身份,以确保通信的安全性。

在HTTP/3中,每个数据包都使用一个独特的标识符(Connection ID)来标识。这个标识符会在每个数据包中包含,以便服务器能够识别它们。这种方式可以防止攻击者进行连接欺骗,从而提高了安全性。

另外,HTTP/3还使用了一些其他的技术来提高安全性,如0-RTT加密、零轮延迟、源地址验证、密钥派生和更新等。

综上所述,HTTP/3采用了一系列安全机制来保护通信安全,使其能够在基于UDP的网络环境下运行,并提供更好的性能和安全性。

你可能感兴趣的:(前端,面试)