你必须知道的常见面试题 多问为什么

你必须知道的面试题

  • 你知道什么是原型吗 我们为什么要用原型呢 或者说原型为我们提供了什么呢

    在es6出现之前并没有引入类的概念 所以之前创建实例的方法是通过构造函数
    构造函数通过new来调用 通过new会创建一个实例对象
    也就是说new一个构造函数会得到一个实例对象 并且每一次都会将相同的属性挂载到返回的实例上
    既然这样为什么不将相同的属性放到同一个地方 让所有的实例都可以访问到呢 这就需要原型
    原型为同一个构造函数new出来的实例对象提供了一个公共的区域来存放共同的属性和方法
    这样就可以节省一定的内存 特别是需要构造多个实例的时候
    举一个简单的例子 实例上有一个name属性和一个sayName方法 sayName的打印结果是根据name打印的 name是传入的参数
    如果不使用原型的话就会在每一个构建的实例对象上都挂载这个sayName方法
    如果将这个sayName方法挂载到原型上的话 那么每一个实例对象只需要挂载一个name属性即可
    当调用sayName方法时会自动到原型上查找这个方法 实现的结果是一样的
    
  • 那你能说一下构造函数在new的过程中都做了什么吗

    首先会创建一个空对象 并将这个对象赋值给this 也就是创建一个this空对象
    然后我们就可以通过this.的方式在this这个对象上挂载一些属性和方法
    最后将这个this对象返回
    注意 这个return是默认返回的 如果人为的return一个什么东西 那么将得不到想要的结果
    
  • 你了解原型链吗 你能说说prototype与proto的区别吗

    prototype是每个函数独有的属性 proto是每个对象都存在的属性
    prototype主要是为每个实例对象提供一个公共的存储属性和方法的区域 实例对象的proto指向的是构造函数的prototype属性
    proto的话和prototype的作用是一样的 我们平时定义的数组、对象、函数这些引用类型都有proto属性 proto指向的是父级对象上的一些属性
    当访问一个对象上没有的属性时 就会顺着proto这个属性向父级对象上查找 如果没有就会报错
    比如我们平时定义的数组 本身是没有froEach、push、reduce这些方法的 都是从Array身上拿到的
    还有自定义函数的call、apply、bind方法也是从Function这个对象上拿到的
    向这样像链式结构层层向上查找的结构就是原型链
    
  • 我们为什么要使用vuex redux与vuex有什么区别

    我们使用vue的组件在父子传参时一般是通过props和$emit的方式 当组件的层级很多时 这样的传参方式就显得很吃力
    会给我们带来非常多的麻烦 比如多个组件依赖同一状态、不同组件的行为要改变同一状态、由于vue是响应式 依赖同一状态的组件会一起更新 vuex可以很好的解决这些问题
    首先vuex是直接可以和vue搭配使用的 redux则需要react-redux这个插件将react和redux融合起来
    其次他们的store的分支管理不一样 vuex通过new一个Vuex.Store方法将不同的分支合并到modules上 组件引入store通过store.的方式访问不同的分支数据 redux则是通过combindReducer方法合并分支
    另外vue获取vuex里的数据和方法是通过从vuex里解构出mapState和mapAction 然后再用...扩展运算符扩展出来
    或者是直接使用this.$store的方式 redux则是通过从react-redux中解构出connect connect是一个高阶函数 通过传入mapState和mapDispatch两个函数将store里面的数据和方法映射到被修饰组件的props中
    他们最大的不同就是派发action的方式 vuex是把副作用操作放到了action中 等action的一系列操作结束以后再去提交mutation 这是vuex人性化的地方 而redux则不同 由于redux派发一个action必须返回一个扁平的对象 所以在redux中做一些副作用操作就得使用中间件
    
  • 你平时用的redux异步处理中间件是什么 怎么运行的

    我用过的中间件有thunk和saga 平时用的最多的是saga saga可以更好的统一管理action
    saga使用的是es6的generator函数
    首先要先引入redux-saga 然后创建一个sagaMiddleWare 在创建store之后调用run方法来监听全局的中间件
    当组件派发一个action后 saga通过takeEvery进行拦截 根据action.type来判断执行哪一个副作用函数
    可以从radux-saga/effects中解构出call和put方法 call方法可以进行异步请求数据 put方法则派发一个带有type属性的扁平对象给reducer处理 reducer再通过type属性来进行数据修改
    
  • 你平时使用hook吗 什么情况下会使用hook 或者说我们为什么要使用hook

    我们在写项目的时候喜欢将一个组件分成容器组件和展示组件
    容器组件主要是获取store里面的数据和方法
    展示组件是一个无状态的函数 通过props来接受容器组件传递的参数数据
    这样可以更好的理解和维护代码 组件的可复用性也更高
    但由于是一个函数组件 所以无法使用this的setState方法来修改自己独有的数据
    hooks主要是解决这个问题
    react提供了useState、useEffect两个hook函数来创建stack hook和effect hook
    useState的参数为初始状态 可以用数组解构出一个状态以及对应的改变状态的方法
    useEffect允许我们添加一些副作用逻辑 它有两个参数 第一个参数是异步函数 第二个参数是一个数组
    通过数组里面值的变化来决定是否执行异步函数
    如果不传第二个参数且没有修改状态 或者是第二个参数是一个空数组的话 那么函数只执行一次
    如果不传第二个参数又修改了状态 则会死循环
    可以用来模拟生命周期函数
    

原型与原型链

构造函数

在es6出现之前并没有引入类的概念 所以之前创建实例的方法是通过构造函数

写法

构造函数与普通函数并没有什么区别 所以我们会用大驼峰的方法创建构造函数 用来区分

new

构造函数通过new来调用 通过new会创建一个实例对象

this

通过new一个构造函数 在内部发生了以下过程

  • 创建一个this空对象

  • 人为的(this.name = ‘zhangsan’)将构造函数里的变量和方法放到this里

  • 最后返回this

也就是说new一个构造函数会得到一个实例对象 并且每一次都会将相同的属性挂载到返回的实例上

既然这样为什么不将相同的属性放到同一个地方 让所有的实例都可以访问到呢 这就需要原型

原型为同一个构造函数new出来的实例对象提供了一个公共的区域来存放共同的属性和方法

原型

prototype
  • 函数独有 每一个函数都有一个prototype属性
  • 作用是让该函数构造的实例对象可以找到公共的属性和方法
proto
  • proto 每一个对象都有proto属性

  • 当访问一个对象的属性时 如果这个对象没有这个属性就会到proto这个属性上找

  • proto属性指向的是父对象的prototype

    • 例如function的父对象是object object的父对象是null
    • null是原型链的顶端 null不存在proto属性
  • 像这样层层向上访问原型形成链式结构的就是原型链

redux vuex

当不同的组件需要共享数据的时候 我们就需要使用状态管理

vuex

  • 当不同组件共享状态时 单向数据流就会显得很吃力 多层嵌套的组件之间传参会非常繁琐

    • 多个组件依赖同一状态
    • 不同组件的行为要改变同一状态
    • 响应式 一个组件改变状态 其他组件也会跟着改变
  • 组件派发一个action

  • action提交一个mutation

    • action可以进行异步操作
  • mutation改变一个状态

    • mutation只能是同步操作
  • 状态改变更新渲染组件

    • state拥有module属性 可以创建子分支
  • 子组件通过从vuex中结构出mapState等映射到自己的方法中

redux

  • 组件派发一个action
  • action传到reducer
    • action必须是一个扁平的对象
    • 可以添加中间件来进行异步操作
      • redux-thunk
      • redux-saga
  • reducer更改状态
  • 状态改变更新渲染组件
  • redux并不能直接响应react 需要通过react-redux插件建立连接
    • 通过从react-redux中结构出connect将react与redux连接起来
    • connect是一个高阶函数 用于增强组件的功能

saga thunk

redux中的action仅支持原始对象(plain object)处理有副作用的action 需要使用中间件

中间件可以在发出action 到reducer函数接受action之间 执行具有副作用的操作

thunk

  • 其实thunk是一个三层函数 在thunk的第三层函数中判断action是一个函数还是一个扁平的对象

  • 如果是函数 返回这个函数(可以是执行异步操作)执行的结果 这个结果应该是一个扁平的对象

  • 如果是一个对象 直接next放走 交给reducer

function createThunkMiddleware(extraArgument) {
  return ({ dispatch, getState }) => next => action => {
    if (typeof action === 'function') {
      return action(dispatch, getState, extraArgument);
    }
    return next(action);
  };
}
缺点
  • action形式不统一
  • 异步操作分散在了各个action中

saga

  • saga使用的是es6的generator
import { call , put , takeEvery } from 'redux-saga/effects'
// generator函数
function * getProduct () {
  // 通过call方法执行异步操作
  const data = yield call()
  // 通过put方法提交给reducer一个扁平的action
  yield put({
    type: 'GET_PRODUCT',
    data
  })
}
function * sagas () {
	  // 拦截redux派发的action 
    yield takeEvery('SAGA_GET_PRODUCT' , getProduct)
}

export default sagas
  • 将所有的异步函数集中在了一起
  • action就是一个扁平的对象 带有type属性的对象
  • 每一个分支都可以有自己的saga 然后合并到主干的saga

hook

  • 为了结构清晰 react可以分为容器组件和函数组件(展示组件)

    • 通过分离可以更好的理解和维护代码
    • 可复用性更好 同一个函数组件可用于不同的数据源来展示不同的页面
  • 容器组件负责请求数据和数据更新 函数组件负责渲染数据 容器组件通过自定义属性的方式将数据传到函数组件中的props里

  • 容器组件通过react-redux的connect装饰器连接store 然后在componentDidMount这个生命周期里dispatch派发方法来更新store里的状态

  • 有时候函数组件里可能需要使用自己的一些状态 但由于函数组件没有this 无法使用setState这个方法 达不到响应的效果 所以我们就需要使用hooks

    • react提供了useState、useEffect两个hook函数来创建stack hook和effect hook

      • useState的参数为初始状态 可以用数组结构出一个状态以及改变状态的方法

        function Example() {
          const [count, setCount] = useState(0);
        
          return (
            <div>
              <p>You clicked {count} times</p>
        			// setCount里面可以直接写值 或者表达式 或者写一个函数
              <button onClick={() => setCount(count + 1)}>
                Click me
              </button>
            </div>
          );
        }
        

        由于react的状态管理机制是根据旧的状态改变新的状态 不会改变旧的状态

        所以 在使用set方法时最好是写一个回调函数 这样比较符合react的机制

      • useEffect可以给组件添加一些副作用逻辑

        • 接受两个参数 第一个参数是副作用函数 第二个是判断是否执行副作用函数的数组
          • 如果不传第二个参数
            • 并且不做修改状态的事 那么函数只会执行一次
            • 如果修改了状态 那么函数会无限循环
          • 如果有第二个参数
            • 如果参数是一个空数组 那么函数只会执行一次
            • 如果数组有值 那么函数会根据数组里面值的变化来判断是否执行函数
      • 同时我们也可以自定义一个hook函数

        • 自定义hook要用小驼峰命名 并且必须使用use开头

        • 其实自定义hook函数的内部是在使用了hook的基础上多增加了一些方法 最后将状态和方法返回

          • 这里的方法要用一个对象包裹

          • 最终的返回结果是一个数组 第一个参数是状态 第二个参数是一个对象 对象里面是改变状态的方法

            import { useState } from 'react'
            
            function useCount(param) {
              let [count, setCount] = useState(param)
            
              // 可以写一个参数
              function add(num) {
                // 或者不写参数 直接写死
                // setCount(count => count + 1)
                setCount(count => count + num)
              }
            
              function minus(num) {
                setCount(count => count - num)
              }
              
              return [count, { add, minus }]
            }
            
            export default useCount
            
            let [count, setCount] = useCount(1)
              let handleAddClick = useCallback(() => {
                setCount.add(1)
              }, [])
            
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值