封装一个深拷贝
写的时候一点一点调试,从最简单的开始写
深拷贝需要递归,所以写一个递归函数 deepClone()
先数组、再对象
然后考虑循环引用
然后再考虑 正则、日期、map、set
最后考虑函数,函数需要考虑有参数、没参数、箭头函数,需要使用到eval
const isObject = (target) => {
return (typeof target === 'object' || typeof target === 'function') && target !== null
}
function deepClone(target, map = new WeakMap()) {
// 基本类型和null直接返回
if (!isObject(target)) {
return target
}
if (map.get(target)) {
return target
}
map.set(target, true)
let result
// 函数
if (Object.prototype.toString.call(target) === '[object Function]') {
const fnStr = target.toString()
if (target.prototype) {
// 普通函数
const index1 = fnStr.indexOf('(')
const index2 = fnStr.indexOf(')')
const index3 = fnStr.indexOf('{')
// 找params
const params = fnStr.slice(index1 + 1, index2)
// 找body
const body = fnStr.slice(index3 + 1, -1)
if (params) {
return new Function(params, body)
} else {
return new Function(body)
}
} else {
// 箭头函数
return eval(fnStr)
}
}
// 正则
if (Object.prototype.toString.call(target) === '[object RegExp]') {
result = new RegExp(target.source, target.flags)
return result
}
// 日期
if (Object.prototype.toString.call(target) === '[object Date]') {
result = new Date(target)
return result
}
// set
if (Object.prototype.toString.call(target) === '[object Set]') {
result = new Set()
target.forEach((value) => {
result.add(value)
})
return result
}
// map
if (Object.prototype.toString.call(target) === '[object Map]') {
result = new Map()
target.forEach((value, key) => {
result.set(key, deepClone(value, map))
})
return result
}
// 数组
if (Array.isArray(target)) {
result = []
target.forEach((item) => {
result.push(deepClone(item, map))
})
return result
}
// 对象
if (typeof target === 'object') {
result = {}
for (const key in target) {
if (Object.hasOwnProperty.call(target, key)) {
result[key] = deepClone(target[key], map)
}
}
return result
}
}
封装一个防抖函数
高频触发的函数 加个防抖操作,只在最后一次执行
名字叫 debounce
写一个最简单的
优化版的可以加个参数 控制是滚动一开始执行 还是滚动结束后执行
简单版
const debounce = (fn, delay) => {
let timer
return (...args) => {
clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(this, args)
}, delay)
}
}
复杂版
const debounce = (fn, delay, immediate) => {
let timer
return (...args) => {
if (immediate && !timer) {
fn.apply(this, args)
}
clearTimeout(timer)
timer = setTimeout(() => {
timer = null
if (!immediate) {
fn.apply(this, args)
}
}, delay)
}
}
封装一个节流函数
高频触发的函数,降低触发频率 一段时间内只执行一次 实现节流
const throttle = (fn, delay) => {
let start = 0
return (...args) => {
const now = new Date().getTime()
if (now - start > delay) {
fn.apply(this, args)
start = now
}
}
}
实现 a == 1 && a == 2 && a == 3
返回true
Symbol.toPrimitive
valueOf
toString
三个方法,有任意一个都行。
如果有 Symbol.toPrimitive
,优先 Symbol.toPrimitive
。
然后就是 toString
或者 valueOf
当需要隐式转换为 string
就会先调 toString
,如果没有的话 就valueOf
。
隐式转换为number一样,先valueOf
,如果没有的话 就toString
const a = {
count: 1,
[Symbol.toPrimitive](hint) {
console.log(this.count, 'toPrimitive', hint)
return this.count++
},
valueOf() {
console.log(this.count, 'valueOf')
return this.count++
},
toString() {
console.log(this.count, 'toString')
return this.count++
},
}
if (a == 1 && a == 2 && a == 3) {
console.log('进来了')
} else {
console.log('没进来')
}
封装一个版本号对比函数
长度可能有长有短 先把长度都保持一致 短的往后补0 然后遍历 落后版本 返回-1 优先版本 返回 1 版本一样 返回0
const compareVersion = (v1: string, v2: string) => {
const v1Arr = v1.split('.')
const v2Arr = v2.split('.')
/* 有可能有长有短 统一长度 */
const diffLength = v1Arr.length - v2Arr.length
if (diffLength > 0) {
v2Arr.push(...new Array(diffLength).fill(0))
} else if (diffLength < 0) {
v1Arr.push(...new Array(Math.abs(diffLength)).fill(0))
}
for (let i = 0; i < v1Arr.length; i++) {
if (v1Arr[i] > v2Arr[i]) {
return 1
} else if (v1Arr[i] < v2Arr[i]) {
return -1
}
}
return 0
}
参考: 手写 JavaScript 代码系列 —— 比较版本号
['a', 'b', 'c']
数组全排列
数组全排列使用递归 先排好一个,再排好两个,再排好3个,当排好一次之后,回溯,排下一个
递归记得先写出口,然后再写递归逻辑
const permutation = (arr: string[]) => {
const result = [] // 结果
/**
* @param inOrder 排好序的数组
* @param left 剩余的
*/
const recursion = (inOrder: string[], left: string[]) => {
// 出口
if (inOrder.length === arr.length) { // 都排好序了
// 收集结果
result.push(inOrder.join(''))
} else {
left.forEach((item, index) => {
let temp = [...left]
temp.splice(index, 1)
recursion(inOrder.concat(item), temp)
})
}
}
recursion([], arr)
return result
}
参考: js实现排列组合算法(全排列)
写一个函数 将数字展示为 万、亿、万亿
设置一个基准,假如单位是万,基准就是万
判断目标值可以有几个基准阶乘 floor(log(num) / log(base))
定义单位数组 ['万', '亿', '万亿']
又一个的话 单位就是万,两个的话 单位就是亿
const bigNumTransform = (num: number) => {
const base = 10000 // 基准
const result = {
value: '',
unit: '',
} // 存放结果
// 1. 判断是否超过基准
if (num < base) {
return num
}
// 2. 超过基准,开始转换
const units = ['万', '亿', '万亿']
// 3. 计算单位
const i = Math.floor(Math.log10(num) / Math.log10(base))
result.unit = units[i - 1]
// 4. 计算值
result.value = (num / Math.pow(base, i)).toFixed(2)
return `${result.value}${result.unit}`
}
封装一个函数使用正则表达式,获取url上的参数
关键正则:/([^?&=]+)=([^?&=]*)/g
解释:
[^?&=]+
匹配一个或者多个 不是 ?
&
或 =
的字符,一个或者多个
=
匹配 =
[^?&=]*
匹配一个或者多个 不是 ?
&
或 =
的字符,0个或者多个
()
括号创建捕获组
g
全局搜索
使用replace
方法,拼装结果。
export function getParams(url: string): any {
const params: any = {};
const reg = /([^?&=]+)=([^?&=]*)/g;
url.replace(reg, (match, key, value) => {
params[key] = value;
return match;
});
return params;
}
const url = 'https://www.baidu.com?name=zs&age=18';
console.log(getParams(url)); // { name: 'zs', age: '18' }
写一个冒泡排序
将最大的值冒泡到最后,每次排好序一个
/*
冒泡排序
两层for循环 将最大的数冒泡到尾部 每一次排好序一个
*/
function bubbleSort(arr: number[]) {
for (let i = 0; i < arr.length - 1; i++) {
for (let j = 0; j < arr.length - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
;[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]
}
}
}
}
const arr = [3, 5, 1, 4, 2]
bubbleSort(arr)
console.log(arr) // [1, 2, 3, 4, 5]
写一个快排
/*
快速排序
1. 从数列中挑出一个元素,称为 “基准”(pivot);
2. 小于基准的元素移动到左边,大于基准的元素移动到右边;
3. 递归地对左右两部分进行排序。
出口条件:数组长度小于等于1
*/
function quickSort(arr: number[]): number[] {
if (arr.length <= 1) {
return arr;
}
// 选择基准
const pivotIndex = Math.floor(arr.length / 2);
const pivot = arr.splice(pivotIndex, 1)[0];
const left = [];
const right = [];
// 分区
for (let i = 0; i < arr.length; i++) {
if (arr[i] < pivot) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
// 递归
return quickSort(left).concat([pivot], quickSort(right));
}
const arr = [3, 5, 1, 6, 4, 7, 2];
console.log(quickSort(arr)); // [1, 2, 3, 4, 5, 6, 7]