前端面试核心篇 (附答案)

1.webpack和gulp区别(模块化与流的区别)

答:

gulp强调的是前端开发的工作流程,我们可以通过配置一系列的task,定义task处理的事务(例如文件压缩合并、雪碧图、启动server、版本控制等),然后定义执行顺序,来让gulp执行这些task,从而构建项目的整个前端开发流程。

webpack是一个前端模块化方案,更侧重模块打包,我们可以把开发中的所有资源(图片、js文件、css文件等)都看成模块,通过loader(加载器)和plugins(插件)对资源进行处理,打包成符合生产环境部署的前端资源。

2.redux里常用方法

答:

在组件化的应用中,会有着大量的组件层级关系,深嵌套的组件与浅层父组件进行数据交互,变得十分繁琐困难。而redux,站在一个服务级别的角度,可以毫无阻碍地将应用的状态传递到每一个层级的组件中。redux就相当于整个应用的管家。

提供 getState() 方法获取 state;

提供 dispatch(action) 方法更新 state;

通过 subscribe(listener) 注册监听器;

等等

3.说说vue和react的区别

答:

参考:https://blog.csdn.net/caoyan0829/article/details/78275701

4.react的生命周期函数

答:

初始化

1、getDefaultProps()

设置默认的props,也可以用dufaultProps设置组件的默认属性.

2、getInitialState()

在使用es6的class语法时是没有这个钩子函数的,可以直接在constructor中定义this.state。此时可以访问this.props

3、componentWillMount()

组件初始化时只调用,以后组件更新不调用,整个生命周期只调用一次,此时可以修改state。

4、 render()

react最重要的步骤,创建虚拟dom,进行diff算法,更新dom树都在此进行。此时就不能更改state了。

5、componentDidMount()

组件渲染之后调用,只调用一次。

更新

6、componentWillReceiveProps(nextProps)

组件初始化时不调用,组件接受新的props时调用。

7、shouldComponentUpdate(nextProps, nextState)

react性能优化非常重要的一环。组件接受新的state或者props时调用,我们可以设置在此对比前后两个props和state是否相同,如果相同则返回false阻止更新,因为相同的属性状态一定会生成相同的dom树,这样就不需要创造新的dom树和旧的dom树进行diff算法对比,节省大量性能,尤其是在dom结构复杂的时候

8、componentWillUpdata(nextProps, nextState)

组件初始化时不调用,只有在组件将要更新时才调用,此时可以修改state

9、render()

组件渲染

10、componentDidUpdate()

组件初始化时不调用,组件更新完成后调用,此时可以获取dom节点。

卸载

11、componentWillUnmount()

组件将要卸载时调用,一些事件监听和定时器需要在此时清除。

5.reactJs的组件交流

答:

1)父组件向子组件传值:主要是利用props来进行交流

2)子组件向父组件传值:子组件通过控制自己的state然后告诉父组件的点击状态。然后在父组件中展示出来,如图:

3)没有任何嵌套关系的组件之间传值:如果组件之间没有任何关系,组件嵌套层次比较深(个人认为 2 层以上已经算深了),或者你为了一些组件能够订阅、写入一些信号,不想让组件之间插入一个组件,让两个组件处于独立的关系。对于事件系统,这里有 2 个基本操作步骤:订阅(subscribe)/监听(listen)一个事件通知,并发送(send)/触发(trigger)/发布(publish)/发送(dispatch)一个事件通知那些想要的组件。

6.有了解过react的虚拟DOM吗,虚拟DOM是怎么对比的呢

答:

当然是使用的diff算法,diff算法有三种优化形式:

tree diff:将新旧两颗DOM树按照层级遍历,只对同级的DOM节点进行比较,即同一父节点下的所有子节点,当发现节点已经不存在,则该节点及其子节点会被完全删除,不会进一步比较

component diff:不同组件之间的对比,如果组件类型相同,暂不更新,否则删除旧的组件,再创建一个新的组件,插入到删除组件的位置

element diff:在类型相同的组件内,再继续对比组件内部的元素,

参考:https://juejin.im/post/5a3200fe51882554bd5111a0

7.setState之后的流程

答:

在代码中调用setState函数之后,React 会将传入的参数对象与组件当前的状态合并,然后触发所谓的调和过程(Reconciliation)。 经过调和过程,React 会以相对高效的方式根据新的状态构建 React 元素树并且着手重新渲染整个UI界面。 在 React 得到元素树之后,React 会自动计算出新的树与老树的节点差异,然后根据差异对界面进行最小化重渲染。 在差异计算算法中,React 能够相对精确地知道哪些位置发生了改变以及应该如何改变,这就保证了按需更新,而不是全部重新渲染。

8.OSI七层模型

答:

osi七层模型可以说是面试必考基础了

从上到下分别是:

应用层:文件传输,常用协议HTTP,snmp,FTP ,

表示层:数据格式化,代码转换,数据加密,

会话层:建立,解除会话

传输层:提供端对端的接口,tcp,udp

网络层:为数据包选择路由,IP,icmp

数据链路层:传输有地址的帧

物理层:二进制的数据形式在物理媒体上传输数据

9.怎么生成token,怎么传递

答:

Token是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个Token便将此Token返回给客户端,以后客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码。

基于 Token 的身份验证

使用基于 Token 的身份验证方法,在服务端不需要存储用户的登录记录。流程是这样的:

1)客户端使用用户名跟密码请求登录

2)服务端收到请求,去验证用户名与密码

3)验证成功后,服务端会签发一个 Token,再把这个 Token 发送给客户端

4)客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 Local Storage 里

5)客户端每次向服务端请求资源的时候需要带着服务端签发的 Token

6)服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据

7)APP登录的时候发送加密的用户名和密码到服务器,服务器验证用户名和密码,如果成功,以某种方式比如随机生成32位的字符串作为token,存储到服务器中,并返回token到APP,以后APP请求时,

8)凡是需要验证的地方都要带上该token,然后服务器端验证token,成功返回所需要的结果,失败返回错误信息,让他重新登录。其中服务器上token设置一个有效期,每次APP请求的时候都验证token和有效期

token的优势

1)无状态、可扩展 :在客户端存储的Tokens是无状态的,并且能够被扩展。基于这种无状态和不存储Session信息,负载负载均衡器能够将用户信息从一个服务传到其他服务器上。如果我们将已验证的用户的信息保存在Session中,则每次请求都需要用户向已验证的服务器发送验证信息(称为Session亲和性)。用户量大时,可能会造成  一些拥堵。但是不要着急。使用tokens之后这些问题都迎刃而解,因为tokens自己hold住了用户的验证信息。

2)安全性 :请求中发送token而不再是发送cookie能够防止CSRF(跨站请求伪造)。即使在客户端使用cookie存储token,cookie也仅仅是一个存储机制而不是用于认证。不将信息存储在Session中,让我们少了对session操作。token是有时效的,一段时间之后用户需要重新验证。我们也不一定需要等到token自动失效,token有撤回的操作,通过token revocataion可以使一个特定的token或是一组有相同认证的token无效。

3)可扩展性 :Tokens能够创建与其它程序共享权限的程序。例如,能将一个随便的社交帐号和自己的大号(Fackbook或是Twitter)联系起来。当通过服务登录Twitter(我们将这个过程Buffer)时,我们可以将这些Buffer附到Twitter的数据流上(we are allowing Buffer to post to our Twitter stream)。使用tokens时,可以提供可选的权限给第三方应用程序。当用户想让另一个应用程序访问它们的数据,我们可以通过建立自己的API,得出特殊权限的tokens。

4)多平台跨域 :我们提前先来谈论一下CORS(跨域资源共享),对应用程序和服务进行扩展的时候,需要介入各种各种的设备和应用程序。Having our API just serve data, we can also make the design choice to serve assets from a CDN. This eliminates the issues that CORS brings up after we set a quick header configuration for our application.只要用户有一个通过了验证的token,数据和资源就能够在任何域上被请求到。Access-Control-Allow-Origin: *

5)基于标准 :创建token的时候,你可以设定一些选项。我们在后续的文章中会进行更加详尽的描述,但是标准的用法会在JSON Web Tokens体现。最近的程序和文档是供给JSON Web Tokens的。它支持众多的语言。这意味在未来的使用中你可以真正的转换你的认证机制。

参考:https://www.cnblogs.com/lufeiludaima/p/pz20190203.html

10.操作系统进程和线程的区别

答:

进程,是并发执行的程序在执行过程中分配和管理资源的基本单位,是一个动态概念,竞争计算机系统资源的基本单位。

线程,是进程的一部分,一个没有线程的进程可以被看作是单线程的。线程有时又被称为轻权进程或轻量级进程,也是 CPU 调度的一个基本单位。

11.Linux查询进程指令,查询端口,杀进程

查询进程:

ps 命令用于查看当前正在运行的进程。

grep 是搜索

例如: ps -ef | grep java

表示查看所有进程里CMD是java的进程信息

ps -aux | grep java

-aux 显示所有状态

ps

杀死进程:

kill -9[PID]

12.Linux修改权限

答:

chgrp:  修改用户组            chgrp  caoyan(存在的用户组).文件名

chown:修改文件拥有者     chown caoyan .bashrc_test

chmod:  修改权限                chmod 777 .bashrc_test

13.把多维数组变成一维数组的方法

答:

法一:递归

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;
}

法二:toString

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

法三:reduce

function flatten(arr) {
    return arr.reduce(function(prev, next){
        return prev.concat(Array.isArray(next) ? flatten(next) : next)
    }, [])
}

法四:rest运算符

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

14.几种常见的排序算法,手写

答:

不稳定: 一些快选堆   (希快选堆)

冒泡排序:重复地走访过要排序的元素列,依次比较两个相邻的元素,如果他们的顺序(如从大到小、首字母从A到Z)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素已经排序完成

参考:https://blog.csdn.net/qq_39741605/article/details/80821595

var bubbleSort=function(arr){
    var temp;
	for(var i=0;i<arr.length-1;i++){
		for(var j=i+1;j<arr.Length-1-i;j++){
			if(arr[i]>arr[j]){//如果前面的数据比后面的大就交换
				temp=arr[i];
				arr[i]=arr[j];
				arr[j]=temp;
			}
		}
	} 
	return arr;
}
console.log("The result is:"+bubbleSort(arr));

快速排序:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列

var quickSort=function(arr){ 
	//如果数组长度小于等于1无需判断直接返回即可
	if(arr.length<=1){
		return arr;
	}
	var midIndex=Math.floor(arr.length/2);//取基准点
	var midIndexVal=arr.splice(midIndex,1)[0];//取基准点的值,splice(index,1)函数可以返回数组中被删除的那个数arr[index+1]
	var left=[];//存放比基准点小的数组
	var right=[];//存放比基准点大的数组
	//遍历数组,进行判断分配
	for(var i=0;i<arr.length;i++){
		if(arr[i]<midIndexVal){
			left.push(arr[i]);//比基准点小的放在左边数组
		}
		else{
			right.push(arr[i]);//比基准点大的放在右边数组
		}
	}
	//递归执行以上操作,对左右两个数组进行操作,直到数组长度为<=1;
	return quickSort(left).concat(midIndexVal,quickSort(right));
};
//console.log(quickSort(arr));

插入排序:插入排序的基本操作就是将一个数据插入到已经排好序的有序数据中,从而得到一个新的、个数加一的有序数据,算法适用于少量数据的排序,时间复杂度为O(n^2)。是稳定的排序方法。

function insertSort(arr) {
    var len = arr.length;
    var temp;
    for (var i = 1;i < len;i++){
        temp = arr[i]
        for (var j = i;j > 0 && temp < arr[j-1];j--){
            // 当前值和之前的每个值进行比较,发现有比当前值小的值就进行重新赋值
            arr[j] = arr[j-1];
        }
        arr[j] = temp;
    }
    return arr;
}

选择排序:从原始数组中找到最小的元素,并把该元素放在数组的最前面,然后再从剩下的元素中寻找最小的元素,放在之前最小元素的后面,直到排序完毕

function selectSort(arr){
      var min,temp;
      for(var i=0;i<arr.length-1;i++){
          min=i;
          for(var j=i+1;j<arr.length;j++){
              if(arr[j]<arr[min]){   //寻找最小的数 
                  min = j;    //保存最小数的索引 
              } 
          }
          temp=arr[i];    //值交换
          arr[i]=arr[min];
          arr[min]=temp;
      }
      return arr;
  }
//  console.log(selectSort([6,1,2,4,3,5])) 

15.常用的数组的去重方法

答:

1)es6去重(推荐)

let arr = [1,2,2,3,4,4,4];

let s = new Set(arr);   //结果:Set {1,2,3,4}

let newArr = Array.from(s);   //结果:[1,2,3,4],完成去重

2)indexOf去重

function uniq(array){
    var temp = []; //一个新的临时数组
    for(var i = 0; i < array.length; i++){ //新建一新数组,遍历传入数组,值不在新数组就push进该新数组中
        if(temp.indexOf(array[i]) == -1){
            temp.push(array[i]);
        }
    }
    return temp;
}

3)任意类型去重

let arr = [123, [1, 2, 3], [1, "2", 3], [1, 2, 3], "meili"];
const unique = (array) => {
    let obj = {}
    return array.filter((item, index) => {
      // 防止key重复
      let newItem = item + JSON.stringify(item)
      return obj.hasOwnProperty(newItem) ? false : obj[newItem] = true
    })
}
console.log(unique(arr)); //[123, [1, 2, 3], [1, "2", 3], "meili"]

前端面试基础篇 二:https://blog.csdn.net/caoyan0829/article/details/89965445

前端面试基础篇 一:https://blog.csdn.net/caoyan0829/article/details/89888629

此文章参考牛客面经汇总

发布了149 篇原创文章 · 获赞 51 · 访问量 23万+

猜你喜欢

转载自blog.csdn.net/caoyan0829/article/details/90022103