目录
- 快速排序
- 你怎么带人的
- 说一下从url输入到返回请求的过程
- let、var、const的区别
- 深拷贝的实现
- cookie和storage的区别
- compose函数的实现
- 性能优化
- 统计字符串个数
- 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时,会进行一下操作:
- 操作系统会首先在本地缓存中查询IP
- 没有的话会去系统配置的DNS服务器中查询
- 如果这时候还没得话,会直接去DNS根服务器查询,这一步查询会找出负责com这个一级域名的服务器
- 然后去该服务器查询baidu这个二级域名
- 接下来三级域名的查询其实是我们配置的,你可以给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的区别
- let和const声明的变量不存在变量提升,如果要使用这个变量,我们需要在变量定义之后使用;
- let和const不能重复声明变量,如果重复声明会报错;
- 用let和const在全局声明变量不会给window增加属性;
- 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. 性能优化
减少请求资源大小或者次数
- 尽量和并和压缩css和js文件。(将css文件和并为一个。将js合并为一个)
- 原因:主要是为了减少http请求次数以及减少请求资源的大小
- 打包工具:webpack、gulp、grunt
- 尽量所使用的字体图标或者SVG图标来代替传统png图
- 因为字体图标或者SVG是矢量图,代码编写出来的,方大不会变形,而且渲染速度快
- 采用图片的懒加载(延迟加载)
- 目的为了,减少页面第一次加载过程中http的请求次数
- 页面开始加载时不去发送http请求,而是放置一张占位图
- 当页面加载完时,并且图片在可视区域再去请求加载图片信息
- 能用css做的效果,不要用js做,能用原生js做的,不要轻易去使用第三方插件
- 避免引入第三方大量的库。而自己却只是用里面的一个小功能
- 使用雪碧图或者是说图片精灵
- 把所有相对较小的资源图片,绘制在一张大图上,只需要将大图下载下来,然后利用
- 图片定位来讲小图展现在页面中(background-position:百分比,数值)
- 减少对cookie的使用(最主要的就是减少本地cookie存储内容的大小),因为客户端操作cookie的时候,这些信息总是在客户端和服务端传递。如果上设置不当,每次发送一个请求将会携带cookie
- 前端与后端进行数据交互时,对于多项数据尽可能基于json格式来进行传送。相对于使用xml来说传输有这个优势
- 目的:是数据处理方便,资源偏小
- 前端与后端协商,合理使用keep-alive
- 前端与服务器协商,使用响应资源的压缩
- 避免使用iframe
- 不仅不好管控样式,而且相当于在本页面又嵌套其他页面,消耗性能会更大。因为还回去加载这个嵌套页面的资源
- 在基于ajax的get请求进行数据交互的时候,根据需求可以让其产生缓存(注意:这个缓存不是我们常看到的304状态码,去浏览器本地取数据),这样在下一次从相同地址获取是数据时,取得就是上一次缓存的数据。(注意:很少使用,一般都会清空。根据需求来做)
代码优化相关
- 在js中尽量减少闭包的使用
- 原因:使用闭包后,闭包所在的上下文不会被释放
- 减少对DOM操作,主要是减少DOM的重绘与回流(重排)
- 关于重排(回流)的分离读写:如果需要设置多个样式,把设置样式全放在一起设置,不要一条一条的设置。使用文档碎片或者字符串拼接做数据绑定(DOM的动态创建)
- 在js中避免嵌套循环和"死循环"(一旦遇到死循环,浏览器就会直接卡掉)
- 把css放在body上,把js放在body下面
- 让其先加载css(注意:这里关于优化没有多大关系)
- 减少css表达式的使用
- css选择器解析规则所示从右往左解析的。减少元素标签作为对后一个选择对象
- 尽量将一个动画元素单独设置为一个图层(避免重绘或者回流的大小)
- 注意:图层不要过多设置,否则不但效果没有达到反而更差了
- 在js封装过程中,尽量做到低耦合高内聚。减少页面的冗余代码
- css中设置定位后,最好使用z-index改变盒子的层级,让盒子不在相同的平面上
- css导入的时候尽量减少@import导入式,因为@import是同步操作,只有把对应的样式导入后,才会继续向下加兹安,而link是异步的操作
- 使用window.requestAnimationFrame(js的帧动画)代替传统的定时器动画
- 如果想使用每隔一段时间执行动画,应该避免使用setInterval,尽量使用setTimeout
- 代替setInterval定时器。因为setInterval定时器存在弊端:可能造成两个动画间隔时间缩短
- 尽量减少使用递归。避免死递归
- 解决:建议使用尾递归
- 基于script标签下载js文件时,可以使用defer或者async来异步加载
- 在事件绑定中,尽可能使用事件委托,减少循环给DOM元素绑定事件处理函数。
- 在js封装过程中,尽量做到低耦合高内聚。减少页面的冗余代码
- 减少Flash的使用
存储
- 结合后端,利用浏览器的缓存技术,做一些缓存(让后端返回304,告诉浏览器去本地拉取数据)。(注意:也有弊端)可以让一些不太会改变的静态资源做缓存。比如:一些图片,js,cs
- 利用h5的新特性(localStorage、sessionStorage)做一些简单数据的存储,
- 避免向后台请求数据或者说在离线状态下做一些数据展示。
其他优化
- 避免使用iframe不仅不好管控样式,而且相当于在本页面又嵌套其他页面,消耗性能会更大。因为还回去加载这个嵌套页面的资源
- 页面中的是数据获取采用异步编程和延迟分批加载,使用异步加载是数据主要是为了避免浏览器失去响应。如果你使用同步,加载数据很大并且很慢
- 那么,页面会在一段时间内处于阻塞状态。目的:为了解决请求数据不耽搁渲染,提高页面的渲染效率。
- 解决方法:需要动态绑定的是数据区域先隐藏,等数据返回并且绑定后在让其显示延迟分批加载类似图片懒加载。减少第一次页面加载时候的http请求次数
- 页面中出现音视频标签,我们不让页面加载的时候去加载这些资源(否则第一次加载会很慢)
- 解决方法:只需要将音视频的preload=none即可。
- 目的:为了等待页面加载完成时,并且音视频要播放的时候去加兹安音视频资源
- 尽量将一个动画元素单独设置为一个图层(避免重绘或者回流的大小)
- 注意:图层不要过多设置,否则不但效果没有达到反而更差了
- 服务端渲染
- CDN技术
- 骨架屏技术
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的区别
- 数据监听实现原理不同:
Vue通过getter/setter以及一些函数的劫持,能精确知道数据变化。
React默认是通过比较引用的方式(diff)进行的,如果不优化可能导致大量不必要的VDOM的重新渲染。为什么React不精确监听数据变化呢?这是因为Vue和React设计理念上的区别,Vue使用的是可变数据,而React更强调数据的不可变,两者没有好坏之分,Vue更加简单,而React构建大型应用的时候更棒。
- 数据流的不同
vue 2.x开始进制父子组件的双向数据流,而保留了组件和UI的数据绑定
React一直不支持双向绑定,提倡的是单向数据流,称之为onChange/setState()模式。不过由于我们一般都会用Vuex以及Redux等单向数据流的状态管理框架,因此很多时候我们感受不到这一点的区别了。
- HoC和mixins
Vue组合不同功能的方式是通过mixin,Vue中组件是一个被包装的函数,并不简单的就是我们定义组件的时候传入的对象或者函数。
React组合不同功能的方式是通过HoC(高阶组件)。React最早也是使用mixins的,不过后来他们觉得这种方式对组件侵入太强会导致很多问题,就弃用了mixinx转而使用HoC。高阶组件本质就是高阶函数,React的组件是一个纯粹的函数,所以高阶函数对React来说非常简单。
- 组件通信的区别
Vue中有三种方式可以实现组件通信:
- 父组件通过props向子组件传递数据或者回调,虽然可以传递回调,但是我们一般只传数据;
- 子组件通过事件向父组件发送消息;
- 通过V2.2.0中新增的provide/inject来实现父组件向子组件注入数据,可以跨越多个层级。
React中也有对应的三种方式:
父组件通过props可以向子组件传递数据或者回调;可以通过context进行跨层级的通信,这其实和provide/inject起到的作用差不多。React本身并不支持自定义事件,而Vue中子组件向父组件传递消息有两种方式:事件和回调函数,但Vue更倾向于使用事件。在React中我们都是使用回调函数的,这可能是他们二者最大的区别。
- 模板渲染方式的不同
在表层上,模板的语法不同,React是通过JSX渲染模板。而Vue是通过一种拓展的HTML语法进行渲染,但其实这只是表面现象,毕竟React并不必须依赖JSX。
在深层上,模板的原理不同,这才是他们的本质区别:React是在组件JS代码中,通过原生JS实现模板中的常见语法,比如插值,条件,循环等,都是通过JS语法实现的,更加纯粹更加原生。而Vue是在和组件JS代码分离的单独的模板中,通过指令来实现的,比如条件语句就需要v-if来实现对这一点,这样的做法显得有些独特,会把HTML弄得很乱。
举个例子,说明React的好处:react中render函数是支持闭包特性的,所以我们import的组件在render中可以直接调用。但是在Vue中,由于模板中使用的数据都必须挂在 this 上进行一次中转,所以我们import 一个组件完了之后,还需要在 components 中再声明下,这样显然是很奇怪但又不得不这样的做法。
- 渲染过程不同
Vue可以更快地计算出Virtual DOM的差异,这是由于它在渲染过程中,会跟踪每一个组件的依赖关系,不需要重新渲染整个组件树。
React在应用的状态被改变时,全部子组件都会重新渲染。通过shouldComponentUpdate这个生命周期方法可以进行控制,但Vue将此视为默认的优化。
如果应用中交互复杂,需要处理大量的UI变化,那么使用Virtual DOM是一个好主意。如果更新元素并不频繁,那么Virtual DOM并不一定适用,性能很可能还不如直接操控DOM。
- 框架本质不同
Vue本质是MVVM框架,由MVC发展而来;
React是前端组件化框架,由后端组件化发展而来。
- 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被直接注入到了组件实例中,因此可以比较灵活的使用:使用dispatch、commit提交更新,通过mapState或者直接通过this.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的感觉。