2023.03.27 - 2023.03.28 更新前端面试问题总结(14道题)

272 阅读16分钟

2023.03.27 - 2023.03.28 更新前端面试问题总结(14道题)
获取更多面试问题可以访问
github 地址: github.com/pro-collect…
gitee 地址: gitee.com/yanleweb/in…

目录:

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

    • 224.对象取值中 a.b.c.d 和 a['b']['c']['d'] 有何区别?【JavaScript】【出题公司: 腾讯】
  • 中级开发者相关问题【共计 9 道题】

    • 103.箭头函数和普通函数的区别?【JavaScript】
    • 173.为什么小程序里拿不到dom相关的api【web框架】
    • 215.[Vue] 双向绑定和单向数据流原则是否冲突?【web框架】
    • 216.实现 (5).add(3).minus(2) 功能【JavaScript】【出题公司: 百度】
    • 217.[Vue] 响应式原理中 Object.defineProperty 有什么缺陷【web框架】【出题公司: 腾讯】
    • 218.对象引用类问题:以下代码的执行结果是什么,并解释原因【JavaScript】【出题公司: 百度】
    • 220.opacity: 0visibility: hiddendisplay: none 有啥区别, 主要使用场景是啥子?【CSS】
    • 221.箭头函数为何不能作为构造函数使用?【JavaScript】【出题公司: 腾讯】
    • 222.给定两个数组,写一个方法来计算它们的交集?【JavaScript】【出题公司: 腾讯】
  • 高级开发者相关问题【共计 4 道题】

    • 69.前端如何实现即时通讯?【网络】
    • 206.浏览器缓存中 Memory Cache 和 Disk Cache, 有啥区别?【网络】【出题公司: 字节跳动】
    • 223.介绍下如何实现 token 加密?【网络】
    • 225.ES6 代码转成 ES5 代码的实现思路是什么?【JavaScript】【出题公司: 阿里巴巴】

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

224.对象取值中 a.b.c.d 和 a['b']['c']['d'] 有何区别?【JavaScript】【出题公司: 腾讯】

使用区别

在 JavaScript 中,对象的取值可以使用两种方式,即使用点号(.)和使用方括号([])。对于对象的多层嵌套属性,可以使用两种方式分别取值,例如:

cssCopy codevar obj = {a: {b: {c: {d: 123}}}};
var d1 = obj.a.b.c.d;
var d2 = obj['a']['b']['c']['d'];

这两种方式获取的结果是相同的,都是 123。其中,使用点号取值的方式称为“点操作符”,使用方括号取值的方式称为“方括号操作符”。

两种方式的区别在于:

  1. 点操作符必须使用标识符作为属性名,而方括号操作符可以使用任何字符串作为属性名。
  2. 点操作符在代码书写上更加简洁直观,而方括号操作符可以动态地构造属性名。

因此,在使用时应根据具体的情况选择合适的方式。例如,如果属性名是固定的,建议使用点操作符;如果属性名需要根据变量或其他动态条件构造,则需要使用方括号操作符。

性能区别

在对象属性的取值操作中,使用点号.和中括号[]两种方式都能取到相应的属性值,它们在性能上也有些许的差别。

一般情况下,使用点号.来获取属性的性能要高于中括号[],因为在解析的过程中使用点号.可以直接根据属性名获取到对应的属性值,而中括号[]需要先进行解析里面的属性名,然后再去查找相应的属性值,因此多了一个解析的过程。

但是在以下两种情况下,只能使用中括号[]来获取属性值:

  1. 属性名包含特殊字符或者是关键字,比如 a['class']。
  2. 属性名是动态生成的,比如 a[${name}]。

在这两种情况下,使用点号.将会出现语法错误,只能使用中括号[]来获取属性值。

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

103.箭头函数和普通函数的区别?【JavaScript】

箭头函数和普通函数是 JavaScript 中两种不同的函数定义方式,它们有以下的区别:

  • 语法不同:箭头函数使用箭头 => 来定义函数,而普通函数使用 function 关键字来定义函数。
  • 箭头函数没有自己的 this,它会继承其所在作用域的 this 值。而普通函数的 this 则由函数调用时的上下文所决定,可以通过 call、apply、bind 方法来改变。
  • 箭头函数没有自己的 arguments 对象,它可以通过 rest 参数语法来接收不定数量的参数。而普通函数则有自己的 arguments 对象,它可以接收任意数量的参数。
  • 箭头函数不能作为构造函数使用,不能使用 new 来实例化,因为它没有自己的 this,而普通函数可以用 new 来创建新的对象。
  • 箭头函数不能使用 yield 关键字来定义生成器函数,而普通函数可以。
  • 箭头函数不支持call()/apply()函数特性
  • 箭头函数没有prototype属性
  • 原型函数不能定义成箭头函数 比如下面这个例子:
function Person(name){
  this.name = name
}

// 原型函数使用箭头函数,其中的this指向全局对象,而不会指向构造函数
// 因此访问不到构造函数本身,也就访问不到实例属性
Person.prototype.say = ()=>{console.log(this.name)}

173.为什么小程序里拿不到dom相关的api【web框架】

小程序为了追求更高的性能和更好的安全性,采用了类Webview的渲染方案,并使用了自己的渲染引擎,与浏览器的渲染引擎不同。因此,小程序的API和浏览器的API并不完全相同。

在小程序中,开发者可以使用WXML语言构建页面,WXML是一种类似HTML的标记语言,但并不是真正的HTML。小程序中的组件是由开发者提前定义好的,而不是由开发者在运行时动态生成的,因此在小程序中无法直接访问和操作DOM。相反,开发者需要使用小程序提供的API来操作组件。

同时,小程序为了保证安全性,也限制了一些操作,如不允许使用eval函数和Function构造函数等动态生成代码的方式。

215.[Vue] 双向绑定和单向数据流原则是否冲突?【web框架】

Vue 的双向绑定和单向数据流原则不冲突,因为它们是针对不同的场景和目的而提出的。

Vue 的双向绑定是指,在模板中通过 v-model 指令可以实现表单元素和组件数据之间的双向绑定,当表单元素的值发生变化时,组件数据也会同步更新;反过来,当组件数据发生变化时,表单元素的值也会同步更新。这种双向绑定的机制可以减少手动编写事件监听器的工作量,提高代码的可读性和可维护性。

而单向数据流原则是指,在 Vue 应用中,数据的流动是单向的,即自上而下单向流动。父组件通过 props 把数据传递给子组件,子组件通过 $emit 事件把数据传递给父组件或者其他祖先组件。这种单向数据流的机制使得数据的变化更加可控和可预测,方便进行状态管理和调试。

双向绑定和单向数据流原则虽然在实现机制上有所不同,但它们都是为了解决不同的问题和提高代码的可维护性和可读性。在实际开发中,可以根据实际情况选择合适的机制来使用。

216.实现 (5).add(3).minus(2) 功能【JavaScript】【出题公司: 百度】

可以通过在 Number 原型上定义 add 和 minus 方法来实现该功能,代码如下:

Number.prototype.add = function(num) {
  return this + num;
};

Number.prototype.minus = function(num) {
  return this - num;
};

console.log((5).add(3).minus(2)); // 输出6

上述代码中,通过在 Number.prototype 上定义 add 和 minus 方法,实现了将数字类型的值转换为 Number 对象,并且可以链式调用这两个方法。最终返回的结果是一个数值类型的值。

217.[Vue] 响应式原理中 Object.defineProperty 有什么缺陷【web框架】【出题公司: 腾讯】

Vue 的响应式原理中使用了 Object.defineProperty 方法来劫持对象的属性,从而实现数据变化时自动更新视图。但是这种方式也存在一些缺陷:

  1. 对象新增属性和删除属性时无法监听:由于 Object.defineProperty 方法需要在对象初始化时就定义属性,因此对于后续新增和删除的属性是无法进行监听的。需要使用 Vue 提供的 $set 和 $delete 方法来进行操作。
  2. 对象属性为数组时需要额外处理:对于数组,虽然可以通过 Object.defineProperty 来监听数组元素的变化,但是无法监听数组的 push、pop、shift、unshift 等方法的调用。Vue 中使用了重写数组原型的方式来实现这一功能。
  3. 性能问题:由于 Object.defineProperty 每次只能劫持一个属性,因此当对象属性较多时,会带来一定的性能问题。Vue 中使用了异步更新和缓存 watcher 的方式来优化性能。

综上所述,虽然 Object.defineProperty 在 Vue 的响应式原理中得到了广泛的应用,但是其也存在一些缺陷。为了解决这些问题,Vue 在其内部实现中进行了一些额外的处理和优化。

218.对象引用类问题:以下代码的执行结果是什么,并解释原因【JavaScript】【出题公司: 百度】

代码如下, 请问执行结果

var a = {n: 1};
var b = a;
a.x = a = {n: 2};

console.log(a.x) 	
console.log(b.x)

执行结果和原因

结果是 undefined 和 {n: 2}

这段代码可以分解为以下步骤:

  1. 创建一个对象 a,属性 n 的值为 1
  2. 将变量 b 指向 ab 现在也引用了这个对象。
  3. 执行赋值语句 a.x = a = {n: 2},其中 a.x 引用的是对象 a 的 x 属性,但是此时 a 的值被重新赋值为一个新的对象 {n: 2}
  4. 所以现在 a 引用的是 {n: 2},而 b 仍然引用原始的对象 {n: 1},且其 x 属性被赋值为 {n: 2}
  5. 所以 console.log(a.x) 结果为 undefined,因为 a 引用的对象没有 x 属性;而 console.log(b.x) 结果为 {n: 2},因为 b 引用的对象的 x 属性被赋值为 {n: 2}

220.opacity: 0visibility: hiddendisplay: none 有啥区别, 主要使用场景是啥子?【CSS】

opacity: 0visibility: hiddendisplay: none 都可以使元素不可见,但它们之间有一些区别。

  • opacity: 0:设置元素透明度为0,元素依然占据原来的空间,并且可以接收到鼠标事件。通常用于实现淡出效果。
  • visibility: hidden:元素不可见,但是仍然占据原来的空间,并且可以接收到鼠标事件。常用于实现菜单的展开和收起。
  • display: none:元素不可见,且不占据空间,也不接收鼠标事件。通常用于实现元素的隐藏和显示。

因为这三种属性的区别,它们在使用场景上也有所不同:

  • opacity: 0:适用于需要实现淡出效果的场景,比如弹出层的显示和隐藏。
  • visibility: hidden:适用于需要占据原来空间的元素,但不需要显示的场景,比如菜单的展开和收起。
  • display: none:适用于需要完全隐藏元素的场景,比如实现一个开关,点击开关后可以隐藏或者显示某个元素。

221.箭头函数为何不能作为构造函数使用?【JavaScript】【出题公司: 腾讯】

在箭头函数中,this指向的是定义时所在的对象,而不是使用时所在的对象。换句话说,箭头函数没有自己的this,而是继承父作用域中的this

看个例子:

var person = {
  name:'张三',
  age:18,
  getName:function(){
     console.log('我的名字是:'+this.name)
  },
  getAge:()=>{
     console.log('我的年龄是:'+this.age)
  }
}

person.getName() // 我的名字是张三
person.getAge()  // 我的年龄是undefined

person.getName()this指向函数的调用者,也就是person实例,因此this.name = "张三"

getAge()通过箭头函数定义,而箭头函数是没有自己的this,会继承父作用域的this,因此person.getAge()执行时,此时的作用域指向window,而window没有定义age属性,所有报undefined

从例子可以得出:对象中定义的函数使用箭头函数是不合适的

先解答下标题问题,为啥箭头函数不能作为构造函数?

// 构造函数生成实例的过程
function Person(name,age){
  this.name = name
  this.age = age
}
var p = new Person('张三',18)

//new关键字生成实例过程如下
// 1. 创建空对象p
var p = {}
// 2. 将空对象p的原型链指向构造器Person的原型
p.__proto__ = Person.prototype
// 3. 将Person()函数中的this指向p
// 若此处Person为箭头函数,而没有自己的this,call()函数无法改变箭头函数的指向,也就无法指向p。
Person.call(p)

构造函数是通过new关键字来生成对象实例,生成对象实例的过程也是通过构造函数给实例绑定this的过程,而箭头函数没有自己的this。创建对象过程,new 首先会创建一个空对象,并将这个空对象的__proto__指向构造函数的prototype,从而继承原型上的方法,但是箭头函数没有prototype。因此不能使用箭头作为构造函数,也就不能通过new操作符来调用箭头函数。

222.给定两个数组,写一个方法来计算它们的交集?【JavaScript】【出题公司: 腾讯】

可以使用 ES6 的 Set 数据结构来实现数组交集。

首先,将一个数组转化为 Set,然后遍历另一个数组,将数组中存在于 Set 中的元素存入结果数组中。

以下是一个示例代码:

function intersection(nums1, nums2) {
  const set1 = new Set(nums1);
  const res = [];

  for (let num of nums2) {
    if (set1.has(num)) {
      res.push(num);
    }
  }

  return res;
}

使用示例:

const nums1 = [1, 2, 2, 1];
const nums2 = [2, 2];

console.log(intersection(nums1, nums2)); // [2]

该算法的时间复杂度为 O(m+n),其中 m 和 n 分别为两个数组的长度。

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

69.前端如何实现即时通讯?【网络】

前端如何实现即时通讯

短轮询

短轮询的原理很简单,每隔一段时间客户端就发出一个请求,去获取服务器最新的数据,一定程度上模拟实现了即时通讯。

  • 优点:兼容性强,实现非常简单
  • 缺点:延迟性高,非常消耗请求资源,影响性能

comet

comet有两种主要实现手段, 一种是基于 AJAX 的长轮询(long-polling)方式, 另一种是基于 Iframe 及 htmlfile 的流(streaming)方式,通常被叫做长连接。

具体两种手段的操作方法请移步 Comet技术详解:基于HTTP长连接的Web端实时通信技术

  • 长轮询优缺点:

    • 优点:兼容性好,资源浪费较小
    • 缺点:服务器hold连接会消耗资源,返回数据顺序无保证,难于管理维护
  • 长连接优缺点:

    • 优点:兼容性好,消息即时到达,不发无用请求
    • 缺点:服务器维护长连接消耗资源

SSE

SSE(Server-Sent Event,服务端推送事件)是一种允许服务端向客户端推送新数据的HTML5技术。

  • 优点:基于HTTP而生,因此不需要太多改造就能使用,使用方便,而websocket非常复杂,必须借助成熟的库或框架
  • 缺点:基于文本传输效率没有websocket高,不是严格的双向通信,客户端向服务端发送请求无法复用之前的连接,需要重新发出独立的请求

Websocket

Websocket是一个全新的、独立的协议,基于TCP协议,与http协议兼容、却不会融入http协议,仅仅作为html5的一部分,其作用就是在服务器和客户端之间建立实时的双向通信。

  • 优点:真正意义上的实时双向通信,性能好,低延迟
  • 缺点:独立与http的协议,因此需要额外的项目改造,使用复杂度高,必须引入成熟的库,无法兼容低版本浏览器

Web Worker

Web Worker 的作用,就是为 JavaScript 创造多线程环境,允许主线程创建 Worker 线程,将一些任务分配给后者运行

Service workers

Service workers 本质上充当Web应用程序与浏览器之间的代理服务器,也可以在网络可用时作为浏览器和网络间的代理,创建有效的离线体验。

206.浏览器缓存中 Memory Cache 和 Disk Cache, 有啥区别?【网络】【出题公司: 字节跳动】

Memory Cache 和 Disk Cache 的区别

在浏览器缓存中,Memory Cache 和 Disk Cache 是两种不同的缓存类型,它们有以下区别:

  1. 存储位置:Memory Cache 存储在内存中,而 Disk Cache 存储在硬盘中。
  2. 读取速度:Memory Cache 读取速度比 Disk Cache 快,因为内存访问速度比硬盘访问速度快。
  3. 存储容量:Memory Cache 存储容量比较小,一般只有几十兆,而 Disk Cache 存储容量比较大,可以有数百兆或者更多。
  4. 生命周期:Memory Cache 生命周期短暂,一般只在当前会话中有效,当会话结束或者浏览器关闭时,Memory Cache 就会被清空;而 Disk Cache 生命周期比较长,数据可以被保存很长时间,即使浏览器关闭了,下次打开还可以使用。

一般来说,浏览器在请求资源时,会优先从 Memory Cache 中读取,如果没有找到再去 Disk Cache 中查找。如果两种缓存中都没有找到,则会向服务器发送请求。如果需要强制刷新缓存,可以通过清空浏览器缓存来实现。

什么情况下资源会缓存在 Memory Cache, 什么情况下会缓存在 Disk Cache ?

浏览器中的缓存是为了提高网页访问速度和减少网络流量而存在的。缓存分为 Memory Cache 和 Disk Cache 两种。

Memory Cache 是浏览器内存缓存,资源会被缓存在内存中,由于内存读取速度快,所以 Memory Cache 的读取速度也较快。资源被缓存在 Memory Cache 中的情况有:

  1. 当前页面中通过 或者
  2. 当前页面通过 XMLHttpRequest 或 Fetch API 请求获取到的资源。

Disk Cache 是浏览器磁盘缓存,资源会被缓存在磁盘中。由于磁盘读取速度相对内存较慢,所以 Disk Cache 的读取速度也较慢。资源被缓存在 Disk Cache 中的情况有:

  1. 当前页面中通过 <img> 标签引入的资源;
  2. 当前页面中通过 <audio> 或 <video> 标签引入的资源;
  3. 当前页面中通过 iframe 加载的资源;
  4. 当前页面中通过 WebSocket 加载的资源;
  5. 通过 Service Worker 缓存的资源。

一般来说,比较大的资源会被缓存到 Disk Cache 中,而较小的资源则会被缓存到 Memory Cache 中。如果需要手动清除缓存,可以在浏览器设置中找到相应选项进行操作。

223.介绍下如何实现 token 加密?【网络】

Token 是一种常用的身份验证机制,通常被用于 Web 应用程序的用户身份验证。Token 的生成和使用可以使用加密技术来增强安全性,下面介绍一下如何实现 Token 加密。

Token 加密一般有两个步骤:

  1. 生成 Token
  2. 加密 Token

1. 生成 Token

在生成 Token 时,需要将用户的一些信息进行组合,生成一个字符串,该字符串通常包括以下信息:

  1. 用户的唯一标识(如用户 ID)
  2. 时间戳
  3. 有效期

这些信息可以用分隔符分隔开,如用“.”分隔。

2. 加密 Token

加密 Token 有多种方式,下面介绍两种常用的方式:

1. 对称加密

对称加密是指加密和解密使用同一密钥的加密算法。对称加密的优点是加解密速度快,缺点是密钥传输容易被截获,从而影响安全性。

常用的对称加密算法有 DES、3DES、AES 等。

2. 非对称加密

非对称加密是指加密和解密使用不同密钥的加密算法。非对称加密的优点是密钥传输安全,缺点是加解密速度较慢。

常用的非对称加密算法有 RSA、DSA 等。

一般情况下,为了兼顾安全性和效率,通常采用混合加密,即先使用非对称加密算法对 Token 进行加密,再使用对称加密算法对加密后的 Token 进行加密。

综上所述,实现 Token 加密的关键在于对 Token 的生成和加密,需要根据具体业务需求来选择合适的加密算法和加密方式。

225.ES6 代码转成 ES5 代码的实现思路是什么?【JavaScript】【出题公司: 阿里巴巴】

ES6 代码转成 ES5 代码的实现思路主要是通过使用 Babel 这样的工具来实现。Babel 是一个广泛使用的 JavaScript 编译器,可以将 ES6 代码转换成向后兼容的 ES5 代码,从而在现有的浏览器和环境中运行。其主要实现思路如下:

  1. 词法分析:Babel 首先会将输入的代码进行词法分析,将代码分割成一个个词法单元。
  2. 语法分析:接下来 Babel 会对分割后的词法单元进行语法分析,生成抽象语法树(AST)。
  3. 转换:通过对 AST 进行遍历和修改,Babel 将 ES6 代码转换成 ES5 代码。
  4. 代码生成:最后,Babel 会根据转换后的 AST 生成可运行的 ES5 代码。

在转换过程中,Babel 会根据预定义的插件和预设对代码进行转换。插件和预设可以分别处理一些特定的语法和功能,如箭头函数、类和模块等。同时,Babel 还支持开发者自定义插件和预设来处理更加特殊和个性化的需求。