今日进行了字节的视频面试, 个人感觉面试官真的是往 si 里问, 抓住一个点, 各种深挖. 这里将遇到的几个题做个总结…
更新
[2019-10-30]
- Initial Release
[2019-10-31]
Added
- 解答问题 String(‘’) 和 new String(‘’) 的区别?
- 解答问题 基本类型和基本包装类型的区别?
- 解答问题 ES6 的 Set?
[2019-11-1]
Added
- 解答问题 Map的键是怎么实现的?
- 解答问题 哈希是什么?
- 解答问题 HTTP静态资源的优化方式?
- 解答问题 浏览器缓存机制?
[2019-11-8]
Changed
- 更新文章所属标签
目录
- 1. 后端返回一个长整型, 问前端如何处理, 才能让精度不丢失?
- 2. 数组去重, 写了 Set, 又问你 Set 是什么, 怎么实现 Set?
- 3. new 做了什么事情, 实现 new?
- 4. 构造函数有 return, 会出现什么情况?
- 5. HTTP 静态资源的优化方式?
- 6. 浏览器缓存机制?
- 7. 用 CSS 画一个菱形?
- 8. String(‘’) 和 new String(‘’) 的区别?
- 9. ES6 的 Set?
- 10. Map 的键是怎么实现的?
- 11. 哈希是什么?
- 12. 基本类型和基本包装类型的区别?
问题
1. 后端返回一个长整型, 问前端如何处理, 才能让精度不丢失?
为什么会发生精度丢失?
JS
所有的数字, 在底层都是以双精度浮点型
的形式存储的, 那么:
何谓双精度浮点型?
占用8
个字节, 也就是64
个比特位, 大致结构可表示为:
1 | | 1 个符号位 | 11 个指数位 | 52 个尾数位 | |
因此, JS
所能承受的最大整数为2 ^ 53 - 1
, 也可以通过Number.MAX_SAFE_INTEGER
得到, 故后端返回的长整型如果大于该极限, 则会出现精度丢失
怎么解决精度丢失?
- 将
长整型
的数值转化为字符串
参考
2. 数组去重, 写了 Set, 又问你 Set 是什么, 怎么实现 Set?
indexOf 去重
1 | function uniqueArrByIndexOf(arr) { |
Set 去重
1 | function uniqueArrBySet(arr) { |
3. new 做了什么事情, 实现 new?
4. 构造函数有 return, 会出现什么情况?
说实话, 这个问题以前倒真没有注意过, 可以通过几个在线示例了解一下:
可以看到, 构造函数中:
- 如果返回
基本类型
的值, 那么正常创建实例
并返回 - 反之, 如果返回
引用类型
的值, 则会接收到该引用
值, 不会创建实例
5. HTTP 静态资源的优化方式?
合并
对于大量的图片文件, 使用雪碧图
代替, 减少HTTP
请求的数量, 但是使用HTTP/2
可以避免这个优化
HTTP/2
提出了多路复用
的特性, 同域名下的HTTP
请求都在一个TCP
连接下进行
压缩
对于JS
、CSS
文件, 使用Webpack
等工具进行打包压缩, 减小文件的体积
浏览器缓存
(1). 缓存位置
- Service Worker
- Memory Cache
- 存储于内存中, 页面关闭时释放
- Disk Cache
- 存储于磁盘中, 永久存储, 浏览器缓存服务器其实存的是文件在
磁盘
或内存
的位置
- 存储于磁盘中, 永久存储, 浏览器缓存服务器其实存的是文件在
- Server Push
HTTP/2
的推送机制
(2). 缓存策略
- 强缓存
- Expires(
HTTP/1.0
) - Cache-Control(
HTTP/1.1
)
- Expires(
- 协商缓存
- Last-Modified + If-Modified-Since(
HTTP/1.0
) - ETag + If-None-Match(
HTTP/1.1
)
- Last-Modified + If-Modified-Since(
CDN
内容分发网络
权威DNS
服务器将域名的解析权交给CNAME
指向的CDN
的解析系统, CDN
返回离客户机最近的节点的IP
地址, 本质上CDN
也是一层缓存
6. 浏览器缓存机制?
缓存位置与缓存策略可查看: 浏览器缓存
浏览器缓存的大概过程:
- 首次访问
Server
, 会在Response Header
添加Cache-Control
、ETag
字段, 浏览器接收到相关资源, 会在缓存服务器拷贝一份资源 - 后续访问资源, 首先检查是否命中
强缓存
, 如果有,缓存服务器
直接返回200
状态码和对应资源 - 反之,
缓存服务器
携带If-None-Match
字段向源服务器
发起请求, 如果命中协商缓存
, 直接返回304
状态码, 反之返回200
状态码和更新后的资源到缓存服务器
,缓存服务器
将对应的资源返回给Client
7. 用 CSS 画一个菱形?
我最先想到的是通过给上下两个堆叠的矩形分别设置border
, 代码如下:
但是, 面试完思考了一下, 发现其实自己想多了, 事实上, 只要将正方形
旋转即可, 代码如下:
8. String(‘’) 和 new String(‘’) 的区别?
1 | String(''); // => '' |
String('')
返回一个基本类型字符串, 相当于调用''.toString()
方法new String('')
则返回字符串的基本包装类型的对象, 是String
基本包装类型的实例
9. ES6 的 Set?
是什么
Set
是ES2015
提出的一个新的数据结构, 与数组类似, 但是内部的元素都是不重复的, 接受一个数组作为构造函数的参数, 并且可以通过set.add()
来添加条目
使用场景
- 数组去重
1 | function uniqueArrayBySet(arr) { |
10. Map 的键是怎么实现的?
Map
与Set
一样, 也是ES2015
推出的一个新的数据结构(散列表
), 类似于对象(字典
), 也是键值对的集合. 但是其与普通对象不同的是, 它的键可以是任意值, 包括但不仅仅局限于字符串
、数字
. 可以看个简单的例子:
其实, Map
内部维护了了一个散列表
数组, 将传入的key
通过哈希函数
, 生成一个特定的数字, 在散列表
的对应位置存储value
, 代码如下:
1 | function generateHashCode(key) { |
11. 哈希是什么?
Hash 原意为
散列
, 音译过来被称作哈希
在记录的关键字
与记录的存储地址
之间建立的一种对应关系叫哈希函数
用人话来说, 就是根据一系列特征, 计算出的一个特定的标识符
12. 基本类型和基本包装类型的区别?
基本类型:
1 | let a = 'duanzhaoyang'; |
基本包装类型:
1 | let d = new String('duanzhaoyang'); // => String{ 'duanzhaoyang' } |
使用typeof
操作符:
1 | typeof a // => 'string' |
可以明显地看到, 基本类型的值的类型都可以通过typeof
明显地区分, 而基本包装类型的值则都为对象
, 由此可以得出一个结论:
- 基本包装类型是一种特殊的
引用
类型
为什么基本类型的值可以调用方法
按理说, 基本类型值不是对象, 应该不能调用方法, 这也是我比较好奇的:
1 | let str = 'yanggege'; |
事实上, JS 引擎内部会进行如下操作:
1 | let str = new String('yanggege'); |
基本包装类型和引用类型的区别
主要的区别在于生命周期
, 基本包装类型
的值会在执行完本行代码后被自动销毁, 而引用类型
的值则会在执行流
走完当前作用域之后才被销毁