Aaaaash / blog

✍️不定期断更
108 stars 9 forks source link

面试题整理 #7

Open Aaaaash opened 6 years ago

Aaaaash commented 6 years ago

操作系统

进程和线程的区别

HTTP

HTTP是什么

全称HyperText Transfer Protocol,超文本传输协议

是一个客户端和服务器端请求和应答的标准(TCP),属于应用层的通信协议

请求报文

响应报文

HTTP方法

HTTP状态码

服务器返回的 响应报文 中第一行为状态行,包含了状态码以及原因短语,用来告知客户端请求的结果。

1XX

表示接受的请求正在处理

2XX

3XX

重定向

4XX

客户端错误

5XX

服务器错误

JavaScript

JavaScript是单线程还是多线程

单线程,因为作为浏览器的脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM.如果是多线程,一个线程添加了一个DOM节点,另一个线程删除了DOM节点,浏览器无法判断以哪个线程为主

通过WebWorker标准可以简单模拟多线程操作,但子线程完全受主线程控制,且无法操作DOM,本质上还是单线程

为什么单线程的JavaScript可以实现异步

单线程同步的情况下一些耗时较长的任务将会导致页面渲染阻塞甚至卡死,浏览器是多线程的,因此浏览器为这些耗时较长的任务比如HTTP,定时器以及事件监听等单独开一个线程,由于JavaScript单线程的特性,所有任务会形成一个任务队列等待被执行,所以异步线程通过回调函数将执行完毕的异步任务放到JavaScript主线程的任务队列里

EventLoop

JavaScript主线程会一直循环查找任务队列里是否还有其他任务

function fn2(){
    //dosomething...
}
function fn1() {
    fn2()
    // dosomething...
}
function foo(){
    fn1();
}
foo();

比如这段代码,函数foo在执行时,主线程发现它还需要执行函数fn1,就把它推入一个栈中,执行到fn1时又发现它需要先执行fn2,于是又将fn1推入栈中,等到fn2执行结束,继续执行fn1,fn1执行结束将继续执行foo,待所有函数执行完成,主线程会让这些函数出栈,此时说明一个任务已经执行完毕,主线程会从下一个任务队列(callback equeue)中寻找下一个任务推入栈中,这个过程就叫做EventLoop

用C语言实现一个事件循环

扑街

以下代码输入结果是什么,为什么?

var a = {
    value: 'aaa',
    say: function(){
        console.log(this.vlaue);
    }
}

var b = {
    value: 'bbb',
    say: function() {
        console.log(this.value);
    }
}

a.say = b.say;
a.say(); // 输出结果是什么

输出字符串aaa,因为函数中this的指向始终是运行时调用它的对象

多个异步请求,如何使所有请求结束后才执行下一步

将请求包装为一个Promise数组,使用Promise.all执行,之后调用.then即可

React

React生命周期函数有哪些

React有几种组件

高阶组件具体使用场景是什么,解决了什么问题

高阶组件可以将多个组件中逻辑相同的部分抽离出来,由一个函数包装后形成一个新的组件,并且不影响原组件

class A extends PureComponent {
    componentWillMount() {
        console.log(this.props.name);
    }
    render(){
        return <div>I am {this.props.name}</div>
    }
}

class B extends PureComponent {
    componentWillMount() {
        console.log(this.props.name);
    }
    render(){
        return <div>I am {this.props.name}</div>
    }
}

function HighOrderLog(Component) {
    class Com extends PureComponent {

        componentWillMount() {
            console.log(this.props.name);
        }

        return <Component name={this.props.name} />
    }
    return Com;
}

setState是同步还是异步, 为什么,调用后怎么拿到新的state

异步,因为每次调用setState之后,React内部不光要更新state,还要进行一系列比如diff算法来决定下一次render,setState多次调用会造成一定程度上性能的损耗,所以React会将多个setState先合并再执行,这样一来避免了不必要的性能损失

拿到新的state有两种方法

父组件更新state后,子组件会不会rerender

分两种情况

为什么PureComponent不会引起子组件重渲染

因为PureComponent实现了shouldComponentUpdate方法,收到新的props后会做一次浅对比,既仅对比引用是否相同,shouldComponentUpdate方法返回布尔值,将决定组件是否进行重渲染

谈谈对Redux的理解

Redux是单Store的思想,通过view->action->reducer->view的单向数据流管理页面状态

Redux的State与React组件本身的State是否冲突

不冲突,业务层面上可以将React的state作为内部状态,既不依赖父组件或外部环境的组件可以使用state,而一些后端返回的数据,可能需要在多个组件中共享的,则可以作为全局的状态存放在redux的state中

Redux的中间件机制如何实现的

applyMIddleware源码解读

算法

算法的时间复杂度是什么意思

简单来讲就是指一个算法解决相应问题其代码执行基本语句需要的次数

算法中某个特定步骤的执行次数/对于总执行时间的估算成本,随着「问题规模」的增大时,增长的形式。

决定算法复杂度的两个重要因素是

实现冒泡排序,并说明其时间复杂度

function swap(arr, i, j) {
    const tmp = arr[i];
    arr[i] = arr[j];
    arr[j] = tmp;
}
const arrays = [21,454,6578,784534,443565,87978,4567];
function bubbleSort(arr) {
    const length = arr.length;
    for (let i = 0; i < length; i += 1) {
        for (let j = 0; j < length - 1; j += 1) {
            if (arr[j] > arr[j + 1]) {
                swap(arr, j, j + 1);
            }
        }
    }
}

bubbleSort(arrays); // [21, 454, 4567, 6578, 87978, 443565, 784534]

冒泡排序的策略是依次循环比较两个相邻的项,如果第一个比第二个大,则交换他们的位置,较小的项会逐渐向上移动到正确的位置

由于无论数组是否已经排序,都会对每一项进行双重的循环遍历,所以冒泡排序的时间复杂度是O(n²)

实现插入排序,并说明其时间复杂度

const arrays = [21,454,6578,784534,443565,87978,4567];
function insertionSort(arr) {
    const length = arr.length;
    for (let i = 1; i < length; i += 1) {
        for (let j = i - 1; j >= 0; j -= 1) {
            if (arr[j] > arr[j + 1]) {
                swap(arr, j, j + 1);
            }
        }
    }
}

insertionSort(arrays); // [21, 454, 4567, 6578, 87978, 443565, 784534]

插入排序的策略是,将数组分为两个部分, 假设原第一项是已经排好序的一个数组,那么用其余数组项依次与这个已经排好序的数组对比,小的交换位置,直到其余数组为空,排序结束

这段代码中,外循环从i = 1开始,即表示不论第一项多大,它被看做一个已经排好序的数组,从原数组第二项开始遍历,由于第一轮循环,有序数组只有一项,所以直接与第二项对比后交换位置,依次类推

插入排序是不稳定的排序,最好的情况下时间复杂度为O(n),最坏情况下为O(n²)

数组扁平化

[123, [2,32,445,[32,54,4]]]转为[123,2,32,445,32,54,4]

使用isArray方法判断是否为数组,递归调用即可

const arrays = [123, [2, 32, 445, [32, 54, 4]]];

function flatten(arr) {
  return arr.reduce((pre, cur) => {
       return pre.concat(Array.isArray(cur) ? flatten(cur) : cur);
  },[])
}
flatten(arrays); // [123, 2, 32, 445, 32, 54, 4]

翻转二叉树

function reversalBST(root) {
    if (root === null) return null;
    const temp = root.left;
    root.left = root.right;
    root.right = temp;
    temp(root.left);
    temp(root.right);
}