# 手写篇
# 1. 数值转换,一元运算符 +
const age = +'22' // 22
let text2 = '1' + 2 // "12"
let text3 = 1 + '2' // "12"
let text4 = 1 + 2 + '3' // "33"
let num = +text1 // 12 转换为 Number 类型
# 2. es5 实现 promise
# 3. 栈内存、堆内存理解
var a = { n: 1 }
var b = a
a.x = a = { n: 2 }
a.x // --> undefined
b.x // --> {n: 2}
- 优先级。.的优先级高于=,所以先执行 a.x,堆内存中的{n: 1}就会变成{n: 1, x: undefined},改变之后相应的 b.x 也变化了,因为指向的是同一个对象。
- 赋值操作是从右到左,所以先执行 a = {n: 2},a 的引用就被改变了,然后这个返回值又赋值给了 a.x,需要注意的是这时候 a.x 是第一步中的{n: 1, x: undefined}那个对象,其实就是 b.x,相当于 b.x = {n: 2}
# 4. 手写实现 new 详细参考
function create(Con) {
// 创建一个空的对象
var obj = new Object(),
// 获得构造函数,arguments中去除第一个参数
Con = [].shift.call(arguments)
// 链接到原型,obj 可以访问到构造函数原型中的属性
obj.__proto__ = Con.prototype
// 绑定 this 实现继承,obj 可以访问到构造函数中的属性
var ret = Con.apply(obj, arguments)
// 优先返回构造函数返回的对象
return ret instanceof Object ? ret : obj
}
# 5. 深拷贝实现
深拷贝可以拆分成 2 步,浅拷贝+递归,浅拷贝时判断属性值是否是对象,如果是对象就进行递归操作,两个一结合就实现了深拷贝。
function cloneDeep1(source) {
var target = {}
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
if (typeof source[key] === 'object') {
target[key] = cloneDeep1(source[key]) // 注意这里
} else {
target[key] = source[key]
}
}
}
return target
}
一个简单的深拷贝就完成了,但是这个实现还存在很多问题。
1、没有对传入参数进行校验,传入 null 时应该返回 null 而不是 {}
2、对于对象的判断逻辑不严谨,因为 typeof null === 'object'
3、没有考虑数组的兼容
4、没有考虑循环引用,循环引用指的是 a.a = a (循环引用延伸到 commonjs、esmodule 的循环引用)
下面代码是一个改良版的,但仍存在一些问题,比如正则、symbol、date 类型的拷贝可能会出现问题, 所以日常开发中用 lodash 的深拷贝,知道深拷贝的过程中存在哪些问题就好了!
const isObj = target => target !== null && typeof target === 'object'
// 因为保存source在push进arr的时候,保存的是指针地址,所以下面用===能找到
const findSoure = (arr, source) => arr.find(item => item.source === source)
function deepClone(source, saveList = []) {
if (!isObj(source)) return source
let result = Array.isArray(source) ? [] : {}
let isExitReult = findSoure(saveList, source)
if (isExitReult) return isExitReult.result
// 须在递归调用之前deepClone之前保存
saveList.push({ source, result })
for (const key in source) {
// 只处理source的自身属性,不处理prototype的属性
if (Object.hasOwnProperty.call(source, key)) {
const element = source[key]
if (isObj(element)) {
result[key] = deepClone(element, saveList)
} else {
result[key] = element
}
}
}
return result
}
let AA = {
a: 3,
b: {
text: '8',
},
c: [1, 2, 3, 4],
}
AA.d = AA
let BB = deepClone(AA)
BB.c = [2, 3, 4, 5]
console.log(AA, BB)
# 6. js 中大数相加
JS 在存放整数的时候是有一个安全范围的,一旦数字超过这个范围便会损失精度。不能拿精度损失的数字进行运行,因为运算结果一样是会损失精度的。所以,我们要用字符串来表示数据!(不会丢失精度)
JS 中整数的最大安全范围可以查到是:9007199254740991
假如我们要进行 9007199254740991 + 1234567899999999999
function addString(str1, str2) {
// 此处加1是防止位置相同,第一位相加后需要向前进位
let len = Math.max(str1.length, str2.length) + 1
let newStr1 = str1.padStart(len, 0)
let newStr2 = str2.padStart(len, 0)
let newStr1Arr = newStr1
.split('')
.reverse()
.map(i => Number(i))
let newStr2Arr = newStr2
.split('')
.reverse()
.map(i => Number(i))
let resultArr = []
let addFlag = 0
let pushNum = null
newStr1Arr.forEach((element, idx) => {
let sumFirst = element + newStr2Arr[idx] + addFlag
if (sumFirst >= 10) {
pushNum = sumFirst % 10
addFlag = 1
} else {
pushNum = sumFirst
addFlag = 0
}
resultArr.push(pushNum)
})
let resultString = resultArr.reverse().join('')
// 字符串首位为0截取去掉
if (resultString[0] == 0) {
return resultString.substring(1)
} else {
return resultString
}
}
addString('9007199254740991', '1234567899999999999') // 11233575099254740990
//实现字符串大数相加
function add(a, b) {
//取两个数字的最大长度
let maxLength = Math.max(a.length, b.length)
//用0去补齐长度
a = a.padStart(maxLength, 0) //"0009007199254740991"
b = b.padStart(maxLength, 0) //"1234567899999999999"
//定义加法过程中需要用到的变量
let t = 0
let f = 0 //"进位"
let sum = ''
for (let i = maxLength - 1; i >= 0; i--) {
t = parseInt(a[i]) + parseInt(b[i]) + f
f = Math.floor(t / 10)
sum = (t % 10) + sum
}
if (f == 1) {
sum = '1' + sum
}
return sum
}