2023-5-15 小红书一面准备

2023-5-15 小红书一面准备

岗位:安全前端

地点:上海

15 点给我打电话吧,就说 17 点面试,感觉确实很着急

大小周

23 春招 4.23 安全前端一面二面 过

4.23 一面

面试时间 1h,面试官很温和,比隔壁美团好太多了。至少先介绍了部门的情况,面试会给反馈,遇到不太会的会引导

  1. 介绍了 xhs 的部门,然后让我自我介绍
  2. 说说实习遇到最有挑战性的项目吧,是怎么解决的
  3. 在滴滴的实习主要干什么,产出多少
  4. vue3 对比 vue2 区别
  5. vue-router 两种模式和实现原理
  6. keep-alive 使用场景,你觉得和 vue-router 结合能怎么用
  7. watch 和 watchEffect 区别
  8. vue 的 diff 算法
  9. 怎么前端性能优化
  10. webpack 的 plugin 和 loader 区别和原理,有自己写过吗
  11. 手写题:实现个防抖函数
  12. 手写 promise,要求实现 resolve、reject、then 方法

反问:

  1. 项目组技术栈(vue 居多)
  2. 工作强度(大小周,说到这面试官都笑了 hhh)
  3. 面试官反问:有其他在流程的企业吗,我说有,然后说那尽快帮我推进一下进度

先不说 xhs 招不招人吧,面试体验确实比较好,许愿个二面吧

4.25 二面

4.27 更新 约了当天下午 hr 面

过程有点逆天,今天上午上着课呢,hr 突然打电话说最近这周都在忙绩效,原定今天面试拖到五一后,过十分钟又打电话说一面面评很好,想要优先推进,还是今天下午面试

总时长大概 50 分钟,没有手撕,只有项目实习拷打,八股随便问了两个很简单的

  1. 相互介绍
  2. 对我的弹幕播放器项目很感兴趣,想要深入了解,问我 GitHub 地址,我说没上传,问我能不能现场演示,我说目前电脑没这份代码。。。。。估计把他也整不会了,他说那就直接开始问项目吧,问弹幕实现原理,hls 切片原理,怎么做前端优化,同时实现很多弹幕不会很卡吗等等等,挖得特别深。
  3. 实习经历拷打,问我滴滴干了啥,页面上线了吗,让我展示一下页面,问了很多具体的工作细节,包括产出的模态框组件怎么设计的,相较原来的有什么优点,有遇到什么难题怎么解决的等等等,反正问了非常久,挖得很深
  4. 开始闲聊。问滴滴氛围,发布流程,排期时间,一个前端对几个后端等
  5. 非常随意的八股。vue 的 diff 算法,生命周期函数。只问了这两个
  6. 反问:和上一个面试官做相同的东西吗?(我是全栈,上一个面试官是后端,但是都有接触过前端)(哈哈哈)
  7. 反问:可以选 base 吗(可以)
  8. 可以转正吗(这个暑期岗位开放就是可以转正的,但是也要看你的表现)
  9. 流程什么时候可以推进呢(直接说你二面过了等 hr 罢)
  10. 面试官反问:收到几个 offer?

感觉这个部门确实比较缺人的吧,问题都很开放,也没手撕,感觉就是八股完全没用,亏我还准备了那么久

结果是好的,等着 hr 面吧,顺便许愿个 offer

日常实习 数据部门 一二面 过

4.4 上海小红书一面(65min)

  1. 实习项目的难点说一下

  2. 平时是怎么学习前端的?

  3. 讲一下 vue-router ✅

    1. 用于构建单页面应用 SPA 讲一下 vue-router
    2. 支持路由配置,嵌套路由,路由导航守卫,懒加载
  4. history 模式怎么解决刷新 404 的问题?❌

    1. 因为刷新页面会重新向服务器发送请求,但是服务器没有相应的路由配置,所以 404 了
    2. 解决方法是进行服务器配置
    3. 后端服务器配置:在后端服务器上配置一个 fallback 页面,即无论用户请求的是哪个 URL,都返回同一个 HTML 页面。这样,当用户刷新页面时,服务器会返回这个页面,然后 Vue Router 会根据当前 URL 解析路由并正确加载相应的组件。
    4. 使用服务器重定向:在服务器配置中,将所有非静态资源的请求重定向到 index.html 页面。这样,无论用户请求的是哪个 URL,都会被重定向到 index.html,然后 Vue Router 会根据当前 URL 进行路由匹配和加载。
    5. 使用后端路由配置:在后端服务器上配置与前端路由相匹配的路由规则,并将这些路由指向同一个 HTML 页面。这样,当用户请求这些路由时,服务器会返回同一个 HTML 页面,并由前端的 Vue Router 处理路由逻辑。
  5. react 都知道哪些 hook? ✅

    1. useState:用于在函数组件中添加状态管理,可以通过它定义和更新状态变量。
    2. useEffect:用于在组件渲染完成后执行副作用操作,比如订阅事件、发送网络请求等。
    3. useContext:用于在函数组件中访问 React 的上下文(Context),可以获取和更新上下文中的值。
    4. useReducer:用于在函数组件中实现复杂的状态管理,类似于 Redux 中的 reducer,通过定义不同的 action 类型来更新状态。
    5. useCallback:用于缓存函数,避免函数在每次渲染时都重新创建,提高性能。
    6. useMemo:用于缓存计算结果,只有在依赖项发生变化时才重新计算。
    7. useRef:用于在函数组件中创建可变的引用,可以用来存储任意可变值,类似于类组件中的实例属性。
  6. useState 的原理有了解过吗? ⭕️

  7. useEffect 说一下,接收几个参数? ✅

  8. 第一个参数前面能加 async 吗? ⭕️

  9. useRef 的用法 ✅

  10. useCallback 是干嘛的? ✅

  11. 如果给子组件传函数,怎么性能优化? ✅

  12. setState 是同步还是异步 ✅

  13. 什么情况下 setState 是同步 ⭕️

  14. var let const 的区别 ✅

  15. 看代码说输出(构造函数原型链相关) ✅

  16. 做题:对象拍平 ⭕️

    1. 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
      }
  17. 反问:技术栈?答 vue3+ts

4.4 上海小红书二面(70min)

问了很多框架的原理性东西,记不全了……

  1. vue 和 react 有什么区别? ✅
  2. 什么是受控组件? ✅
  3. 你实现过受控组件吗? ✅
  4. 完全受控组件和非完全受控组件 ❌
  5. 讲一下 diff 算法 ✅
  6. redux 是怎么用的? ✅
  7. react 中异步请求你都在哪里发送? ✅
  8. ES6 有哪些新特性? ✅
  9. 说一说 class ✅
  10. class 中静态属性怎么定义 ✅
  11. 常见的数据类型有哪些? ✅
  12. 检测数据类型的方法?区别?✅
  13. react 是怎么监视数据改变的? ⭕️
  14. 有没有用过 suspense(没听说过) ❌
  15. vue 中组件通信的方式 ✅
  16. 那 react 中是怎么通信的? ⭕️
  17. 详细讲讲事件总线的用法 ⭕️
  18. 别说了,你给我实现一下吧 ⭕️
  19. 你有什么要问的吗?(没有)

二面完一小时 oc,晚上 offer()

日常实习 研发效能 一二面 过

2.22 一面

代码题:

1、手写 flat

let arr = [1233, [434, [344, [3434]]]]

function myFlat(arr) {
return arr.reduce((res, cur) => {
if (Array.isArray(cur)) return res.concat(myFlat(cur))
else return res.concat(cur)
}, [])
}

console.log(myFlat(arr))

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

牛客上面的面试题

小红书 2022 前端开发面试题 (nowcoder.com)

小红书 2022 前端开发面试题 2 (nowcoder.com)

  1. 断点续传怎么做的

    1. 客户端发起文件上传请求时,向服务端发送一个 HTTP 请求,包含文件名、文件大小等信息。
    2. 服务端接收到请求后,根据文件名和文件大小创建一个空文件,并返回已经上传的文件大小,如果之前有部分数据已经上传过,那么服务端可以根据已上传的大小确定从哪个位置开始上传。
    3. 客户端接收到服务端返回的已经上传的文件大小后,可以根据已上传的大小计算出下一次应该从哪个位置开始上传。 客户端将文件切分为多个小块,每个小块的大小一般为几十 KB 或几百 KB,将每个小块的数据上传到服务端,并在每个小块的末尾添加一个分界符,标记上传结束。
    4. 服务端接收到每个小块后,将其存储到文件对应的位置,等待下一次上传。 如果上传过程中出现中断,客户端可以记录已经上传的文件大小和已经上传的小块数,下次上传时从上一次中断的位置开始继续上传。
    5. 总的来说,断点续传的实现需要客户端和服务端的相互配合,并需要考虑文件上传的稳定性和可靠性。
  2. 秒传怎么实现

    1. 一种常见的实现方式是在上传文件时,先计算文件的哈希值或者 MD5 值,并将其作为文件的唯一标识。然后将该标识与后台数据库中已经存在的文件的标识进行比对。如果存在相同的标识,说明该文件已经上传过,那么就直接使用已经存在的文件;如果不存在相同的标识,说明该文件是一个新文件,那么就进行正常的上传操作。
    2. 在实现过程中,可以通过在前端计算文件哈希值或 MD5 值的方式,避免上传重复的文件,减少服务器的压力,提高文件上传的效率。同时,后台服务器也需要维护一个文件信息的数据库,用于存储已经上传过的文件的唯一标识和存储路径等信息,以便实现秒传功能。
  3. var let const 块级作用域

    1. var 声明变量的作用域是函数级别的,不受块级作用域的限制。在全局作用域中声明的变量会成为全局对象的属性。
    2. let 声明的变量是块级作用域的,只在声明的块内有效。在 for 循环中,每次迭代都会创建一个新的变量。
    3. const 声明的变量也是块级作用域的,和 let 类似,但是其值不能被重新赋值,只能被赋值一次。
  4. ts Partial Omit 怎么实现的

  5. 箭头函数有哪些限制

    1. 无法使用 arguments 对象,需通过剩余参数(rest parameters)或默认参数来获取传递给函数的参数列表;
    2. 无法使用 new 关键字调用函数,箭头函数没有自己的 this 值,它会捕获其所在的上下文中的 this 值;
    3. 无法使用 yield 关键字,因此箭头函数不能用作 generator 函数;
    4. 箭头函数没有自己的作用域,它会捕获所在上下文的作用域链(scope chain),因此对于闭包的使用需要特别注意。
  6. 箭头函数为什么不能作为构造函数

    1. 没有自己的 this 值,箭头函数中的 this 值是在定义时确定的,不能通过 new 关键字来改变 this 的指向。
  7. 而构造函数中,this 的指向是随着实例化对象而改变的,因此箭头函数不具备构造函数的特性,不能用来创建对象。

  8. Promise 用过哪些 api

    1. then():指定 Promise 成功(resolve)和失败(reject)时的回调函数,接受两个参数,一个是成功时的回调函数,一个是失败时的回调函数。
    2. catch():指定 Promise 拒绝(reject)时的回调函数,接收一个参数,即拒绝时的回调函数。
    3. finally():无论 Promise 成功还是失败,都会执行的回调函数,没有参数。
    4. all():接收一个 Promise 数组,返回一个新的 Promise,当所有 Promise 都成功(resolve)时,返回值为数组,包含所有 Promise 成功时的结果;当其中任何一个 Promise 失败(reject)时,返回值为失败的 Promise 的值。
    5. race():接收一个 Promise 数组,返回一个新的 Promise,当其中任何一个 Promise 成功(resolve)或失败(reject)时,返回值为该 Promise 的值。
    6. Promise.resolve():返回一个解决(resolve)的 Promise 对象,可以指定解决的值。
    7. Promise.reject():返回一个拒绝(reject)的 Promise 对象,可以指定拒绝的原因。
    8. Promise.allSettled():接收一个 Promise 数组,返回一个新的 Promise,当所有 Promise 都结束时,返回值为数组,包含所有 Promise 的状态和值。
    9. Promise.any():接收一个 Promise 数组,返回一个新的 Promise,当其中任何一个 Promise 成功(resolve)时,返回值为成功的 Promise 的值;当所有 Promise 失败(reject)时,返回值为失败的 Promise 的值。 以上是 Promise 常见的方法和静态方法,可以用来管理异步操作的状态和结果。
  9. Map 和 Object 的区别

    1. 都可以用来存储键值对
    2. 键的类型:Map 中的键可以是任意类型,包括原始类型和对象引用,而 Object 的键必须是字符串或者 Symbol 类型。
    3. 插入顺序:Map 保留了插入顺序,因此在迭代 Map 时会按照插入顺序返回元素,而 Object 不保留插入顺序。
    4. 大小:Map 可以使用 size 属性来获取元素的数量,而 Object 只能手动计算属性的数量。
    5. 迭代:Map 支持 for-of 循环和 forEach 方法来迭代元素,而 Object 只能使用 for-in 循环来迭代属性,但是需要手动判断是否是自身属性而不是继承属性。
    6. 性能:在非常大的数据集合下,Map 的查找和插入操作通常比 Object 更快。
  10. vue 怎么实现双向绑定

    1. 创建一个Observer对象,对模型数据进行监听,当数据发生变化时,通知订阅者。 创建一个Directive对象,它是指令,负责将模型数据渲染到页面中。

    2. 创建一个Watcher对象,它是订阅者,当 Observer 对象监听到数据发生变化时,通知 Watcher 对象,Watcher 对象再调用 Directive 对象,更新页面中的数据。

    3. 将 Watcher 对象添加到订阅者列表中,当模型数据发生变化时,Observer 对象会遍历订阅者列表,通知所有的 Watcher 对象。

    4. 将 Directive 对象添加到节点上,当数据发生变化时,Directive 对象会调用 Watcher 对象,更新页面中的数据。

    5. Vue 使用了 ES5 中的 Object.defineProperty()方法来实现数据的劫持,通过 get 和 set 方法来实现对数据的监听,从而实现了双向绑定。

    6. 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)
  11. 实现栈,有入栈出栈的方法,以及 length 属性

    1. 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))
      })
      })
      }