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: 0、visibility: hidden、display: 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。其中,使用点号取值的方式称为“点操作符”,使用方括号取值的方式称为“方括号操作符”。
两种方式的区别在于:
- 点操作符必须使用标识符作为属性名,而方括号操作符可以使用任何字符串作为属性名。
- 点操作符在代码书写上更加简洁直观,而方括号操作符可以动态地构造属性名。
因此,在使用时应根据具体的情况选择合适的方式。例如,如果属性名是固定的,建议使用点操作符;如果属性名需要根据变量或其他动态条件构造,则需要使用方括号操作符。
性能区别
在对象属性的取值操作中,使用点号.和中括号[]两种方式都能取到相应的属性值,它们在性能上也有些许的差别。
一般情况下,使用点号.来获取属性的性能要高于中括号[],因为在解析的过程中使用点号.可以直接根据属性名获取到对应的属性值,而中括号[]需要先进行解析里面的属性名,然后再去查找相应的属性值,因此多了一个解析的过程。
但是在以下两种情况下,只能使用中括号[]来获取属性值:
- 属性名包含特殊字符或者是关键字,比如 a['class']。
- 属性名是动态生成的,比如 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 方法来劫持对象的属性,从而实现数据变化时自动更新视图。但是这种方式也存在一些缺陷:
- 对象新增属性和删除属性时无法监听:由于
Object.defineProperty方法需要在对象初始化时就定义属性,因此对于后续新增和删除的属性是无法进行监听的。需要使用 Vue 提供的$set和$delete方法来进行操作。 - 对象属性为数组时需要额外处理:对于数组,虽然可以通过
Object.defineProperty来监听数组元素的变化,但是无法监听数组的 push、pop、shift、unshift 等方法的调用。Vue 中使用了重写数组原型的方式来实现这一功能。 - 性能问题:由于
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}。
这段代码可以分解为以下步骤:
- 创建一个对象
a,属性n的值为1。 - 将变量
b指向a,b现在也引用了这个对象。 - 执行赋值语句
a.x = a = {n: 2},其中a.x引用的是对象a的x属性,但是此时a的值被重新赋值为一个新的对象{n: 2}。 - 所以现在
a引用的是{n: 2},而b仍然引用原始的对象{n: 1},且其x属性被赋值为{n: 2}。 - 所以
console.log(a.x)结果为undefined,因为a引用的对象没有x属性;而console.log(b.x)结果为{n: 2},因为b引用的对象的x属性被赋值为{n: 2}。
220.opacity: 0、visibility: hidden、display: none 有啥区别, 主要使用场景是啥子?【CSS】
opacity: 0、visibility: hidden、display: 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 是两种不同的缓存类型,它们有以下区别:
- 存储位置:Memory Cache 存储在内存中,而 Disk Cache 存储在硬盘中。
- 读取速度:Memory Cache 读取速度比 Disk Cache 快,因为内存访问速度比硬盘访问速度快。
- 存储容量:Memory Cache 存储容量比较小,一般只有几十兆,而 Disk Cache 存储容量比较大,可以有数百兆或者更多。
- 生命周期:Memory Cache 生命周期短暂,一般只在当前会话中有效,当会话结束或者浏览器关闭时,Memory Cache 就会被清空;而 Disk Cache 生命周期比较长,数据可以被保存很长时间,即使浏览器关闭了,下次打开还可以使用。
一般来说,浏览器在请求资源时,会优先从 Memory Cache 中读取,如果没有找到再去 Disk Cache 中查找。如果两种缓存中都没有找到,则会向服务器发送请求。如果需要强制刷新缓存,可以通过清空浏览器缓存来实现。
什么情况下资源会缓存在 Memory Cache, 什么情况下会缓存在 Disk Cache ?
浏览器中的缓存是为了提高网页访问速度和减少网络流量而存在的。缓存分为 Memory Cache 和 Disk Cache 两种。
Memory Cache 是浏览器内存缓存,资源会被缓存在内存中,由于内存读取速度快,所以 Memory Cache 的读取速度也较快。资源被缓存在 Memory Cache 中的情况有:
- 当前页面中通过 或者
- 当前页面通过 XMLHttpRequest 或 Fetch API 请求获取到的资源。
Disk Cache 是浏览器磁盘缓存,资源会被缓存在磁盘中。由于磁盘读取速度相对内存较慢,所以 Disk Cache 的读取速度也较慢。资源被缓存在 Disk Cache 中的情况有:
- 当前页面中通过
<img>标签引入的资源; - 当前页面中通过
<audio>或<video>标签引入的资源; - 当前页面中通过
iframe加载的资源; - 当前页面中通过
WebSocket加载的资源; - 通过
Service Worker缓存的资源。
一般来说,比较大的资源会被缓存到 Disk Cache 中,而较小的资源则会被缓存到 Memory Cache 中。如果需要手动清除缓存,可以在浏览器设置中找到相应选项进行操作。
223.介绍下如何实现 token 加密?【网络】
Token 是一种常用的身份验证机制,通常被用于 Web 应用程序的用户身份验证。Token 的生成和使用可以使用加密技术来增强安全性,下面介绍一下如何实现 Token 加密。
Token 加密一般有两个步骤:
- 生成 Token
- 加密 Token
1. 生成 Token
在生成 Token 时,需要将用户的一些信息进行组合,生成一个字符串,该字符串通常包括以下信息:
- 用户的唯一标识(如用户 ID)
- 时间戳
- 有效期
这些信息可以用分隔符分隔开,如用“.”分隔。
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 代码,从而在现有的浏览器和环境中运行。其主要实现思路如下:
- 词法分析:Babel 首先会将输入的代码进行词法分析,将代码分割成一个个词法单元。
- 语法分析:接下来 Babel 会对分割后的词法单元进行语法分析,生成抽象语法树(AST)。
- 转换:通过对 AST 进行遍历和修改,Babel 将 ES6 代码转换成 ES5 代码。
- 代码生成:最后,Babel 会根据转换后的 AST 生成可运行的 ES5 代码。
在转换过程中,Babel 会根据预定义的插件和预设对代码进行转换。插件和预设可以分别处理一些特定的语法和功能,如箭头函数、类和模块等。同时,Babel 还支持开发者自定义插件和预设来处理更加特殊和个性化的需求。