今日进行了字节的视频面试, 个人感觉面试官真的是往 si 里问, 抓住一个点, 各种深挖. 这里将遇到的几个题做个总结…

更新


[2019-10-30]

  • Initial Release

[2019-10-31]

Added

[2019-11-1]

Added

[2019-11-8]

Changed

  • 更新文章所属标签

目录


问题


1. 后端返回一个长整型, 问前端如何处理, 才能让精度不丢失?

为什么会发生精度丢失?

JS所有的数字, 在底层都是以双精度浮点型的形式存储的, 那么:

何谓双精度浮点型?

占用8个字节, 也就是64个比特位, 大致结构可表示为:

1
| 1 个符号位 | 11 个指数位 | 52 个尾数位 |

因此, JS所能承受的最大整数为2 ^ 53 - 1, 也可以通过Number.MAX_SAFE_INTEGER得到, 故后端返回的长整型如果大于该极限, 则会出现精度丢失

怎么解决精度丢失?

  • 长整型的数值转化为字符串

参考

2. 数组去重, 写了 Set, 又问你 Set 是什么, 怎么实现 Set?

indexOf 去重

1
2
3
4
5
6
7
8
9
10
11
function uniqueArrByIndexOf(arr) {
const result = [];

arr.forEach((v) => {
if(result.indexOf(v) === -1) {
result.push(v);
}
});

return result;
}

Set 去重

1
2
3
function uniqueArrBySet(arr) {
return [...new Set(arr)];
}

3. new 做了什么事情, 实现 new?

4. 构造函数有 return, 会出现什么情况?

说实话, 这个问题以前倒真没有注意过, 可以通过几个在线示例了解一下:

可以看到, 构造函数中:

  • 如果返回基本类型的值, 那么正常创建实例并返回
  • 反之, 如果返回引用类型的值, 则会接收到该引用值, 不会创建实例

5. HTTP 静态资源的优化方式?

合并

对于大量的图片文件, 使用雪碧图代替, 减少HTTP请求的数量, 但是使用HTTP/2可以避免这个优化

HTTP/2提出了多路复用的特性, 同域名下的HTTP请求都在一个TCP连接下进行

压缩

对于JSCSS文件, 使用Webpack等工具进行打包压缩, 减小文件的体积

浏览器缓存

(1). 缓存位置

  • Service Worker
  • Memory Cache
    • 存储于内存中, 页面关闭时释放
  • Disk Cache
    • 存储于磁盘中, 永久存储, 浏览器缓存服务器其实存的是文件在磁盘内存的位置
  • Server Push
    • HTTP/2的推送机制

(2). 缓存策略

  • 强缓存
    • Expires(HTTP/1.0)
    • Cache-Control(HTTP/1.1)
  • 协商缓存
    • Last-Modified + If-Modified-Since(HTTP/1.0)
    • ETag + If-None-Match(HTTP/1.1)

CDN

内容分发网络

权威DNS服务器将域名的解析权交给CNAME指向的CDN的解析系统, CDN返回离客户机最近的节点的IP地址, 本质上CDN也是一层缓存

6. 浏览器缓存机制?

缓存位置与缓存策略可查看: 浏览器缓存

浏览器缓存的大概过程:

  • 首次访问Server, 会在Response Header添加Cache-ControlETag字段, 浏览器接收到相关资源, 会在缓存服务器拷贝一份资源
  • 后续访问资源, 首先检查是否命中强缓存, 如果有, 缓存服务器直接返回200状态码和对应资源
  • 反之, 缓存服务器携带If-None-Match字段向源服务器发起请求, 如果命中协商缓存, 直接返回304状态码, 反之返回200状态码和更新后的资源到缓存服务器, 缓存服务器将对应的资源返回给Client

7. 用 CSS 画一个菱形?

我最先想到的是通过给上下两个堆叠的矩形分别设置border, 代码如下:

但是, 面试完思考了一下, 发现其实自己想多了, 事实上, 只要将正方形旋转即可, 代码如下:

8. String(‘’) 和 new String(‘’) 的区别?

1
2
3
String(''); // => ''

new String(''); // => String{}
  • String('')返回一个基本类型字符串, 相当于调用''.toString()方法
  • new String('')则返回字符串的基本包装类型的对象, 是String基本包装类型的实例

9. ES6 的 Set?

是什么

SetES2015提出的一个新的数据结构, 与数组类似, 但是内部的元素都是不重复的, 接受一个数组作为构造函数的参数, 并且可以通过set.add()来添加条目

使用场景

  • 数组去重
1
2
3
function uniqueArrayBySet(arr) {
return [...new Set(arr)];
}

10. Map 的键是怎么实现的?

MapSet一样, 也是ES2015推出的一个新的数据结构(散列表), 类似于对象(字典), 也是键值对的集合. 但是其与普通对象不同的是, 它的键可以是任意值, 包括但不仅仅局限于字符串数字. 可以看个简单的例子:

其实, Map内部维护了了一个散列表数组, 将传入的key通过哈希函数, 生成一个特定的数字, 在散列表的对应位置存储value, 代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function generateHashCode(key) {
let hashCode = 0;

// 字符串
if(typeof key === 'string') {
for(let i = 0; i < key.length; i++>) {
hashCode += i.charCodeAt();
}

return hashCode % 37;
}

// 对象
if(typeof key === 'object') {
...
}
}

11. 哈希是什么?

Hash 原意为散列, 音译过来被称作哈希

在记录的关键字与记录的存储地址之间建立的一种对应关系叫哈希函数

用人话来说, 就是根据一系列特征, 计算出的一个特定的标识符

12. 基本类型和基本包装类型的区别?

基本类型:

1
2
3
let a = 'duanzhaoyang';
let b = 23235.2309534;
let c = false;

基本包装类型:

1
2
3
let d = new String('duanzhaoyang'); // => String{ 'duanzhaoyang' }
let e = new Number(23235.2309534); // => Number{ 23235.2309534 }
let f = new Boolean(false); // => Boolean{ false }

使用typeof操作符:

1
2
3
4
5
6
7
typeof a // => 'string'
typeof b // => 'number'
typeof c // => 'boolean'

typeof d // => 'object'
typeof e // => 'object'
typeof f // => 'object'

可以明显地看到, 基本类型的值的类型都可以通过typeof明显地区分, 而基本包装类型的值则都为对象, 由此可以得出一个结论:

  • 基本包装类型是一种特殊的引用类型

为什么基本类型的值可以调用方法

按理说, 基本类型值不是对象, 应该不能调用方法, 这也是我比较好奇的:

1
2
3
let str = 'yanggege';

console.log(str.split(''));

事实上, JS 引擎内部会进行如下操作:

1
2
3
4
5
6
let str = new String('yanggege');

console.log(str.split(''));

// 自动销毁
str = null;

基本包装类型和引用类型的区别

主要的区别在于生命周期, 基本包装类型的值会在执行完本行代码后被自动销毁, 而引用类型的值则会在执行流走完当前作用域之后才被销毁

参考