2023-5-15 小红书一面准备
2023-5-15 小红书一面准备
Juns2023-5-15 小红书一面准备
岗位:安全前端
地点:上海
15 点给我打电话吧,就说 17 点面试,感觉确实很着急
大小周
23 春招 4.23 安全前端一面二面 过
4.23 一面
面试时间 1h,面试官很温和,比隔壁美团好太多了。至少先介绍了部门的情况,面试会给反馈,遇到不太会的会引导
- 介绍了 xhs 的部门,然后让我自我介绍
- 说说实习遇到最有挑战性的项目吧,是怎么解决的
- 在滴滴的实习主要干什么,产出多少
- vue3 对比 vue2 区别
- vue-router 两种模式和实现原理
- keep-alive 使用场景,你觉得和 vue-router 结合能怎么用
- watch 和 watchEffect 区别
- vue 的 diff 算法
- 怎么前端性能优化
- webpack 的 plugin 和 loader 区别和原理,有自己写过吗
- 手写题:实现个防抖函数
- 手写 promise,要求实现 resolve、reject、then 方法
反问:
- 项目组技术栈(vue 居多)
- 工作强度(大小周,说到这面试官都笑了 hhh)
- 面试官反问:有其他在流程的企业吗,我说有,然后说那尽快帮我推进一下进度
先不说 xhs 招不招人吧,面试体验确实比较好,许愿个二面吧
4.25 二面
4.27 更新 约了当天下午 hr 面
过程有点逆天,今天上午上着课呢,hr 突然打电话说最近这周都在忙绩效,原定今天面试拖到五一后,过十分钟又打电话说一面面评很好,想要优先推进,还是今天下午面试
总时长大概 50 分钟,没有手撕,只有项目实习拷打,八股随便问了两个很简单的
- 相互介绍
- 对我的弹幕播放器项目很感兴趣,想要深入了解,问我 GitHub 地址,我说没上传,问我能不能现场演示,我说目前电脑没这份代码。。。。。估计把他也整不会了,他说那就直接开始问项目吧,问弹幕实现原理,hls 切片原理,怎么做前端优化,同时实现很多弹幕不会很卡吗等等等,挖得特别深。
- 实习经历拷打,问我滴滴干了啥,页面上线了吗,让我展示一下页面,问了很多具体的工作细节,包括产出的模态框组件怎么设计的,相较原来的有什么优点,有遇到什么难题怎么解决的等等等,反正问了非常久,挖得很深
- 开始闲聊。问滴滴氛围,发布流程,排期时间,一个前端对几个后端等
- 非常随意的八股。vue 的 diff 算法,生命周期函数。只问了这两个
- 反问:和上一个面试官做相同的东西吗?(我是全栈,上一个面试官是后端,但是都有接触过前端)(哈哈哈)
- 反问:可以选 base 吗(可以)
- 可以转正吗(这个暑期岗位开放就是可以转正的,但是也要看你的表现)
- 流程什么时候可以推进呢(直接说你二面过了等 hr 罢)
- 面试官反问:收到几个 offer?
感觉这个部门确实比较缺人的吧,问题都很开放,也没手撕,感觉就是八股完全没用,亏我还准备了那么久
结果是好的,等着 hr 面吧,顺便许愿个 offer
日常实习 数据部门 一二面 过
4.4 上海小红书一面(65min)
实习项目的难点说一下
平时是怎么学习前端的?
讲一下 vue-router ✅
- 用于构建单页面应用 SPA 讲一下 vue-router
- 支持路由配置,嵌套路由,路由导航守卫,懒加载
history 模式怎么解决刷新 404 的问题?❌
- 因为刷新页面会重新向服务器发送请求,但是服务器没有相应的路由配置,所以 404 了
- 解决方法是进行服务器配置
- 后端服务器配置:在后端服务器上配置一个 fallback 页面,即无论用户请求的是哪个 URL,都返回同一个 HTML 页面。这样,当用户刷新页面时,服务器会返回这个页面,然后 Vue Router 会根据当前 URL 解析路由并正确加载相应的组件。
- 使用服务器重定向:在服务器配置中,将所有非静态资源的请求重定向到 index.html 页面。这样,无论用户请求的是哪个 URL,都会被重定向到 index.html,然后 Vue Router 会根据当前 URL 进行路由匹配和加载。
- 使用后端路由配置:在后端服务器上配置与前端路由相匹配的路由规则,并将这些路由指向同一个 HTML 页面。这样,当用户请求这些路由时,服务器会返回同一个 HTML 页面,并由前端的 Vue Router 处理路由逻辑。
react 都知道哪些 hook? ✅
- useState:用于在函数组件中添加状态管理,可以通过它定义和更新状态变量。
- useEffect:用于在组件渲染完成后执行副作用操作,比如订阅事件、发送网络请求等。
- useContext:用于在函数组件中访问 React 的上下文(Context),可以获取和更新上下文中的值。
- useReducer:用于在函数组件中实现复杂的状态管理,类似于 Redux 中的 reducer,通过定义不同的 action 类型来更新状态。
- useCallback:用于缓存函数,避免函数在每次渲染时都重新创建,提高性能。
- useMemo:用于缓存计算结果,只有在依赖项发生变化时才重新计算。
- useRef:用于在函数组件中创建可变的引用,可以用来存储任意可变值,类似于类组件中的实例属性。
useState 的原理有了解过吗? ⭕️
useEffect 说一下,接收几个参数? ✅
第一个参数前面能加 async 吗? ⭕️
useRef 的用法 ✅
useCallback 是干嘛的? ✅
如果给子组件传函数,怎么性能优化? ✅
setState 是同步还是异步 ✅
什么情况下 setState 是同步 ⭕️
var let const 的区别 ✅
看代码说输出(构造函数原型链相关) ✅
做题:对象拍平 ⭕️
let obj = { a: 1, age: 234, arr: [12, 4, 5, 65], cs: { sg: { g: 'sgd', }, }, } function flattenObject(obj) { let res = {} function dfs(obj, prefix = '') { for (let key in obj) { if (obj.hasOwnProperty(key)) { const propKey = prefix ? `${prefix}.${key}` : key if (typeof obj[key] === 'object' && obj[key] !== null) { dfs(obj[key], propKey) } else { res[propKey] = obj[key] } } } } dfs(obj) return res } console.log(flattenObject(obj))
17. 做题:有效的括号 ✅
1. ```js
/**
* @param {string} s
* @return {boolean}
*/
var isValid = function (s) {
let stack = []
for (let val of s) {
if (val === '(') stack.push(')')
else if (val === '[') stack.push(']')
else if (val === '{') stack.push('}')
else if (stack.length === 0 || val !== stack.pop()) return false
}
return stack.length === 0
}
反问:技术栈?答 vue3+ts
4.4 上海小红书二面(70min)
问了很多框架的原理性东西,记不全了……
- vue 和 react 有什么区别? ✅
- 什么是受控组件? ✅
- 你实现过受控组件吗? ✅
- 完全受控组件和非完全受控组件 ❌
- 讲一下 diff 算法 ✅
- redux 是怎么用的? ✅
- react 中异步请求你都在哪里发送? ✅
- ES6 有哪些新特性? ✅
- 说一说 class ✅
- class 中静态属性怎么定义 ✅
- 常见的数据类型有哪些? ✅
- 检测数据类型的方法?区别?✅
- react 是怎么监视数据改变的? ⭕️
- 有没有用过 suspense(没听说过) ❌
- vue 中组件通信的方式 ✅
- 那 react 中是怎么通信的? ⭕️
- 详细讲讲事件总线的用法 ⭕️
- 别说了,你给我实现一下吧 ⭕️
- 你有什么要问的吗?(没有)
二面完一小时 oc,晚上 offer()
日常实习 研发效能 一二面 过
2.22 一面
代码题:
1、手写 flat
let arr = [1233, [434, [344, [3434]]]] |
2、手写发布订阅模式 once
断点续传怎么做的
秒传怎么实现
var let const 块级作用域
ts Partial Omit 怎么实现的
箭头函数有哪些限制
箭头函数为什么不能作为构造函数
promise 用过那些 api
promise.any
数组有哪些方法
map 和 forEach 对于对象类型会不会改变
Map 和 Object 的区别
vue 怎么实现双向绑定
2.24 二面
代码题:
1、实现栈,有入栈出栈的方法,以及 length 属性(怎样保证 length 外部不可修改)
2、一个简单的数组遍历问题,就不描述了
然后就是问项目:
1、如何封装组件
2、单页应用怎么跨页面传参
3、权限怎么设计的
4、项目中的懒加载怎么做到的
当晚 oc
牛客上面的面试题
断点续传怎么做的
- 客户端发起文件上传请求时,向服务端发送一个 HTTP 请求,包含文件名、文件大小等信息。
- 服务端接收到请求后,根据文件名和文件大小创建一个空文件,并返回已经上传的文件大小,如果之前有部分数据已经上传过,那么服务端可以根据已上传的大小确定从哪个位置开始上传。
- 客户端接收到服务端返回的已经上传的文件大小后,可以根据已上传的大小计算出下一次应该从哪个位置开始上传。 客户端将文件切分为多个小块,每个小块的大小一般为几十 KB 或几百 KB,将每个小块的数据上传到服务端,并在每个小块的末尾添加一个分界符,标记上传结束。
- 服务端接收到每个小块后,将其存储到文件对应的位置,等待下一次上传。 如果上传过程中出现中断,客户端可以记录已经上传的文件大小和已经上传的小块数,下次上传时从上一次中断的位置开始继续上传。
- 总的来说,断点续传的实现需要客户端和服务端的相互配合,并需要考虑文件上传的稳定性和可靠性。
秒传怎么实现
- 一种常见的实现方式是在上传文件时,先计算文件的哈希值或者 MD5 值,并将其作为文件的唯一标识。然后将该标识与后台数据库中已经存在的文件的标识进行比对。如果存在相同的标识,说明该文件已经上传过,那么就直接使用已经存在的文件;如果不存在相同的标识,说明该文件是一个新文件,那么就进行正常的上传操作。
- 在实现过程中,可以通过在前端计算文件哈希值或 MD5 值的方式,避免上传重复的文件,减少服务器的压力,提高文件上传的效率。同时,后台服务器也需要维护一个文件信息的数据库,用于存储已经上传过的文件的唯一标识和存储路径等信息,以便实现秒传功能。
var let const 块级作用域
- var 声明变量的作用域是函数级别的,不受块级作用域的限制。在全局作用域中声明的变量会成为全局对象的属性。
- let 声明的变量是块级作用域的,只在声明的块内有效。在 for 循环中,每次迭代都会创建一个新的变量。
- const 声明的变量也是块级作用域的,和 let 类似,但是其值不能被重新赋值,只能被赋值一次。
ts Partial Omit 怎么实现的
箭头函数有哪些限制
- 无法使用 arguments 对象,需通过剩余参数(rest parameters)或默认参数来获取传递给函数的参数列表;
- 无法使用 new 关键字调用函数,箭头函数没有自己的 this 值,它会捕获其所在的上下文中的 this 值;
- 无法使用 yield 关键字,因此箭头函数不能用作 generator 函数;
- 箭头函数没有自己的作用域,它会捕获所在上下文的作用域链(scope chain),因此对于闭包的使用需要特别注意。
箭头函数为什么不能作为构造函数
- 没有自己的 this 值,箭头函数中的 this 值是在定义时确定的,不能通过 new 关键字来改变 this 的指向。
而构造函数中,this 的指向是随着实例化对象而改变的,因此箭头函数不具备构造函数的特性,不能用来创建对象。
Promise 用过哪些 api
- then():指定 Promise 成功(resolve)和失败(reject)时的回调函数,接受两个参数,一个是成功时的回调函数,一个是失败时的回调函数。
- catch():指定 Promise 拒绝(reject)时的回调函数,接收一个参数,即拒绝时的回调函数。
- finally():无论 Promise 成功还是失败,都会执行的回调函数,没有参数。
- all():接收一个 Promise 数组,返回一个新的 Promise,当所有 Promise 都成功(resolve)时,返回值为数组,包含所有 Promise 成功时的结果;当其中任何一个 Promise 失败(reject)时,返回值为失败的 Promise 的值。
- race():接收一个 Promise 数组,返回一个新的 Promise,当其中任何一个 Promise 成功(resolve)或失败(reject)时,返回值为该 Promise 的值。
- Promise.resolve():返回一个解决(resolve)的 Promise 对象,可以指定解决的值。
- Promise.reject():返回一个拒绝(reject)的 Promise 对象,可以指定拒绝的原因。
- Promise.allSettled():接收一个 Promise 数组,返回一个新的 Promise,当所有 Promise 都结束时,返回值为数组,包含所有 Promise 的状态和值。
- Promise.any():接收一个 Promise 数组,返回一个新的 Promise,当其中任何一个 Promise 成功(resolve)时,返回值为成功的 Promise 的值;当所有 Promise 失败(reject)时,返回值为失败的 Promise 的值。 以上是 Promise 常见的方法和静态方法,可以用来管理异步操作的状态和结果。
Map 和 Object 的区别
- 都可以用来存储键值对
- 键的类型:Map 中的键可以是任意类型,包括原始类型和对象引用,而 Object 的键必须是字符串或者 Symbol 类型。
- 插入顺序:Map 保留了插入顺序,因此在迭代 Map 时会按照插入顺序返回元素,而 Object 不保留插入顺序。
- 大小:Map 可以使用 size 属性来获取元素的数量,而 Object 只能手动计算属性的数量。
- 迭代:Map 支持 for-of 循环和 forEach 方法来迭代元素,而 Object 只能使用 for-in 循环来迭代属性,但是需要手动判断是否是自身属性而不是继承属性。
- 性能:在非常大的数据集合下,Map 的查找和插入操作通常比 Object 更快。
vue 怎么实现双向绑定
创建一个Observer对象,对模型数据进行监听,当数据发生变化时,通知订阅者。 创建一个Directive对象,它是指令,负责将模型数据渲染到页面中。
创建一个Watcher对象,它是订阅者,当 Observer 对象监听到数据发生变化时,通知 Watcher 对象,Watcher 对象再调用 Directive 对象,更新页面中的数据。
将 Watcher 对象添加到订阅者列表中,当模型数据发生变化时,Observer 对象会遍历订阅者列表,通知所有的 Watcher 对象。
将 Directive 对象添加到节点上,当数据发生变化时,Directive 对象会调用 Watcher 对象,更新页面中的数据。
Vue 使用了 ES5 中的 Object.defineProperty()方法来实现数据的劫持,通过 get 和 set 方法来实现对数据的监听,从而实现了双向绑定。
Object.defineProperty(person, 'nameString', { get() { return nameT }, set(val) { nameT = val }, })
7. ```js
let person = {
name: '张三',
age: 12,
}
let handler = {
get(obj, key) {
console.log('get')
return key in obj ? obj[key] : '不存在'
},
set(obj, key, val) {
console.log('set')
obj[key] = val
// MDN上明确指出set()方法应该返回一个布尔值,否则会报错TypeError。
return true
},
}
// 代理对象
let proxy = new Proxy(person, handler)
console.log(proxy.name)
proxy.age = 15
console.log(proxy.age)
实现栈,有入栈出栈的方法,以及 length 属性
class Stack { constructor() { this.item = [] } push(element) { this.item.push(element) } pop() { if (this.item.length === 0) return null return this.item.pop() } peek() { return this.item[this.item.length - 1] } isEmpty() { return this.item.length === 0 } size() { return this.item.length } clear() { this.item = [] } } let stack = new Stack() console.log(stack.isEmpty()) // true stack.push(1) stack.push(2) stack.push(3) console.log(stack.peek()) // 3 stack.pop() console.log(stack.size()) // 2 console.log(stack.peek()) // 2 stack.clear() console.log(stack.isEmpty()) // true
12. 如何封装组件
1. 定义组件 API:在设计组件时,首先需要考虑它的使用方式。定义组件 API 有助于其他开发人员快速理解如何使用组件,并确保组件在不同情况下具有一致的行为。
2. 使用 props 传递数据:在 Vue 中,组件通过 props 属性接收父组件传递的数据。在定义组件时,可以指定 props 属性的类型、默认值和必需性等。 使用 slot 插槽分发内容:插槽是 Vue 中一种非常有用的功能,它可以允许组件的使用者插入内容到组件中。
3. 通过使用插槽,可以增加组件的灵活性,允许组件的使用者在不同的情况下自由地定制组件的外观和行为。
4. 使用事件向外通信:组件通常需要与外部环境进行交互。Vue 中通过使用自定义事件可以实现组件向外部环境通信的功能。通过定义事件并在组件中触发该事件,可以允许外部环境对组件进行操作。
5. 使用 mixins 实现代码复用:Vue 中提供了 mixins 功能,可以将一个或多个属性和方法混合到组件中。通过使用 mixins,可以将常用的功能提取到可重用的模块中,减少代码的重复性。
6. 使用插件扩展 Vue:Vue 插件是一种可以扩展 Vue 功能的机制。通过编写插件,可以为 Vue 添加全局功能、指令、过滤器等。使用插件可以提高开发效率,减少代码量。
7. 使用动态组件:Vue 中提供了动态组件的功能。通过使用动态组件,可以允许组件的使用者在运行时根据不同的条件选择不同的组件。
13. 单页应用怎么跨页面传参
1. URL 参数传递:可以通过在 URL 中添加参数的方式来实现页面间传递数据。例如:/page?id=123&name=xxx,在目标页面可以通过`$router.query.id/$router.query.name`来获取参数。
2. localStorage/sessionStorage:可以将需要传递的数据存储在 localStorage/sessionStorage 中,在目标页面再次读取该数据。
3. Vuex/Pinia:Vuex 是 Vue.js 的状态管理库,可以在不同的组件中共享数据。可以将需要传递的数据存储在 Vuex 中,在目标页面中再次读取该数据。
4. EventBus:EventBus 是 Vue.js 的一个事件管理库,可以在不同的组件中广播和监听事件。可以通过 EventBus 在不同页面之间传递数据。
5. PostMessage:可以通过 window.postMessage()方法向另一个页面发送消息,目标页面再通过 window.addEventListener()监听 message 事件来获取传递过来的数据。
14. 权限怎么设计的
15. map 和 forEach 对于对象类型会不会改变
1. map 和 forEach 方法都不会改变对象类型,它们只会遍历数组并执行回调函数。
2. 但是回调函数内部可能会改变对象的属性值,因为对象本身是引用类型,所以如果在回调函数内部改变了对象属性,那么原始对象也会被改变。
3. 如果要避免原始对象被改变,可以在回调函数内部创建一个新的对象来存储新的属性值,而不是直接修改原始对象的属性。
```js
Promise.myAll = function (promises) {
// arr判断
let arr = []
let count = 0
return new Promise((resolve, reject) => {
promises.forEach((item, i) => {
Promise.resolve(item)
.then(res => {
arr[i] = res
count++
if (count === promises.length) resolve(arr)
})
.catch(err => console.log(err))
})
})
}