2022年3月1号 面试题

689 阅读3分钟

1.请先自我介绍一下 

2.从你所做的项目中,说说你遇到的难点 

3.有没有封装过axios,怎么封装的 

4.什么是拦截器?拦截器的实现原理是什么

对请求的拦截,Axios的拦截器有两种,请求拦截器响应拦截器,只要为axios实例添加拦截器每个api请求都会执行拦截器。执行顺序: 请求拦截器 -> api请求 -> 响应拦截器。 使用方式如下

// 添加请求拦截器
axios.interceptors.request.use(function (config) {
    // 在发送请求之前做些什么
    return config;
  }, function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
  });

// 添加响应拦截器
axios.interceptors.response.use(function (response) {
    // 对响应数据做点什么
    return response;
  }, function (error) {
    // 对响应错误做点什么
    return Promise.reject(error);
});
// 取消拦截器
const interceptorId = axios.interceptors.request.use(function () {/*...*/}); 
axios.interceptors.request.eject(interceptorId);

拦截器的源码分析

// 请求拦截器和响应拦截器使用的都是 InterceptorManager构造函数
function Axios(instanceConfig) {
  this.defaults = instanceConfig;
  // 请求拦截器和响应拦截器使用的都是 InterceptorManager构造函数
  this.interceptors = {
    request: new InterceptorManager(),
    response: new InterceptorManager()
  };
}

InterceptorManager构造函数有handlers数组,保存所有的拦截函数, 并且在它的原型上添加三个方法, use用于添加拦截函数, 返回一个id, eject用于取消拦截器, forEach遍历所有拦截器。

// 拦截器构造函数
function InterceptorManager() {
  // 保存拦截器的数组,axios.interceptors.use的拦截函数会被push到handlers,可以添加多个拦截器
  this.handlers = [];
}
// 向拦截器原型上挂载 use方法, 向handler数组中push一个对象, 返回一个id
// 这样就可以通过eject(id) 取消拦截函数了。
InterceptorManager.prototype.use = function use(fulfilled, rejected)   
{      
 this.handlers.push({ fulfilled: fulfilled, rejected: rejected  });  
  return this.handlers.length - 1;  
};
// 移除拦截器 
InterceptorManager.prototype.eject = function eject(id) {     
   if (this.handlers[id]) {         
     this.handlers[id] = null;     
   } 
}; 
// 遍历执行所有拦截器 
InterceptorManager.prototype.forEach = function forEach(fn) {      
  utils.forEach(this.handlers, function forEachHandler(h) {          
     if (h !== null) {               
        fn(h);           
     }      
  }); 
};

理解拦截器思想

其实就是简单的数组,请求拦截器发生在api请求之前, 响应拦截器发生在api请求之后,仔细思考🤔一下,其实它们本质上只是一个执行顺序的关系。 这其实就是一个数组chain能实现的,把请求拦截器的函数推到数组前面, api请求放在中间, 响应拦截器放在数组后面,遍历执行数组chain就实现了拦截器的执行顺序关系,是不是很简单

达到实现,请求拦截器 -> api请求 -> 响应拦截器 执行顺序的目的。

// dispatchRequest是api请求
var chain = [dispatchRequest, undefined];

// 把请求拦截器数组this.interceptors.request 用unshift放在 chain 数组的前面
this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) { 
   chain.unshift(interceptor.fulfilled, interceptor.rejected);  
});

// 把响应拦截器this.interceptors.response 用push放在chain数组的后面
this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
  chain.push(interceptor.fulfilled, interceptor.rejected); 
});

5.map的原理是什么,平时怎么用

Array.prototype.map = function(fn) {
    let newArr = [];
    for (let i = 0; i < this.length; i++) {
        newArr.push(fn(this[i]))
    };
    return newArr;
}
// 用 map 映射,map()方法返回一个新数组

 案例:forEach用es5一步步拆解

var a = [1,2,3,4]
Array.prototype.forEach= function(fn) {   
    let newArr = []    
    console.log(fn, 'fn') // ƒ (item) {return item>2 } 'fn'    
    for(let i=0;i<this.length;i++) {
        console.log(this[i]) // 1,2,3,4        
        console.log(fn(this[i]), 'fn(this[i])') // false false true true        
        if(fn(this[i])){         
         newArr.push(this[i])        
        }     
    }      
    console.log(newArr, 'newArr') //  [3, 4] 'newArr'      
    return newArr 
}

实现
// 写一个fn方法 
var fn = function(item) {   
    return item>2 
} 
var result = a.forEach(fn)  
console.log(result, 'result')  // [3, 4] 'result'

6.reduce的原理是什么,它有几个值,举个平时的使用例子说说

reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值案例

var arr=[2,4,6,8];
let result=arr.reduce(function (val,item,index,origin) {
    return val+item
},0);
console.log(result) //20

实现原理
Array.prototype.reduce = function (reducer,initVal) {
    for(let i=0;i<this.length;i++){
        initVal =reducer(initVal,this[i],i,this);
    }
    return initVal
}

7.有没有做过下载,下载是怎么实现的,在下载过程中,下载中断了,进度条怎么继续 

8.http和axios的区别,从url输入访问请求,和发起请求有什么不同 

9.管理系统的菜单,路由是怎么实现的

 10.数组怎么去重,如果是json数组,怎么去重?空间复杂度为多少?

 11.做开发平台菜单的时候,你说用到算法,减轻了30%的服务器请求,你是怎么实现的? 

12.css3常用吗,一般用到哪些 

13堆和栈是什么 

14.MVVM是什么,具体描述一下 

简写Model-View-ViewModel
在前端页面中,把Model用纯JavaScript对象表示,View负责显示,两者做到了分离。
把Model和View关联起来的就是ViewModel。ViewModel负责把Model的数据同步到View显示出来,还负责把View的修改同步回Model。
MVVM 的核心是 ViewModel 层,它就像是一个中转站(value converter),负责转换 Model 中的数据对象,该层向上与视图层进行双向数据绑定,向下与 Model 层通过接口请求进行数据交互,起呈上启下作用。关注Model的变化,让MVVM框架去自动更新DOM的状态

15.watch和computed的实现原理是什么,区别是什么 

16.props为什么可以直接用this调用

(1) initState > initProps > defineReactive(props, key, value)

通过 this 直接访问到 props 里面的数据的原因是:props里的属性最终会存储到new Vue的实例(vm)上的 _props 对象中,访问 this.xxx,是访问Object.defineProperty代理后的 this._props.xxx

(2) initState > initMethods> bind(methods[key], vm)

通过this直接访问到methods里面的函数的原因是:因为methods里的方法通过 bind 指定了this为 new Vue的实例(vm)。

(3) initState > initData> observe(vm._data = {}, true)

通过 this 直接访问到 data 里面的数据的原因是:data里的属性最终会存储到new Vue的实例(vm)上的 _data对象中,访问 this.xxx,是访问Object.defineProperty代理后的 this._data.xxx

17.vue搭建项目的命令 

npm install -g vue-cli
vue init webpack my-project
cd my-project
npm install
npm run dev

18.发布订阅模式 

19.解释一下原型链