来自 上海某中小型新能源公司
反思:算法还是不够扎实,需要借助资料回忆,不能在面试中信手拈来
努力回忆......
第一问:池、堆、栈是用来存储什么的?
栈:存放基本类型的数据和对象的引用,但对象本身不存放在栈中,而是存放在堆中。
堆: 堆中内存,存放的是对象实例,以及常量池。
池:有时候,我们不需要new出新对象,而是让栈中的地址直接指向常量池的常量。
第二问:JS原型链
原型链就是实例对象和原型对象之间的链接。每个函数都有一个prototype属性,这个prototype属性就是我们的原型对象,我们拿这个函数通过new构造函数创建出来的实例对象,这个实例对象自己会有一个指针(_proto_)指向他的构造函数的原型对象!这样构造函数和实例对象之间就通过( _proto_ )连接在一起形成了链条。
原型对象:JS的每个函数在创建的时候,都会生成一个属性prototype,这个属性指向一个对象,这个对象就是此函数的原型对象。该原型对象中有个属性为constructor,指向该函数。这样原型对象和它的函数之间就产生了联系。
第三问:event loop
Event Loop 即事件循环, 是JavaScript或Node为解决单线程代码执行不阻塞主进程一种机制,也就是我们所说的异步原理。事件循环负责执行代码、收集和处理事件以及执行队列中的子任务。
JS为什么是单线程的?
JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊。
JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?
所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。
为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。
回到问题 event loop
整体的script(作为第一个宏任务)开始执行的时候,会把所有代码分为两部分:“同步任务”、“异步任务”;
同步任务会直接进入主线程依次执行;
异步任务会再分为宏任务(进入宏任务队列) 和 微任务(进入微任务队列)。
当主线程内的任务执行完毕(主线程为空时),会检查微任务的任务队列,如果有任务,就进入主线程全部执行,如果没有就从宏任务队列读取下一个宏任务执行;
每执行完一个宏任务就清空一次微任务队列,此过程会不断重复,这就是Event Loop;
第四问:ES6 async await函数
- async 是异步的意思,await则可以理解为 async wait。所以可以理解async就是用来声明一个异步方法,而 await是用来等待异步方法执行
- async作为一个关键字放在函数前面,表示该函数是一个异步函数,异步函数意味着该函数的执行不会阻塞后面代码的执行;而 await 用于等待一个异步方法执行完成;
- 当函数内部执行到一个 await 语句的时候,如果语句返回一个 promise 对象,那么函数将会等待 promise 对象的状态变为 resolve 后再继续往下执行。并会阻塞该函数内后面的代码。
- 因此async/await的作用就是将异步逻辑,转化为同步的顺序来书写,并且这个函数可以自动执行。
- 为了优化 .then 链而开发出来的。
第五问:在一个方法里面,有两个网络请求A和B,分别对应A方法和B方法,此时有await A(), 后面跟着await B(), 请问这是并行请求还是串行请求
串行请求
也就是说,B 方法中的网络请求会等待 A 方法中的网络请求完成后才会执行。
在 JavaScript 中,使用 await 关键字可以等待一个异步操作的完成,并且在该操作完成后再继续执行后续的代码。因此,当使用 await A() 后面跟着 await B() 的方式时,它们会按照顺序依次执行,而不是同时执行。
第六问,如果想把串行改成并行应该怎么做
将这两个网络请求并行执行,可以将它们放到 Promise.all() 方法中,并使用 await 等待 Promise.all() 的完成
async function myMethod() { await Promise.all([A(), B()]); // 这里可以处理 A 和 B 请求完成后的逻辑 }
第七问:Redux
第八问:设计模式相关,对应的场景
观察者模式(Observer Pattern):
- 场景:当一个对象的状态发生变化时,需要通知其他多个对象,并且这些对象可能需要根据该状态变化做出响应。
- 示例:在一个在线聊天应用中,当某个用户发送一条消息时,所有相关的用户会收到该消息的更新。
单例模式(Singleton Pattern):
- 场景:当只需要实例化一个对象,并在任何地方都能访问该对象时。
- 示例:在一个全局状态管理器(如 Redux)中,只需单个实例来管理整个应用的状态。
工厂模式(Factory Pattern):
- 场景:当需要根据不同的条件来创建相似对象时。
- 示例:在一个电商网站中,根据用户的购买历史,显示不同的推荐商品,需要使用工厂模式来创建不同类型的推荐商品对象。
适配器模式(Adapter Pattern):
- 场景:当需要将一个对象的接口转换成另一个对象所期望的接口时。
- 示例:在一个音频播放器应用中,需要将不同格式的音频文件(如 MP3、WAV)转换为统一的接口以供播放。
策略模式(Strategy Pattern):
- 场景:当有多个算法或策略可以通用地应用于同一类问题时。
- 示例:在一个表单验证的场景中,可以使用不同的验证策略(如长度、格式等)来根据不同的表单字段进行验证。
装饰器模式(Decorator Pattern):
- 场景:当需要在不修改现有对象结构的情况下,动态地添加额外的行为或功能时。
- 示例:在一个日志记录的场景中,可以使用装饰器模式来为现有的对象动态地添加日志记录功能。
第九问:工程方面的问题,实现一个登录的模块,从前端到后端怎么设计或者说流程
前端设计:
- 创建登录表单:在前端创建一个登录表单,包含用户名和密码的输入字段,以及一个登录按钮。
- 表单验证:在前端对用户名和密码进行基本的表单验证,例如检查是否为空、长度限制等。
- 发起登录请求:当用户点击登录按钮时,在前端创建一个登录请求,将用户名和密码发送给后端。
后端设计:
- 接收登录请求:后端需要创建一个接口来接收前端发送的登录请求。该接口应该使用 POST 方法,并接收用户名和密码作为请求参数。
- 校验用户信息:后端接收到登录请求后,首先需要对接收到的用户名和密码进行校验。这可以包括验证用户名是否存在、密码是否正确等。
- 创建会话:如果用户提供的用户名和密码是有效的,则后端可以创建一个会话来跟踪用户的登录状态。可以使用会话 ID、JWT(JSON Web Token)等方式进行会话管理,并将会话相关的信息存储在后端或数据库中。
- 返回登录结果:根据登录请求的校验结果,后端需要将结果返回给前端。如果登录成功,通常会返回一个成功的响应,可能包含一些额外的用户信息。如果登录失败,可能会返回一个错误消息或状态码。
安全性考虑:
- 加密密码:建议在前端对用户的密码进行加密,例如使用加盐哈希算法(如 bcrypt)来保护用户密码的安全性。
- 防止暴力破解:前端和后端都应该实施一些策略来防止暴力破解,例如限制登录尝试次数。
第十问:手写算法题目
1)从1到n的和,用递归的方式
function sumRecursive(n) {
// 递归的终止条件
if (n === 1) {
return 1;
}
// 调用递归函数来计算前 n-1 个数的和,并将结果与当前数 n 相加
return n + sumRecursive(n - 1);
}
// 示例:计算从 1 到 5 的和
const result = sumRecursive(5);
console.log(result); // 输出 15
2)对于一个数组,找到符合要求的元素,该元素要比左边所有的数都小,比右边所有的数都大
function findElement(arr) {
for (let i = 1; i < arr.length - 1; i++) {
let currentElement = arr[i];
let isLeftSmaller = true;
let isRightBigger = true;
// 检查当前元素是否比左边所有的数都小
for (let j = 0; j < i; j++) {
if (arr[j] >= currentElement) {
isLeftSmaller = false;
break;
}
}
// 检查当前元素是否比右边所有的数都大
for (let k = i + 1; k < arr.length; k++) {
if (arr[k] <= currentElement) {
isRightBigger = false;
break;
}
}
// 如果当前元素符合要求,则返回
if (isLeftSmaller && isRightBigger) {
return currentElement;
}
}
// 如果没有找到符合要求的元素,则返回 null 或其他值
return null;
}
// 示例:查找符合要求的元素
const array = [2, 5, 3, 7, 1, 8, 9];
const result = findElement(array);
console.log(result); // 输出 7