[关闭]
@panhonhang 2020-02-17T16:18:56.000000Z 字数 7013 阅读 389

面试问题记录

面试


数组:博文

promise用法:

为什么用promise:解决回调地狱,改成链式调用。
错误处理:接口未报错,请求成功。用状态码判断。

如何promise的then方法:1、每一次成功之后再返回一个promise。(1、Promises/A+标准:原Promise对象的状态将跟新对象保持一致。)

async await实现原理:async 函数的实现原理,就是将 Generator 函数和自动执行器,包装在一个函数里。

实现一个new操作:

 function objectFactory() {
        var obj = new Object(),
        //取出第一个参数,就是我们要传入的构造函数
        Constructor = [...arguments].shift();
        obj.__proto__ = Constructor.prototype;

        Constructor.apply(obj, arguments);
        return obj;
    }

实现数组扁平化:

//方法一
var arr = [1, [2, [3, 4]]];

function flatten(arr) {
    var result = [];
    for (var i = 0, len = arr.length; i < len; i++) {
        if (Array.isArray(arr[i])) {
            result = result.concat(flatten(arr[i]));
        }
        else {
            result.push(arr[i])
        }
    }
    return result;
}


console.log(flatten(arr))

//方法二
var arr = [1, [2, [3, 4]]];

function flatten(arr) {
    return arr.toString().split(',').map(function(item){
        return +item
    })
}

console.log(flatten(arr))

// 方法三
var arr = [1, [2, [3, 4]]];

function flatten(arr) {

    while (arr.some(item => Array.isArray(item))) {
        arr = [].concat(...arr);
    }

    return arr;
}

console.log(flatten(arr))

for in 与for of区别

直接遍历对象会报错!!!遍历对象必须给对象添加 Symbol.iterator 属性

其实一种数据结构只要部署了 Iterator 接口,我们就称这种数据结构是“可遍历的”(iterable)。由此,我们也可以发现 for of 遍历的其实是对象的 Symbol.iterator 属性。

所以 for...of 循环可以使用的范围包括:

数组
Set
Map
类数组对象,如 arguments 对象、DOM NodeList 对象
Generator 对象
字符串

for...in 循环只遍历可枚举属性,是为遍历对象属性而构建的,不建议与数组一起使用,数组可以用Array.prototype.forEach()和for ... of,那么for ... in的到底有什么用呢?

 它最常用的地方应该是用于调试,可以更方便的去检查对象属性

箭头函数

1、对 this 的关联。函数内置 this 的值,取决于箭头函数在哪儿定义,而非箭头函数执行的上下文环境。
2 、new 不可用。箭头函数不能使用 new 关键字来实例化对象,不然会报错。
3、this 不可变。函数内置 this 不可变,在函数体内整个执行环境中为常量。
4、没有arguments对象。更不能通过arguments对象访问传入参数。只能使用显式命名或其他ES6新特性来完成。

为什么箭头函数实例化会报错?

箭头函数永远不能用作构造函数,自然的不能使用new关键字调用它们,因此,对于箭头函数不存在prototype属性。、

那些对象可以使用解构?

解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。由于undefined和null无法转为对象,所以对它们进行解构赋值,都会报错。

数组、对象、字符串、数值和布尔值、函数参数

promise链式调用

每一次链式调用then操作返回的都是新的promise

分开调用返回的then相同

generator函数使用

function* helloWorldGenerator() {
  yield 'hello';
  yield 'world';
  return 'ending';
}

hw.next()
// { value: 'hello', done: false }

hw.next()
// { value: 'world', done: false }

hw.next()
// { value: 'ending', done: true }

hw.next()
// { value: undefined, done: true }

前端实现动画的方法有那些?

javascript直接实现;
SVG(可伸缩矢量图形);
CSS3 transition;(补间动画)
CSS3 animation;(帧动画)
Canvas动画;
requestAnimationFrame;

优化动画提高性能?

首先考虑css动画然后考虑js。其次可以用window.requestAnimationFrame()

发布订阅模式

function Pubsub(){
         //存放事件和对应的处理方法
        this.handles = {};
     }
     Pubsub.prototype={
         //传入事件类型type和事件处理handle
         on: function (type, handle) {
             if(!this.handles[type]){
                 this.handles[type] = [];
             }
             this.handles[type].push(handle);
         },
         emit: function () {
             //通过传入参数获取事件类型
            var type = Array.prototype.shift.call(arguments);
             if(!this.handles[type]){
                 return false;
             }
            for (var i = 0; i < this.handles[type].length; i++) {
                 var handle = this.handles[type][i];
                 //执行事件
                handle.apply(this, arguments);
            }
         },
         off: function (type, handle) {
             handles = this.handles[type];
             if(handles){
                 if(!handle){
                     handles.length = 0;//清空数组
                }else{
                    for (var i = 0; i < handles.length; i++) {
                         var _handle = handles[i];
                         if(_handle === handle){
                             handles.splice(i,1);
                         }
                     }
                 }
             }
         }
     }

框架相关:

生命周期:
生命周期

调用setstate()的时候发生了什么

主要答出批处理

1.this.setState(newState) =>
2.newState存入pending队列 =>
3.调用enqueueUpdate =>
4.是否处于批量更新模式 => 是的话将组件保存到dirtyComponents
5.不是的话遍历dirtyComponents,调用updateComponent,更新pending state or props

enqueueUpdate的源码如下:

 function enqueueUpdate(component){
   //injected注入的
   ensureInjected();
   //如果不处于批量更新模式
   if(!batchingStrategy.isBatchingUpdates){
       batchingStrategy.batchedUpdates(enqueueUpdate, component);
       return;
   }
   //如果处于批量更新模式
   dirtyComponents.push(component);
   } 

如果isBatchingUpdates为true,则对所有队列中的更新执行batchedUpdates方法,否则只把当前组件(即调用了setState的组件)放入dirtyComponents数组中,例子中4次setState调用的表现之所以不同,这里的逻辑判断起了关键作用。

redux 中间件原理

实现:

洋葱圈模型
compose函数

export default function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg
  }

  if (funcs.length === 1) {
    return funcs[0]
  }

  const last = funcs[funcs.length - 1]
  const rest = funcs.slice(0, -1)
  return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))
}

compose(f, g, h)(...args)效果等同于 f(g(h(...args)))

redux 中间件通过改写 store.dispatch 方法实现了action ->reducer的拦截,从上面的描述中可以更加清晰地理解 redux 中间件的洋葱圈模型

redux的原理

发布订阅模式

组件如何订阅store状态的呢?

使用 subscribe 订阅,subscribe 这个函数是用来去订阅 store 的变化,比如你每次对 store 进行 dispatch(action) 都会触发 subscribe注册的函数调用,这个在实际情况不是必须要的,看自己的应用场景,比如你想监控 store 的全局变化时 可以用 subscript 订阅一下,然后作一些反应。

函数节流:

function throttle(fn, interval = 300) {
    let canRun = true;
    return function () {
        if (!canRun) return;
        canRun = false;
        setTimeout(() => {
            fn.apply(this, arguments);
            canRun = true;
        }, interval);
    };
}

函数防抖:

function debounce(fn, interval = 300) {
    let timeout = null;
    return function () {
        clearTimeout(timeout);
        timeout = setTimeout(() => {
            fn.apply(this, arguments);
        }, interval);
    };
}

深拷贝实现

1.Object.assign() 不是完全的深拷贝
2.JSON.parse(JSON.stringify())
3.for in 循环

实现JSON.parse

1、直接使用eval()函数
eval("("+JSON+")")
2、循环递归

正则命名捕获组

const re = /(\d{4})-(\d{2})-(\d{2})/;
const match= re.exec('2019-01-01');
console.log(match[0]);    // → 2019-01-01
console.log(match[1]);    // → 2019
console.log(match[2]);    // → 01
console.log(match[3]);    // → 01

typeof与instanceof区别:
typeof判断基本类型比较好。

instanceof 运算符是用来测试一个对象是否在其原型链原型构造函数的属性。其语法是object instanceof constructor

instanceof 操作符用来比较两个操作数的构造函数。只有在比较自定义的对象时才有意义。 如果用来比较内置类型,将会和 typeof 操作符 一样用处不大。

判断类型的方法

HTTP 部分

HTTP与HTTPS区别

    1、https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。

    2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。

    3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。

    4、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。

HTTP相关资料

ajax请求:Ajax请求过程

触发预检请求

使用了PUT、DELETE、CONNECT、OPTIONS、TRACE、PATCH方法
人为设置了非规定内的其他首部字段,参考上面简单请求的安全字段集合,还要特别注意Content-Type的类型
XMLHttpRequestUpload 对象注册了任何事件监听器
请求中使用了ReadableStream对象

http2.0和HTTP1.1、HTTP1.0区别

http1.0 exprires last-modified 连接无法复用
http1.1 etag cache-control 支持长连接(connection) 支持文件断点续传
http2.0 多路复用 首部压缩 服务器推送 管线操作 二进制传输,传输速度更快了

http缓存相关

更多参考:勤奋蜂复习:http缓存
缓存字段优先级:

Cache-Control > Expires > ETag > Last-Modified

Expires:

Cache-Control

private:             客户端可以缓存
public:              客户端和代理服务器都可缓存
max-age=xxx:         缓存的内容将在 xxx 秒后失效
no-cache:            需要使用对比缓存来验证缓存数据(后面介绍)
no-store:            所有内容都不会缓存,强制缓存,对比缓存都不会触发

If-None-Match:
Etag:

我们可以看到两类缓存规则的不同,强制缓存如果生效,不需要再和服务器发生交互,而对比缓存不管是否生效,都需要与服务端发生交互。两类缓存规则可以同时存在,强制缓存优先级高于对比缓存,也就是说,当执行强制缓存的规则时,如果缓存生效,直接使用缓存,不再执行对比缓存规则。

对比缓存:

对比缓存,顾名思义,需要进行比较判断是否可以使用缓存,需要与服务器进行交互。
浏览器第一次请求数据时,服务器会将缓存标识与数据一起返回给客户端,客户端将二者备份至缓存数据库中。
再次请求数据时,客户端将备份的缓存标识发送给服务器,服务器根据缓存标识进行判断,判断成功后,返回304状态码,通知客户端比较成功,可以使用缓存数据。

http状态码

2XX

200 OK 表明请求已经成功. 默认情况下状态码为200的响应可以被缓存。
201 Created 是一个代表成功的应答状态码,表示请求已经被成功处理,并且创建了新的资源。通常是POST 请求的返回值
202 Accepted 表示服务器端已经收到请求消息,但是尚未进行处理。
204 No Content 成功状态响应码,表示该请求已经成功了,但是客户端客户不需要离开当前页面。默认情况下 204 响应是可缓存的。一个 ETag 标头包含在此类响应中。

3XX

301 永久重定向 说明请求的资源已经被移动到了由 Location 头部指定的url上,是固定的不会再改变。搜索引擎会根据该响应修正。

项目复盘

难点:前期调查、接口与需求文档、后期测试、项目当中。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注