记一些面试问过的题和搜到的答案

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了记一些面试问过的题和搜到的答案相关的知识,希望对你有一定的参考价值。

参考技术A 1. diff算法

传统 diff 算法其时间复杂度最优解是 O(n^3),那么如果有 1000 个节点,则一次 diff 就将进行 10 亿次比较,这显然无法达到高性能的要求。而 React 通过大胆的假设,并基于假设提出相关策略,成功的将 O(n^3) 复杂度的问题转化为 O(n) 复杂度的问题。

tree diff

React 只对虚拟 DOM 树进行分层比较,不考虑节点的跨层级比较

React 通过 updateDepth 对虚拟 Dom 树进行层级控制,只会对相同颜色框内的节点进行比较,根据对比结果,进行节点的新增和删除。如此只需要遍历一次虚拟 Dom 树,就可以完成整个的对比

component diff

React 是基于组件构建的,对于组件间的比较所采用的策略如下:

如果是同类型组件,首先使用 shouldComponentUpdate()方法判断是否需要进行比较,如果返回true,继续按照 React diff 策略比较组件的虚拟 DOM 树,否则不需要比较

如果是不同类型的组件,则将该组件判断为 dirty component,从而替换整个组件下的所有子节点

对于不同类型的组件,默认不需要进行比较操作,直接重新创建。

对于同类型组件, 通过让开发人员自定义shouldComponentUpdate()方法来进行比较优化,减少组件不必要的比较。如果没有自定义,shouldComponentUpdate()方法默认返回true,默认每次组件发生数据(state & props)变化时,都会进行比较。

element diff

element diff 涉及三种操作:移动、创建、删除。element diff 就是通过唯一 key 来进行 diff 优化,通过复用已有的节点,减少节点的删除和创建操作。

不使用key:React 对新老同一层级的子节点对比,发现新集合中的 B 不等于老集合中的 A,于是删除 A,创建 B,依此类推,直到删除 D,创建 C。这会使得相同的节点不能复用,出现频繁的删除和创建操作,从而影响性能。

使用key:React 首先会对新集合进行遍历,通过唯一 key 来判断老集合中是否存在相同的节点,如果没有则创建,如果有的,则判断是否需要进行移动操作。并且 React 对于移动操作也采用了比较高效的算法,使用了一种顺序优化手段,这里不做详细讨论。

如何进行 diff

React 是基于组件构建的,首先可以将整个虚拟 DOM 树,抽象为 React 组件树(每一个组件又是由一颗更小的组件树构成,依次类推),将 React diff 策略应用比较这颗组件树,若其中某个组件需要进行比较,将这个组件看成一颗较小的组件树,继续用 React diff 策略比较这颗较小的组件树,依次类推,直到层次遍历完所有的需要比较的组件

React 通过大胆的假设,制定对应的 diff 策略,将 O(n3) 复杂度的问题转换成 O(n) 复杂度的问题

通过分层对比策略,对 tree diff 进行算法优化

通过相同类生成相似树形结构,不同类生成不同树形结构以及shouldComponentUpdate策略,对 component diff 进行算法优化

通过设置唯一 key 策略,对 element diff 进行算法优化

2. 虚拟dom

虚拟 DOM (Virtual DOM )实际上它只是一层对真实DOM的抽象,以javascript 对象 (VNode 节点) 作为基础的树,用对象的属性来描述节点,最终可以通过一系列操作使这棵树映射到真实环境上

在Javascript对象中,虚拟DOM 表现为一个 Object对象。并且最少包含标签名 (tag)、属性 (attrs) 和子元素对象 (children) 三个属性,不同框架对这三个属性的名命可能会有差别

创建虚拟DOM就是为了更好将虚拟的节点渲染到页面视图中,所以虚拟DOM对象的节点与真实DOM的属性一一照应

你用传统的原生api或jQuery去操作DOM时,浏览器会从构建DOM树开始从头到尾执行一遍流程

当你在一次操作时,需要更新10个DOM节点,浏览器没这么智能,收到第一个更新DOM请求后,并不知道后续还有9次更新操作,因此会马上执行流程,最终执行10次流程

而通过VNode,同样更新10个DOM节点,虚拟DOM不会立即操作DOM,而是将这10次更新的diff内容保存到本地的一个js对象中,最终将这个js对象一次性attach到DOM树上,避免大量的无谓计算

3. hooks如何处理页面渲染

4. setState同步异步

由React控制的事件处理程序,以及生命周期函数调用setState不会同步更新state 。

在React中,如果是由React引发的事件处理(比如通过onClick引发的事件处理),调用setState不会同步更新this.state,除此之外的setState调用会同步执行this.state。所谓“除此之外”,指的是绕过React通过addEventListener直接添加的事件处理函数,还有通过setTimeout/setInterval产生的异步调用

异步下多个setState调用会合并处理

第一个函数调用更新state,第二个函数是更新完之后的回调

/* 函数式用法*/

this.setState((state, props) =>

  return

      count: state.count + 1

 

)

5. ts的初次使用

第一步:全局安装ts npm i -g typescript

第二步:创建tsconfig.json tsc --init

第三步: 安装开发环境依赖 npm install --save-dev typescript @types/react @types/react-dom ts-loader

第四步:修改webpack.config.js

module.exports =

    context: ...,

    entry: ...,

    output: ...,

    // 添加resolve

    resolve:

        extensions: ['.ts', '.tsx', '.js']

    ,

    module:

        loaders: [

            // 增加新的loader

           

                test: /\.tsx?$/,

                loaders: ['babel-loader', 'ts-loader']

            ,

            ...

        ]

    ,

    plugins: ...

;

关于.tsx文件

如果.ts文件中包含jsx,则需要将.ts文件改成.tsx文件

需要引用.tsx文件时,不用加扩展名,import xxx from ‘./xxx’;,而引用.jsx文件,是需要加扩展名的

.tsx文件中,React和ReactDOM不支持default import,需要进行修改

6. useRef的用法

// useRef,返回一个可变的ref对象

const refContainer = useRef(initialValue);

5.26 涂鸦智能

1. useMemo 和useCallback

// useMemo,返回一个memoized值,只有第二个参数发生变化时才会重新计算。类似 useCallback。

function App ()

  const [ count, setCount ] = useState(0)

  const add = useMemo(() =>

    return count + 1

  , [count])

  return (

    <div>

      点击次数: count

      <br/>

      次数加一: add

      <button onClick=() => setCount(count + 1)>点我</button>

    </div>

    )



上面的例子中,useMemo 也支持传入第二个参数,用法和 useEffect 类似

不传数组,每次更新都会重新计算

空数组,只会计算一次

依赖对应的值,当对应的值发生变化时,才会重新计算(可以依赖另外一个 useMemo 返回的值)

需要注意的是,useMemo 会在渲染的时候执行,而不是渲染之后执行,这一点和 useEffect 有区别,所以 useMemo 不建议有 副作用相关的逻辑

useCallback是什么呢,可以说是useMemo的语法糖,能用useCallback实现的,都可以使用useMemo,useCallback 的第二个参数和useMemo一样,没有区别. 在 react 中我们经常面临一个子组件渲染优化的问题, 尤其是在向子组件传递函数props时,每次render都会创建新函数,导致子组件不必要的渲染,浪费性能,这个时候,就是useCallback的用武之地了,useCallback可以保证,无论render多少次,我们的函数都是同一个函数,减小不断创建的开销,具体如何使用看下面例子

const onClick = useMemo(() =>

  return () =>

    console.log('button click')

 

, [])

const onClick = useCallback(() =>

console.log('button click')

, [])

// useCallback,返回一个memoized函数,第二个参数类似useEffect,只有参数变化时才会更改。

const memoizedCallback = useCallback(

  () =>

    doSomething(a, b);

  ,

  [a, b],

);

2. ts的interface和type的区别

首先,interface只能表示function,object和class类型,type除了这些类型还可以表示其他类型

interface Aname:string;

            add:()=>void;



interface B():void

type C=()=>number;

type D=string;

type E=name:string,age:number

interface可以合并同名接口,type不可以

interface Aname:string

interface Aage:number

var x:A=name:'xx',age:20

interface可以继承interface,继承type,使用extends关键字,type也可继承type,也可继承interface,使用&

interface Aname:string

interface B extends Aage:number

type C=sex:string

interface D extends Cname:string

type E=name:string&C

type F =age:number&A

还有类可以实现接口,也可以实现type

interface Aname:string;add:()=>void

type B=age:number,add:()=>void

class C implements A

    name:'xx'

    add()console.log('类实现接口')



class D implements B

    age:20

    add()console.log('类实现type')

   

计算属性:type 支持, interface 不支持。

总的来说,公共的用 interface 实现,不能用 interface 实现的再用 type 实现。主要是一个项目最好保持一致。

以上是关于记一些面试问过的题和搜到的答案的主要内容,如果未能解决你的问题,请参考以下文章

(转)40个Java集合面试问题和答案

40个Java集合面试问题和答案

40个Java集合面试问题和答案

40个Java集合面试问题和答案

40个Java集合面试问题和答案

40个Java集合面试问题和答案