模拟面试(一)

1,277 阅读4分钟

前文

准备开一个模拟面试的专栏,就是找网上大厂的面经,然后自己解答作整理,这是第一篇,不过还没完成,现在发出来其实是想变相催更自己。

面试题目来源 反向人:腾讯QQ音乐前端面经(已offer)


还有很多没写完,拖了很久....


一面

react hooks 有哪些优缺点?

优点:

  1. 组件逻辑更容易复用
  2. 代码风格更友好,有利于拆分组件,提高组件复用率

缺点:

  1. 异步代码会导致数据不一致,数据是旧引用的bug
  2. useEffect的触发依靠响应式,逻辑的执行不够直观
  3. 成本高
  4. 复杂组件隐藏副作用难以排查

useLayoutEffect 和 useEffect 区别是什么?

  • useLayoutEffect是同步调用,发生在浏览器把内容渲染到界面前
  • useEffect是异步调用,发生在浏览器把内容渲染到界面后

说下JSBridge?

JSBridge构建了Native和js进行双向通信的通道,主要目的是给JavaScript提供调用Native的功能。

js调用Native的方式

  • Native侧通过JavaScriptInterface注入API供js侧调用, 注入的形式是key-value, key是对象名,value是Native的对象。然后js就可以通过key调用到Native对象
  • js侧通过iframe.src或者location.href的方式发送自定义的url scheme,Native通过拦截url scheme进行相应操作。

Native调用js的方式

  • 直接调用api执行js拼接的字符串

JSBridge通常需要实现两个函数,

  • postMessage js侧调用Native 需要对此次回调进行callBackId的关联,通常是消息队列的形式
  • receiveMessage Native侧调用js,携带callBackId和结果,这样js就可以在消息队列找到对应的回调进行执行

通过这种方式,就完成了一次js到native的调用。

说下react-native的原理,原生端和js端是怎么通信的?

react-native 运行了一个带js引擎的线程,线程负责js和原生的通信, 最后通过调用原生渲染从而实现跨平台的能力,react-native的产物是一个js文件,所以可通过更新js文件即可实现热更新。

原生端和js的通信分新旧架构两种方式:

  • 旧架构通过bridge,App启动时,原生侧会将原生模块(module)注册到js的映射表,原生模块包括方法(method),即一个module对应多个method的key-value形式。js在调用的时候,通过module + method的方式进行序列化发送到原生,原生解析module得到具体的原生对象,再通过method找到对应的方式,和JSBridge的本质一样,只是做了一层封装。并且,js端自行维护了消息队列实现调用回调。
  • 新架构采用JavaScript Interface (JSI),(等待更新.....)

反转单向链表怎么做,要几个指针,都有什么作用?

// https://leetcode-cn.com/problems/reverse-linked-list/
// 要两个指针 
// 一个指向前一个节点 一个指向后一个节点
// 遍历结束  pre指向了最后一个节点 返回即可
function reverseList(head: ListNode | null): ListNode | null {
    let pre = null
    while(head) {
        const next = head.next
        head.next = pre
        pre = head
        head = next
    }
    return pre
};

二面

react中state有层级很深,比如a.b.c.d,如果只更新c属性有哪些办法?immutable.js实现的原理是什么?

// 主要考的是react的immutable, react推崇浅比较
// 对于react的PureComponent或者React.memo而言
// 如果只是修改了b的title,b引用未修改,那么将无法触发组件render

// 深拷贝对象
const oldState = {
   a: {
      b: {title: "old"},
   }
}
const newState = deepClone(oldState)
newState.a.b.title = "new"


// 生成新的b引用
const oldState = {
   a: {
      b: {title: "old"},
   }
}
const newState = {
     a:{
       b:{
         ...oldState.a.b,
         title: "new"
       }
     }
}

// 通过immutablejs 
// oldState是不可修改的,所以每次修改值之后
// 都会产生新的对象引用
import { Map } from 'immutable';

const oldState = Immutable.Map({
    a: {
      b: {title:"old"}
    }
  })
const newState = oldState.set('a', {b:{title:"new"}})
// true
oldState !== newState

对比三个方案

  • 深拷贝性能消耗大,而且会导致其他引用失效
  • 通过... 生成新的引用,比较啰嗦可读性差
  • immutablejs 直观

immutablejs创建的对象无法更改,通过结构共享,每次修改只会影响当前节点其他节点不会改变,通过这种方式产生新的immutablejs对象,避免性能损耗。

参考