2021汇量校招笔试题总结

前言

同班同学实习把我内推进去了…

题目

写下一些比较有意思的题目

打开一个地址到一个网页的过程

这里就把自己的理解写出来,包括

  • DNS的域名到IP的转换(这里说了下本地的DNS缓存,以及host文件,以及DNS的迭代查询和递归查询)
  • HTTP的报文格式(这里说了下报文分为响应和请求,HTTP的请求方法,然后说了点缓存的首部)
  • 三次握手(讲了下发送的报文格式,就是ACK,SYN怎么发过来发过去)
  • 基于TCP/IP的协议栈(这里说了下数据从HTTP层如何一步步下传到物理层)
  • 浏览器的渲染(这里简单的说了下,把文档树和CSSOM树合并为渲染树然后交给浏览器的渲染引擎渲染,js由js引擎进行解析)

写了小2000字吧,可能还是说的太皮毛了…

display: none;visibility: hidden; 的区别,以及适用范围

这里写了下display: none此时不在渲染树里面了

visibility: none只是变得透明了,就像一块100%透明的玻璃放在你眼前,虽然看不见,但是它还是在那里

去网上看了看,发现还有以下不同点,加上opacity: 0一起分析,这个表示不透明度为0,也就是完全透明

  • visibility: hidden具有继承性,子元素如果重新设置visibilityvisible那么还是可以看见的,而display: none没有继承性

上面我给header设置了visibility: none并且给header的子元素h1设置了visibility: visibleh1还是可以看见,并且此时标题下面的信息栏依然占据着原来的位置,并没有出现坍塌,
post-meta还是占据着布局的部分,而display: none并没有继承属性,并且坍塌了,见下图

编写一个avg请求平均值的函数

1
2
3
4
5
输入
avg(1, 2, 3, 4, 5); // 3
avg(5, null, -5); // 0
avg('1.0', false, 1, true, 1, 'A', 1, 'B', 1, 'C'); // 输出0.5
avg(0.1, 0.2); // 0.15, 而不是0.15000000000002

emmm 这题前三个还好,是一些判断而已

但是第四个我只是知道这种情况,因为只要是使用了IEEE754计数的都会有这种情况

可以输出0.1和0.2的二进制来看看

1
2
console.log((0.1).toString(2));
console.log((0.2).toString(2));

发现由于IEEE754储存格式的问题,0.1和0.2并不能完全的表示,是被截断过的

这可以访问下面网站来了解

IEEE754中使用了64位双精度来存数字,11个数字位(指数域),52个指数位(尾数域),其中1个符号位,

计算公式如图所示

这题我傻逼的尽然想用新特性大数去解决,但是我只是简单使用过而已,并没有了解透彻,导致写了个通不过的代码
并不知道大数只能用于整数😂😂😂😂

1
2
3
4
5
6
7
8
function avg(...args) {
const toString = Object.prototype.toString;
args = args.filter(item => item !== null || item !== undefined) // 从第二条式子看出null应该算进元素的个数中,所以要过滤掉(顺便也把undefined,虽然没有提到)
.map(item => toString.call(item) === '[object String]' ? 0 : item) // 从第三条式子看出字符串应该看成0
.map(item => BigInt(item)); // 这个我是想针对第四条的,但是没想到装了个大逼,没装成😂😂😂😂
const sum = args.reduce((pre, val) => pre + val, 0);
return sum / BigInt(args.length);
}

大数不支持小数,通过转换会报错

所以我答的代码,完全是错误的…

这几天想了想,也查了查网上的解决办法,大部分都是通过两数相减,然后小于Number.EPSILON这个值,就当作两者相等

但是这个逻辑不具有普适性,可以看下面的代码

1
2
3
4
5
6
function eq(n1, n2) {
return Math.abs(n1 - n2) <= Number.EPSILON;
}

console.log(eq(0.1 + 0.2, 0.3)); // true
console.log(eq(1.1 + 2.2, 3.3)); // false

搜了很久,折中的方法是先放大数字,然后计算完再缩小,只有这个方法比较靠谱

1
2
3
4
5
6
7
8
function avg(...args) {
args = args.filter(item => item !== null || item !== undefined)
.map(item => toString.call(item) === '[object String]' ? 0 : item)
.map(item => Number(item))
.map(item => item * 10000); // 放大10000倍
const sum = args.reduce((pre, cur) => pre + val, 0);
return sum / args.length / 10000; // 缩小10000倍
}

测试下

很好的解决了题目的问题,面向答案编程,哈哈哈哈😂😂😂

当然,这种方法也没有普适性,

如果放大之后依然出现了像 .1 + .2 依然会出现计算误差

如果是项目上的话,可以使用Mathjs这个库,这个库支持强大的计算表达式

项目地址:josdejong/mathjs

样例代码,来自项目的README

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import {
atan2, chain, derivative, e, evaluate, log, pi, pow, round, sqrt
} from 'mathjs'

// functions and constants
round(e, 3) // 2.718
atan2(3, -3) / pi // 0.75
log(10000, 10) // 4
sqrt(-4) // 2i
pow([[-1, 2], [3, 1]], 2) // [[7, 0], [0, 7]]
derivative('x^2 + x', 'x') // 2 * x + 1

// expressions
evaluate('12 / (2.3 + 0.7)') // 4
evaluate('12.7 cm to inch') // 5 inch
evaluate('sin(45 deg) ^ 2') // 0.5
evaluate('9 / 3 + 2i') // 3 + 2i
evaluate('det([-1, 2; 3, 1])') // -7

// chaining
chain(3)
.add(4)
.multiply(2)
.done() // 14

查资料的时候还看到了JS关于Decimal的提案,这个可以看

tc39/proposal-decimal

感觉很棒,通过使用特定的后缀m来标明(大数是n),然后可以进行安全的计算

1
2
3
4
5
6
7
8
9
10
11
12
function calculateBill(items, tax) {
let total = 0m;
for (let {price, count} of items) {
total += price * BigDecimal(count);
}
return BigDecimal.round(total * (1m + tax),
{maximumFractionDigits: 2, round: "up"});
}

let items = [{price: 1.25m, count: 5}, {price: 5m, count: 1}];
let tax = .0735m;
console.log(calculateBill(items, tax));

目前处于提案1,感觉不用很久就能使用到和大数相似的特性了

JS一定会越来越好的~~

后记

多搞搞面试,积累面试题目