20210330某农业公司前端面试总结

目录

  1. 快速排序
  2. 你怎么带人的
  3. 说一下从url输入到返回请求的过程
  4. let、var、const的区别
  5. 深拷贝的实现
  6. cookie和storage的区别
  7. compose函数的实现
  8. 性能优化
  9. 统计字符串个数
  10. Vue和react的区别
1. 快速排序
const quickSort = (arr) => {
  let length = arr.length;
  if (length < 2) {
    return arr;
  } else {
    // 选择标尺元素
    let left = [];
    let right = [];
    let temp = arr[0];
    // 把比标尺元素小的放左边,大的放右边
    for (let i = 1; i < length; i++) {
      if (temp > arr[i]) {
        left.push(arr[i]);
      } else {
        right.push(arr[i]);
      }
    }
    // 进行递归
    return quickSort(left).concat(temp, quickSort(right));
  }
}
const arr = [2, 3, 5, 23, 43, 12, 1];
console.log(quickSort(arr))
2. 你怎么带人的

我只带过实习生。他们刚来的时候,我会让他们先学习。开始的时候,给他们一个小任务,比如做一个计算器,或者写一个项目中的几个页面。然后他们熟悉后,就让他们开始参与到项目上来。做一些简单的查询功能。后面做熟了,一些业务功能,简单的也开始让他们上手做。中间遇到不会的,就去指导他们,给他们解惑。

3. 说一下从url输入到返回请求的过程

首先是DNS查询,如果这一步做了智能DNS解析的话,会提供访问速度最快的IP地址回来。

DNS的作用就是通过域名查询到具体的IP。

因为IP存在数字和英文的组合(IPv6),很不利于人类记忆,所以就出现了域名。你可以把域名看成是某个IP的别名,DNS就是去查询这个别名的真正名称是什么。

在TCP握手之前就已经进行了DNS查询,这个查询是操作系统自己做的。当你在浏览器中想访问www.baidu.com时,会进行一下操作:

  1. 操作系统会首先在本地缓存中查询IP
  2. 没有的话会去系统配置的DNS服务器中查询
  3. 如果这时候还没得话,会直接去DNS根服务器查询,这一步查询会找出负责com这个一级域名的服务器
  4. 然后去该服务器查询baidu这个二级域名
  5. 接下来三级域名的查询其实是我们配置的,你可以给www这个域名配置一个IP,然后还可以给别的三级域名配置一个IP

以上介绍的是DNS迭代查询,还有种是递归查询,区别就是前者是由客户端去做请求,后者是由系统配置的DNS服务器做请求,得到结果后将数据返回给客户端。

PS:DNS是基于UDP做的查询,大家也可以考虑下为什么之前不考虑使用TCP去实现。

接下来是TCP握手,应用层会下发数据给传输层,这里TCP协议会指明两端的端口号,然后下发给网络层。网络层中的IP协议会确定IP地址,并且指示了数据传输中如何跳转路由器。然后包会再被封装到数据链路层的数据帧结构中,最后就是物理层面的传输了。

在这一部分中,可以详细说下TCP的握手情况以及TCP的一些特性。

当TCP握手结束后就会进行TLS握手,然后就开始正式的传输数据。

在这一部分中,可以详细说下TLS的握手情况以及两种加密方式的内容。

数据在进入服务端之前,可能还会先经过负责负载均衡的服务器,它的作用就是将请求合理的分发到多台服务器上,这时假设服务端会响应一个HTML文件。

首先浏览器会判断状态码是什么,如果是200那就继续解析,如果400或500的话就会报错,如果300的话会进行重定向,这里会有个重定向计数器,避免过多次的重定向,超过次数也会报错。

浏览器开始解析文件,如果是gzip格式的话会先解压一下,然后通过文件的编码格式知道该如何去解码文件。

文件解码成功后会正式开始渲染流程,先会根据HTML构建DOM树,有CSS的话会去构建CSSOM树。如果遇到script标签的话,会判断是否存在async或者defer,前者会并行进行下载并执行JS,后者会先下载文件,然后等待HTML解析完成后顺序执行。

如果以上都没有,就会阻塞住渲染流程直到JS执行完毕。遇到文件下载的会去下载文件,这里如果使用HTTP/2协议的话会极大的提高多图的下载效率。

CSSOM树和DOM树构建完成后会开始生成Render树,这一步就是确定页面元素的布局、样式等等诸多方面的东西

在生成Render树的过程中,浏览器就开始调用GPU绘制,合成图层,将内容显示在屏幕上了。

4. let、var、const的区别
  1. let和const声明的变量不存在变量提升,如果要使用这个变量,我们需要在变量定义之后使用;
  2. let和const不能重复声明变量,如果重复声明会报错;
  3. 用let和const在全局声明变量不会给window增加属性;
  4. let和const出现在代码块中,会把代码块(字面量声明对象除外)变成会计作用域,并且出现暂时性死区。
5. 深拷贝的实现
function cloneDeep(obj) {
  const constructor = obj.constructor;
  if (obj === null) return null;
  if (typeof obj !== "object") return obj;
  if (/^(RegExp|Date)$/i.test(constructor.name)) return new constructor(obj);
  let clone = new constructor();
  for (let key in obj) {
    if (!obj.hasOwnProperty(key)) break;
    clone[key] = cloneDeep(obj[key]);
  }
  return clone;
}
6. cookie和storage的区别

cookie非常小,它的大小限制在4kb左右,它的主要用于保存登陆信息。

localStorage用于本地存储。

localStorage用于本地存储,但是页面关闭就会消失。

7. compose函数的实现
function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => {
      return arg;
    };
  }
  if (funcs.length === 1) {
    return funcs[0];
  }
  // funcs=[add1, add1, mul3, div2];
  return funcs.reduce((a, b) => {
    return (...args) => {
      return a(b(...args));
    };
  });
  // 第一轮执行 a=add1  b=add1  返回匿名的箭头函数  (...args)=>a(b(...args))
  // 第二轮执行 a=(...args)=>a(b(...args)) b=mul3  返回值(...args)=>a(b(...args))
  // 第三轮执行 a=(...args)=>a(b(...args)) b=div2  返回值(...args)=>a(b(...args))
  // ====
  // ((...args)=>a(b(...args)))(0)  => a(b(0)) => a(div2(0))
  // ((...args)=>a(b(...args)))(0) => a(b(0)) => a(mul3(0))
  // ((...args)=>a(b(...args)))(0) => a(b(0)) => add1(add1(0))
  // return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
const add1 = (x) => x + 1;
const mul3 = (x) => x * 3;
const div2 = (x) => x / 2;
let result = compose()(0);
console.log(result);
result = compose(add1)(0);
console.log(result);
result = compose(add1, add1, mul3, div2)(0);
console.log(result);
8. 性能优化
减少请求资源大小或者次数
  1. 尽量和并和压缩css和js文件。(将css文件和并为一个。将js合并为一个)
    • 原因:主要是为了减少http请求次数以及减少请求资源的大小
    • 打包工具:webpack、gulp、grunt
  2. 尽量所使用的字体图标或者SVG图标来代替传统png图
    • 因为字体图标或者SVG是矢量图,代码编写出来的,方大不会变形,而且渲染速度快
  3. 采用图片的懒加载(延迟加载)
    • 目的为了,减少页面第一次加载过程中http的请求次数
    • 页面开始加载时不去发送http请求,而是放置一张占位图
    • 当页面加载完时,并且图片在可视区域再去请求加载图片信息
  4. 能用css做的效果,不要用js做,能用原生js做的,不要轻易去使用第三方插件
    • 避免引入第三方大量的库。而自己却只是用里面的一个小功能
  5. 使用雪碧图或者是说图片精灵
    • 把所有相对较小的资源图片,绘制在一张大图上,只需要将大图下载下来,然后利用
    • 图片定位来讲小图展现在页面中(background-position:百分比,数值)
  6. 减少对cookie的使用(最主要的就是减少本地cookie存储内容的大小),因为客户端操作cookie的时候,这些信息总是在客户端和服务端传递。如果上设置不当,每次发送一个请求将会携带cookie
  7. 前端与后端进行数据交互时,对于多项数据尽可能基于json格式来进行传送。相对于使用xml来说传输有这个优势
    • 目的:是数据处理方便,资源偏小
  8. 前端与后端协商,合理使用keep-alive
  9. 前端与服务器协商,使用响应资源的压缩
  10. 避免使用iframe
    • 不仅不好管控样式,而且相当于在本页面又嵌套其他页面,消耗性能会更大。因为还回去加载这个嵌套页面的资源
  11. 在基于ajax的get请求进行数据交互的时候,根据需求可以让其产生缓存(注意:这个缓存不是我们常看到的304状态码,去浏览器本地取数据),这样在下一次从相同地址获取是数据时,取得就是上一次缓存的数据。(注意:很少使用,一般都会清空。根据需求来做)
代码优化相关
  1. 在js中尽量减少闭包的使用
    • 原因:使用闭包后,闭包所在的上下文不会被释放
  2. 减少对DOM操作,主要是减少DOM的重绘与回流(重排)
    • 关于重排(回流)的分离读写:如果需要设置多个样式,把设置样式全放在一起设置,不要一条一条的设置。使用文档碎片或者字符串拼接做数据绑定(DOM的动态创建)
  3. 在js中避免嵌套循环和"死循环"(一旦遇到死循环,浏览器就会直接卡掉)
  4. 把css放在body上,把js放在body下面
    • 让其先加载css(注意:这里关于优化没有多大关系)
  5. 减少css表达式的使用
  6. css选择器解析规则所示从右往左解析的。减少元素标签作为对后一个选择对象
  7. 尽量将一个动画元素单独设置为一个图层(避免重绘或者回流的大小)
    • 注意:图层不要过多设置,否则不但效果没有达到反而更差了
  8. 在js封装过程中,尽量做到低耦合高内聚。减少页面的冗余代码
  9. css中设置定位后,最好使用z-index改变盒子的层级,让盒子不在相同的平面上
  10. css导入的时候尽量减少@import导入式,因为@import是同步操作,只有把对应的样式导入后,才会继续向下加兹安,而link是异步的操作
  11. 使用window.requestAnimationFrame(js的帧动画)代替传统的定时器动画
    • 如果想使用每隔一段时间执行动画,应该避免使用setInterval,尽量使用setTimeout
    • 代替setInterval定时器。因为setInterval定时器存在弊端:可能造成两个动画间隔时间缩短
  12. 尽量减少使用递归。避免死递归
    • 解决:建议使用尾递归
  13. 基于script标签下载js文件时,可以使用defer或者async来异步加载
  14. 在事件绑定中,尽可能使用事件委托,减少循环给DOM元素绑定事件处理函数。
  15. 在js封装过程中,尽量做到低耦合高内聚。减少页面的冗余代码
  16. 减少Flash的使用
存储
  1. 结合后端,利用浏览器的缓存技术,做一些缓存(让后端返回304,告诉浏览器去本地拉取数据)。(注意:也有弊端)可以让一些不太会改变的静态资源做缓存。比如:一些图片,js,cs
  2. 利用h5的新特性(localStorage、sessionStorage)做一些简单数据的存储,
    • 避免向后台请求数据或者说在离线状态下做一些数据展示。
其他优化
  1. 避免使用iframe不仅不好管控样式,而且相当于在本页面又嵌套其他页面,消耗性能会更大。因为还回去加载这个嵌套页面的资源
  2. 页面中的是数据获取采用异步编程和延迟分批加载,使用异步加载是数据主要是为了避免浏览器失去响应。如果你使用同步,加载数据很大并且很慢
    • 那么,页面会在一段时间内处于阻塞状态。目的:为了解决请求数据不耽搁渲染,提高页面的渲染效率。
    • 解决方法:需要动态绑定的是数据区域先隐藏,等数据返回并且绑定后在让其显示延迟分批加载类似图片懒加载。减少第一次页面加载时候的http请求次数
  3. 页面中出现音视频标签,我们不让页面加载的时候去加载这些资源(否则第一次加载会很慢)
    • 解决方法:只需要将音视频的preload=none即可。
    • 目的:为了等待页面加载完成时,并且音视频要播放的时候去加兹安音视频资源
  4. 尽量将一个动画元素单独设置为一个图层(避免重绘或者回流的大小)
    • 注意:图层不要过多设置,否则不但效果没有达到反而更差了
  5. 服务端渲染
  6. CDN技术
  7. 骨架屏技术
9. 统计字符串个数
function countObj(str) {
  var obj = {};
  for (var n of str) {
    if (obj[n]) {
      obj[n]++;
    } else {
      obj[n] = 1;
    }
  }
  return obj;
}
let str = 'ssdsdasbvc';
console.log(countObj(str));
10. Vue和react的区别
  1. 数据监听实现原理不同:

Vue通过getter/setter以及一些函数的劫持,能精确知道数据变化。

React默认是通过比较引用的方式(diff)进行的,如果不优化可能导致大量不必要的VDOM的重新渲染。为什么React不精确监听数据变化呢?这是因为Vue和React设计理念上的区别,Vue使用的是可变数据,而React更强调数据的不可变,两者没有好坏之分,Vue更加简单,而React构建大型应用的时候更棒。

  1. 数据流的不同

vue 2.x开始进制父子组件的双向数据流,而保留了组件和UI的数据绑定

React一直不支持双向绑定,提倡的是单向数据流,称之为onChange/setState()模式。不过由于我们一般都会用Vuex以及Redux等单向数据流的状态管理框架,因此很多时候我们感受不到这一点的区别了。

  1. HoC和mixins

Vue组合不同功能的方式是通过mixin,Vue中组件是一个被包装的函数,并不简单的就是我们定义组件的时候传入的对象或者函数。

React组合不同功能的方式是通过HoC(高阶组件)。React最早也是使用mixins的,不过后来他们觉得这种方式对组件侵入太强会导致很多问题,就弃用了mixinx转而使用HoC。高阶组件本质就是高阶函数,React的组件是一个纯粹的函数,所以高阶函数对React来说非常简单。

  1. 组件通信的区别

Vue中有三种方式可以实现组件通信:

  • 父组件通过props向子组件传递数据或者回调,虽然可以传递回调,但是我们一般只传数据;
  • 子组件通过事件向父组件发送消息;
  • 通过V2.2.0中新增的provide/inject来实现父组件向子组件注入数据,可以跨越多个层级。

React中也有对应的三种方式:

父组件通过props可以向子组件传递数据或者回调;可以通过context进行跨层级的通信,这其实和provide/inject起到的作用差不多。React本身并不支持自定义事件,而Vue中子组件向父组件传递消息有两种方式:事件和回调函数,但Vue更倾向于使用事件。在React中我们都是使用回调函数的,这可能是他们二者最大的区别。

  1. 模板渲染方式的不同

在表层上,模板的语法不同,React是通过JSX渲染模板。而Vue是通过一种拓展的HTML语法进行渲染,但其实这只是表面现象,毕竟React并不必须依赖JSX。

在深层上,模板的原理不同,这才是他们的本质区别:React是在组件JS代码中,通过原生JS实现模板中的常见语法,比如插值,条件,循环等,都是通过JS语法实现的,更加纯粹更加原生。而Vue是在和组件JS代码分离的单独的模板中,通过指令来实现的,比如条件语句就需要v-if来实现对这一点,这样的做法显得有些独特,会把HTML弄得很乱。

举个例子,说明React的好处:react中render函数是支持闭包特性的,所以我们import的组件在render中可以直接调用。但是在Vue中,由于模板中使用的数据都必须挂在 this 上进行一次中转,所以我们import 一个组件完了之后,还需要在 components 中再声明下,这样显然是很奇怪但又不得不这样的做法。

  1. 渲染过程不同

Vue可以更快地计算出Virtual DOM的差异,这是由于它在渲染过程中,会跟踪每一个组件的依赖关系,不需要重新渲染整个组件树。

React在应用的状态被改变时,全部子组件都会重新渲染。通过shouldComponentUpdate这个生命周期方法可以进行控制,但Vue将此视为默认的优化。

如果应用中交互复杂,需要处理大量的UI变化,那么使用Virtual DOM是一个好主意。如果更新元素并不频繁,那么Virtual DOM并不一定适用,性能很可能还不如直接操控DOM。

  1. 框架本质不同

Vue本质是MVVM框架,由MVC发展而来;

React是前端组件化框架,由后端组件化发展而来。

  1. Vuex和Redux的区别

从表面上来说,store注入和使用方式有一些区别。在Vuex中, s t o r e 被 直 接 注 入 到 了 组 件 实 例 中 , 因 此 可 以 比 较 灵 活 的 使 用 : 使 用 d i s p a t c h 、 c o m m i t 提 交 更 新 , 通 过 m a p S t a t e 或 者 直 接 通 过 t h i s . store被直接注入到了组件实例中,因此可以比较灵活的使用:使用dispatch、commit提交更新,通过mapState或者直接通过this. store使使dispatchcommitmapStatethis.store来读取数据。在Redux中,我们每一个组件都需要显示的用connect把需要的props和dispatch连接起来。另外,Vuex更加灵活一些,组件中既可以dispatch action,也可以commit updates,而Redux中只能进行dispatch,不能直接调用reducer进行修改。

从实现原理上来说,最大的区别是两点:Redux使用的是不可变数据,而Vuex的数据是可变的,因此,Redux每次都是用新state替换旧state,而Vuex是直接修改。Redux在检测数据变化的时候,是通过diff的方式比较差异的,而Vuex其实和Vue的原理一样,是通过getter/setter来比较的,这两点的区别,也是因为React和Vue的设计理念不同。React更偏向于构建稳定大型的应用,非常的科班化。相比之下,Vue更偏向于简单迅速的解决问题,更灵活,不那么严格遵循条条框框。因此也会给人一种大型项目用React,小型项目用Vue的感觉。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值