第 61 题:介绍下如何实现 token 加密
题目讨论
jwt举例
需要一个secret(随机数)
后端利用secret和加密算法(如:HMAC-SHA256)对payload(如账号密码)生成一个字符串(token),返回前端
前端每次request在header中带上token
后端用同样的算法解密
第 62 题:redux 为什么要把 reducer 设计成纯函数
题目讨论
redux三大原则
单一数据流
整个应用state都被储存在一个store里面 构成一个Object tree
State是只读的
唯一改变state的方法就是触发action, action是一个用于描述已发生事件的普通对象
使用纯函数来执行修改
为了描述action如何改变state tree, 你需要编写reducers
把reducer设计成纯函数,可以实现时间旅行,记录/回放或者热加载
第 63 题:如何设计实现无缝轮播
题目讨论
这里说一个不需要clone的方案:
<div class="slide">
<ul>
<li>图片1</li>
<li>图片2</li>
<li>图片3</li>
</ul>
</div>
1、最外层div.slide定宽、相对定位relative
2、ul 足够宽,最起码li数*li宽度,这里有个技巧,直接 width: 9999em,目的是让里面的所有li一字排开
3、滚动效果通过控制ul的left或者transform来进行滚动效果
4、到了最后一个li,往后看第一个li的时:
- 4.1、准备继续滚动,把最后一个的li设置为相对定位relative,left值为此时此刻相对ul的位置(设置的时候不要带缓动效果),目的是让最后一个li不动。
- 4.2、然后把ul的left或者transform设为0(这步没有缓动效果),
- 4.3、然后再正常的开始一样出现第一个li的滚动效果(这步有缓动效果)
- 4.4、最后等无缝的第一个li效果完成后,把最后一个li的left值复原为0
5、到第一个li,往前看最后一个li时,也是和上面同理
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>轮播</title>
<style>
*{
padding: 0;
margin: 0;
list-style: none;
}
#continer{
width: 300px;
height: 200px;
position: relative;
margin: 20px auto;
border: 1px solid;
overflow: hidden;
}
#lunbo{
width: 9999em;
height: 100%;
position: absolute;
top: 0;
left: 0;
}
li{
float: left;
width: 300px;
height: 200px;
text-align: center;
line-height: 200px;
color: brown;
font-size: 30px;
}
</style>
</head>
<body>
<div id="continer">
<ul class="ul" id="lunbo">
<li class="list">1</li>
<li class="list">2</li>
<li class="list">3</li>
<li class="list">4</li>
<li class="list">5</li>
</ul>
</div>
</body>
<script src="./js/jquery3.0.min.js"></script>
<script>
let selectNum = 0
function lunboFun (selectNum, time) {
$('#lunbo').animate({
'left': -1 * selectNum * 300
}, time, () => {
selectNum++
setTimeout(() => {
if (selectNum > 4) {
selectNum = 0
$('ul li:last').css({
'position': 'absolute'
}, 0)
$('ul li:last').animate({
'left': -300
}, 0)
$('#lunbo').animate({
'left': 300
}, 0)
} else if (selectNum <3) {
$('ul li:last').css({
'position': 'relative',
'left': 0
}, 0)
}
lunboFun(selectNum, 1000)
}, 2000)
});
}
lunboFun(selectNum, 1000)
</script>
</html>
第 64 题:模拟实现一个 Promise.finally
Promise.prototype.finally = function (callback) {
let P = this.constructor;
return this.then(
value => P.resolve(callback()).then(() => value),
reason => P.resolve(callback()).then(() => { throw reason })
);
};
第 65 题: a.b.c.d 和 a[‘b’][‘c’][‘d’],哪个性能更高?
题目讨论
应该是 a.b.c.d 比 a[‘b’][‘c’][‘d’] 性能高点,后者还要考虑 [ ] 中是变量的情况,再者,从两种形式的结构来看,显然编译器解析前者要比后者容易些,自然也就快一点。
下图是两者的 AST 对比:
第 66 题:ES6 代码转成 ES5 代码的实现思路是什么
题目讨论
把 ES6 代码转成 ES5 代码的实现思路可以分成三步:
打开冰箱
把大象装进去
关上冰箱
喔,不对,原谅我开了个玩笑,嗯,有点冷……
将ES6的代码转换为AST语法树,然后再将ES6 AST转为ES5 AST,再将AST转为代码
回到正题上来,说到 ES6 代码转成 ES5 代码,我们肯定会想到 Babel。所以,我们可以参考 Babel 的实现方式。
那么 Babel 是如何把 ES6 转成 ES5 呢,其大致分为三步:
将代码字符串解析成抽象语法树,即所谓的 AST
对 AST 进行处理,在这个阶段可以对 ES6 代码进行相应转换,即转成 ES5 代码
根据处理后的 AST 再生成代码字符串
基于此,其实我们自己就可以实现一个简单的“编译器”,用于把 ES6 代码转成 ES5。
比如,可以使用 @babel/parser 的 parse 方法,将代码字符串解析成 AST;使用 @babel/core 的 transformFromAstSync 方法,对 AST 进行处理,将其转成 ES5 并生成相应的代码字符串;过程中,可能还需要使用 @babel/traverse 来获取依赖文件等。
第 67 题:数组编程题
随机生成一个长度为 10 的整数类型的数组,例如 [2, 10, 3, 4, 5, 11, 10, 11, 20],将其排列成一个新数组,要求新数组形式如下,例如 [[2, 3, 4, 5], [10, 11], [20]]。
// 得到一个两数之间的随机整数,包括两个数在内
function getRandomIntInclusive(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min; //含最大值,含最小值
}
// 随机生成10个整数数组, 排序, 去重
let initArr = Array.from({ length: 10 }, (v) => { return getRandomIntInclusive(0, 99) });
initArr.sort((a,b) => { return a - b });
initArr = [...(new Set(initArr))];
// 放入hash表
let obj = {};
initArr.map((i) => {
const intNum = Math.floor(i/10);
if (!obj[intNum]) obj[intNum] = [];
obj[intNum].push(i);
})
// 输出结果
const resArr = [];
for(let i in obj) {
resArr.push(obj[i]);
}
console.log(resArr);
第 68 题: 如何解决移动端 Retina 屏 1px 像素问题
题目讨论
造成边框变粗的原因
其实这个原因很简单,因为css中的1px并不等于移动设备的1px,这些由于不同的手机有不同的像素密度。在window对象中有一个devicePixelRatio属性,他可以反应css中的像素与设备的像素比。
devicePixelRatio的官方的定义为:设备物理像素和设备独立像素的比例,也就是 devicePixelRatio = 物理像素 / 独立像素。
第 69 题: 如何把一个字符串的大小写取反(大写变小写小写变大写),例如 ’AbC’ 变成 ‘aBc’ 。
function processString (s) {
var arr = s.split('');
var new_arr = arr.map((item) => {
return item === item.toUpperCase() ? item.toLowerCase() : item.toUpperCase();
});
return new_arr.join('');
}
console.log(processString('AbC'));
第 70 题: 介绍下 webpack 热更新原理,是如何做到在不刷新浏览器的前提下更新页面的
题目讨论
1.当修改了一个或多个文件;
2.文件系统接收更改并通知webpack;
3.webpack重新编译构建一个或多个模块,并通知HMR服务器进行更新;
4.HMR Server 使用webSocket通知HMR runtime 需要更新,HMR运行时通过HTTP请求更新jsonp;
5.HMR运行时替换更新中的模块,如果确定这些模块无法更新,则触发整个页面刷新。
简单来说就是:hot-module-replacement-plugin 包给 webpack-dev-server 提供了热更新的能力,它们两者是结合使用的,单独写两个包也是出于功能的解耦来考虑的。
1)webpack-dev-server(WDS)的功能提供 bundle server的能力,就是生成的 bundle.js 文件可以通过 localhost://xxx 的方式去访问,另外 WDS 也提供 livereload(浏览器的自动刷新)。
2)hot-module-replacement-plugin 的作用是提供 HMR 的 runtime,并且将 runtime 注入到 bundle.js 代码里面去。一旦磁盘里面的文件修改,那么 HMR server 会将有修改的 js module 信息发送给 HMR runtime,然后 HMR runtime 去局部更新页面的代码。因此这种方式可以不用刷新浏览器。