前端面试卷二

232 阅读7分钟

算法手写题(数组扁平化flat)

已知如下数组,编写一个程序将数组扁平化去并除其中重复部分数据,最终得到一个升序。且不重复的数组

var arr = [ [1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14] ] ] ], 10];
//Array.from 将set转换为数组
//new Set //创建一个set对象用于数组去重
//arr.flat(infinity) 数组不限维数扁平化
//sort 排序 a>b才需要交换顺序 即从小到大排列
return Array.from(new Set(arr.flat(infinity))).sort((a,b)=>{return a-b})

//array.flat(Infinity)方法实现:
Array.prototype.flat = (depth)=>{    //depth==inifity时无限递归到没有子元素数组为止,不用depth--
    var arr = []
    this.forEach(item=>{
        if(Array.isArray(item)&&depth){
            arr = arr.concat(item.flat(depth--))    //递归子元素数组
        }
        else{
            arr.push(item)
        }
    })
    return arr
}

JS 异步解决方案的发展历程以及优缺点

1、回调函数(callback)
优点:解决了同步的问题(只要有一个任务耗时很长,后面的任务都必须排队
等着,会拖延整个程序的执行。)
缺点:回调地狱,不能用 try catch 捕获错误,不能 return
2、Promise
优点:解决了回调地狱的问题,链式调用
缺点:无法取消 Promise ,错误需要通过回调函数来捕获,写法不够优雅
3、Generator
优点:可以控制函数的执行,可以配合 co 函数库使用。
缺点:要实现迭代器
4、Async/await
优点:代码清晰,不用像 Promise 写一大堆 then 链,处理了回调地狱的问题
缺点:await 将异步代码改造成同步代码,如果多个异步操作没有依赖性而使
用 await 会导致性能上的降低。 5、事件处理($emit,$on) 优点:写法简便,观察者模式,可用于任意组件之间通信 缺点:需要一直监听,耗损性能,而且代码不好维护(到处都有)

Promise 构造函数是同步执行还是异步执行,那么****then 方法呢?

const promise = new Promise((resolve, reject) => {    
    console.log(1)    
    resolve()    
    console.log(2)
})
promise.then(() => {
   console.log(3)
})
console.log(4)
执行结果是:1243,promise构造函数是同步执行的,then方法是异步执行的

如何实现一个 new

1、创建一个新的对象
2、把obj的__proto__指向fn的prototype,实现继承
3、改变this的指向,执行构造函数、传递参数,fn.apply(obj,) 或者 fn.call()
4、返回新的对象obj

function _new(fn,args){ //fn代表要继承的类或者构造函数
    var obj = {}    //创建对象
    obj._proto_ = fn.prototype //把obj的__proto__指向fn的prototype,实现继承
    fn.apply(obj,args)    //调用构造函数并把this指向obj,obj就拥有了fn里面的所有方法
    return obj
}

简单讲解一下 http2 的多路复用

HTTP2 采用二进制格式传输,取代了 HTTP1.x 的文本格式,二进制格式解析更
高效。
多路复用代替了 HTTP1.x 的序列和阻塞机制,所有的相同域名请求都通过同一
个 TCP 连接并发完成。
在 HTTP1.x 中,并发多个请求需要多个 TCP 连接,浏览器为了控制资源会有 6-8 个 TCP 连接限制。
HTTP2 中同域名下所有通信都在单个连接上完成,消除了因多个 TCP 连接而带来的延
时和内存消耗。
单个连接上可以并行交错的请求和响应,之间互不干扰。

概念:

在 HTTP/2 中,有两个非常重要的概念,分别是帧(frame)和流(stream)。 帧代表着最小的数据单位,每个帧会标识出该帧属于哪个流,流也就是多个帧组成的数据流。同一 Tcp 中可以发送多个请求,对端可以通过帧中的标识知道属于哪个请求。通过这个技术,可以避免 HTTP 旧版本中的队头阻塞问题,极大的提高传输性能。

谈谈你对 TCP 三次握手和四次挥手的理解

[链接:https://blog.csdn.net/qq_38950316/article/details/81087809](https://blog.csdn.net/qq_38950316/article/details/81087809)

TCP三次握手:
1、客户端发送syn包到服务器,等待服务器确认接收。
2、服务器确认接收syn包并确认客户的syn,并发送回来一个syn+ack的包给客户端。
3、客户端确认接收服务器的syn+ack包,并向服务器发送确认包ack,二者相互建立联系后,完成tcp三次握手。
四次挥手就是中间多了一层:等待服务器再一次响应回复相关数据的过程。
三次握手之所以是三次是保证client和server均让对方知道自己的接收和发送能力没问题而保证的最小次数。
第一次client => server 只能server判断出client具备发送能力
第二次 server => client client就可以判断出server具备发送和接受能力。此时client还需让server知道自己接收能力没问题于是就有了第三次
第三次 client => server 双方均保证了自己的接收和发送能力没有问题
其中,为了保证后续的握手是为了应答上一个握手,每次握手都会带一个标识 seq,后续的ACK都会对这个seq进行加一来进行确认。
sync包:协议版本,随机数加密密钥,加密方法rsa,压缩方法,req
ask包:多了服务器证书

TCP四次挥手:
客户端发送fin信号,告诉服务器需要断开连接,等待服务器响应.
服务器接收到fin信号并确认,回应等待数据发送完成请求,发送ack包(失败可以重新发送).
服务器数据传输完毕,发送给客户端确认信息,等待客户端响应,如响应,则服务端直接关闭.
客户端收到响应,并确认信息,然后再次发送ack包给服务端,并进入time_wait状态,等待2MSL(最大报文生存时间)后,没有响应,则直接关闭.
MSL指一个片段在网络中最大的存活时间,2MSL就是一个发送和一个回复所需的最大时间。如果直到2MSL,Client都没有再次收到FIN(服务端没收到ack),那么Client推断ACK已经被成功接收,则结束TCP连接。等待的作用就是防止服务端没收到ack消息发送失败的情况。

A、B 机器正常连接后,B 机器突然重启,问 A 此时****处于 TCP 什么状态

因为 B 会在重启之后进入 tcp 状态机的 listen 状态,只要当 a 重新发送一个数据
包(无论是 syn 包或者是应用数据),b 端应该会主动发送一个带 rst 位的重置
包来进行连接重置,所以 a 应该在 syn_sent 状态

React 中 setState 什么时候是同步的,什么时候是异步的

1、由 React 控制的事件处理程序,以及生命周期函数调用setState不会同步更
新state 。this.setState会加入到异步更新队列中。
2、React 控制之外的事件中调用setState是同步更新的。比如原生 js 绑定的事
件,setTimeout/setInterval 等。原生事件不会进入异步更新流程,setTimeout 回调任务会在异步更新结束后调用。this.setState((nextStatus)=>{}),nextState也是 最新的。

React setState 笔试题,下面的代码输出什么?

class Example extends React.Component {   
    constructor() {     
        super()     
        this.state = {       val: 0     }   
    }  
    componentDidMount() {     
        this.setState({ val: this.state.val + 1 })     
        console.log(this.state.val)     // 第 1 次 log     
        this.setState({ val: this.state.val + 1 })     
        console.log(this.state.val)     // 第 2 次 log     
        setTimeout(() => {       
        this.setState({ val: this.state.val + 1 })       
            console.log(this.state.val)       // 第 3 次 log       
            this.setState({ val: this.state.val + 1 })       
            console.log(this.state.val)       // 第 4 次 log}, 0)     
        },0)  
    }
} 
答:0, 0, 2, 3

解析:外层的this.setState是异步的,且异步的setState会合并,因此前两次合并为一次setState,
因为是异步所以都是0,而setTimeout中的setState是同步的,且拿到的最新的state为1,然后同步执行
两次加1操作变成23

介绍下 npm 模块安装机制,为什么输入 npm install就可以自动安装对应的模块

查询node_modules目录之中是否已经存在指定模块
若存在,不再重新安装
若不存在npm向registry查询模块压缩包的网址,下载压缩包,存放在根目录下的.npm目录里,解压压缩包到当前项目的node_modules目录