# 1.JS 数字精读丢失问题
参考链接:[https://www.cnblogs.com/snandy/p/4943138.html](https://www.cnblogs.com/snandy/p/4943138.html)
## 常见的数字精读丢失的情况
1.两个浮点数相加
```js
console.log(0.1 + 0.2 !== 0.3) // true
console.log(0.1 + 0.2) // 0.30000000000000004
```
2.大整数运算(多少位?)
```js
console.log(9999999999999999 === 10000000000000001) // true
var x = 9007199254740992
console.log(x + 1 === x) // true
```
3.toFixed() 没有四舍五入,此外 toFixed 还有一些兼容性问题
```js
console.log(1.335.toFixed(2)) // 1.33
```
## 原因
JS 遵循[IEEE 754](https://en.wikipedia.org/wiki/IEEE_floating_point)规范,采用双精度存储(double precision),占用 64 bit。如图
![](https://box.kancloud.cn/20cfb7d2c57d2aa24f53aeb9865a1ab6_1098x78.png)
- 1位用来表示符号位
- 11位用来表示指数
- 52位表示尾数
浮点数,比如
```js
0.1 >> 0.0001 1001 1001 1001…(1001无限循环)
0.2 >> 0.0011 0011 0011 0011…(0011无限循环)
```
此时只能模仿十进制进行四舍五入了,但是二进制只有 0 和 1 两个,于是变为 0 舍 1 入。这即是计算机中部分浮点数运算时出现误差,丢失精度的根本原因。
大整数的精度丢失和浮点数本质上是一样的,尾数位最大是 52 位,因此 JS 中能精准表示的最大整数是 Math.pow(2, 53),十进制即 9007199254740992。
大于 9007199254740992 的可能会丢失精度
```js
9007199254740992 >> 10000000000000...000 // 共计 53 个 0
9007199254740992 + 1 >> 10000000000000...001 // 中间 52 个 0
9007199254740992 + 2 >> 10000000000000...010 // 中间 51 个 0
```
## 解决方案
对于整数,前端出现问题的几率可能比较低,毕竟很少有业务需要需要用到超大整数,只要运算结果不超过 Math.pow(2, 53) 就不会丢失精度。(如果遇到一般会用字符串来替代?)
对于小数,前端出现问题的几率还是很多的,尤其在一些电商网站涉及到金额等数据。解决方式:把小数放到位整数(乘倍数),再缩小回原来倍数(除倍数)
```js
// 0.1 + 0.2
(0.1*10 + 0.2*10) / 10 == 0.3 // true
```
对于 toFixed 方法出现的问题
```js
/**
*
* @param {*} num 需要转化的数值
* @param {*} s 保留到小数点后第几位
*/
function toFixed (num, s) {
let times = Math.pow(10, s)
let des = num * times + 0.5
des = parseInt(des, 10) / times
return des + ''
}
console.log(toFixed(1.335, 2)) // 1.34
```
>TODO:这里的解决方案感觉不是很好,有空再找找...
# 2.几个细节题
```js
// this 指向
var obj = {
name: 'foo',
fun1: function () {
console.log(this.name)
},
fun2: () => {
console.log(this.name)
}
}
var fun3 = obj.fun1
fun3()
obj.fun1()
obj.fun2()
```
```js
// 事件循环 - resolve 之后是否会继续往下执行?
setTimeout(function () {
console.log('a')
})
var p = new Promise(function (resolve, reject) {
console.log('b')
resolve()
console.log('c')
})
p.then(function () {
console.log('d')
})
console.log('e')
```
```js
// 原型链 构造函数的 __proto__ 指向什么呢?
Function.prototype.a = 1
Object.prototype.a = 2
Object.a = ?
```
# 3.模拟实现 lodash 中的 \_.get() 函数
模拟实现 lodash 中的 \_.get() 函数,实现如下传入参数取值效果
```js
function get() {
// 请补全函数参数和实现逻辑
}
const obj = {
selector: { to: { toutiao: 'FE coder' } },
target: [1, 2, { name: 'byted' }]
};
// 运行代码
get(obj, 'selector.to.toutiao', 'target[0]', 'target[2].name')
// 输出结果:// ['FE coder', 1, 'byted']
```
勉强实现了
```js
function get (obj, ...args) {
const res = [] // 存储返回结果
const params = args
params.forEach(item => { // 'selector.to.toutiao' , 'target[0]', 'target[2].name'
let splitItem = item.split('.') // -> ['selector', 'to', 'toutiao'], ['target[0]'], ['target[2], 'name']
let temp = null
splitItem.forEach(value => {
let index = value.indexOf('[') // case of 'target[0]'
if (index !== -1) {
let num = parseInt(value.substring(index + 1)) // 数组下标 Number 类型
let attr = value.substring(0, index)
temp = temp === null ? obj[attr][num] : temp[attr][num]
} else {
temp = temp === null ? obj[value] : temp[value] // obj.selector.to.toutiao
}
})
res.push(temp)
})
return res
}
console.log(get(obj, 'selector.to.toutiao', 'target[0]', 'target[2].name'))
```
# 4.找出页面出现最多的标签
你知道 `document.getElementsByTagName('*')`的用法吗?
```js
// 获取元素列表,以键值对的形式存储为一个对象
function getElements () {
// 如果把特殊字符串 "*" 传递给 getElementsByTagName() 方法
// 它将返回文档中所有元素的列表,元素排列的顺序就是它们在文档中的顺序。
// 返回一个 HTMLCollection - 类数组对象
const nodes = document.getElementsByTagName('*')
const tagsMap = {}
for (let i = 0, len = nodes.length; i < len; i++) {
let tagName = nodes[i].tagName
if (!tagsMap[tagName]) {
tagsMap[tagName] = 1
} else {
tagsMap[tagName] ++
}
}
return tagsMap
}
// n 为要选取的标签个数 - 即出现次数前 n 的标签名
// 将上面的方法获取的对象的键值对取出组成数组,按出现次数排序
function sortElements (obj, n) {
const arr = []
const res = []
for (let key of Object.keys(obj)) {
arr.push({ tagName: key, count: obj[key] })
}
// 冒泡
for (let i = arr.length - 1; i > 0; i--) {
for (let j = 0; j < i; j++) {
if (arr[j].count < arr[j + 1].count) { // 升序
swap(arr, j, j + 1)
}
}
}
for (let i = 0; i < n; i++) {
res.push(arr[i].tagName)
}
return res
}
function swap (arr, index1, index2) {
let temp = arr[index1]
arr[index1] = arr[index2]
arr[index2] = temp
}
let res = sortElements(getElements(), 2)
console.log(res)
```
# 5.细节问题
以下代码的执行结果是什么?
```js
function func() {
var a = b = 1;
}
func();
console.log(a);
console.log(b);
```
答案:error,1
解析:b 没有 var 为全局变量,var a = b 局部变量外部访问不到
# 6.fetch 本身不支持 timeout,能否对其进行封装使其支持 timeout?
参考:[https://imweb.io/topic/57c6ea35808fd2fb204eef63](https://imweb.io/topic/57c6ea35808fd2fb204eef63)
```js
/**
* 包装 fetch 方法,使其支持 timeout
* @param {Promise} fetch_promise fetch 请求
* @param {Numebr} timeout 超时时间设置
*/
function _fetch (fetch_promise, timeout) {
var abort_fn = null
// 这是一个可以被 reject 的 promise
var abort_promise = new Promise(function (resolve, reject) {
abort_fn = function () {
reject('abort promise')
}
})
// 这里使用 Promise.race, 以最快 resolve 或 reject 的结果来传入后续绑定的回调
var abortable_promise = Promise.race([
fetch_promise,
abort_promise
])
setTimeout(() => {
abort_fn()
}, timeout)
return abortable_promise
}
// usage
_fetch(fetch('http://test.com'), 2000)
.then(res => {
console.log(res)
})
.catch(err => {
console.log(err)
})
```
- 序言 & 更新日志
- H5
- Canvas
- 序言
- Part1-直线、矩形、多边形
- Part2-曲线图形
- Part3-线条操作
- Part4-文本操作
- Part5-图像操作
- Part6-变形操作
- Part7-像素操作
- Part8-渐变与阴影
- Part9-路径与状态
- Part10-物理动画
- Part11-边界检测
- Part12-碰撞检测
- Part13-用户交互
- Part14-高级动画
- CSS
- SCSS
- codePen
- 速查表
- 面试题
- 《CSS Secrets》
- SVG
- 移动端适配
- 滤镜(filter)的使用
- JS
- 基础概念
- 作用域、作用域链、闭包
- this
- 原型与继承
- 数组、字符串、Map、Set方法整理
- 垃圾回收机制
- DOM
- BOM
- 事件循环
- 严格模式
- 正则表达式
- ES6部分
- 设计模式
- AJAX
- 模块化
- 读冴羽博客笔记
- 第一部分总结-深入JS系列
- 第二部分总结-专题系列
- 第三部分总结-ES6系列
- 网络请求中的数据类型
- 事件
- 表单
- 函数式编程
- Tips
- JS-Coding
- Framework
- Vue
- 书写规范
- 基础
- vue-router & vuex
- 深入浅出 Vue
- 响应式原理及其他
- new Vue 发生了什么
- 组件化
- 编译流程
- Vue Router
- Vuex
- 前端路由的简单实现
- React
- 基础
- 书写规范
- Redux & react-router
- immutable.js
- CSS 管理
- React 16新特性-Fiber 与 Hook
- 《深入浅出React和Redux》笔记
- 前半部分
- 后半部分
- react-transition-group
- Vue 与 React 的对比
- 工程化与架构
- Hybird
- React Native
- 新手上路
- 内置组件
- 常用插件
- 问题记录
- Echarts
- 基础
- Electron
- 序言
- 配置 Electron 开发环境 & 基础概念
- React + TypeScript 仿 Antd
- TypeScript 基础
- 样式设计
- 组件测试
- 图标解决方案
- Algorithm
- 排序算法及常见问题
- 剑指 offer
- 动态规划
- DataStruct
- 概述
- 树
- 链表
- Network
- Performance
- Webpack
- PWA
- Browser
- Safety
- 微信小程序
- mpvue 课程实战记录
- 服务器
- 操作系统基础知识
- Linux
- Nginx
- redis
- node.js
- 基础及原生模块
- express框架
- node.js操作数据库
- 《深入浅出 node.js》笔记
- 前半部分
- 后半部分
- 数据库
- SQL
- 面试题收集
- 智力题
- 面试题精选1
- 面试题精选2
- 问答篇
- Other
- markdown 书写
- Git
- LaTex 常用命令