主题
尾递归
js
/** 当一个函数直接返回另一个函数调用的结果时,我们说这个函数是尾递归的 */
// 非尾递归版本
function factorial(n) {
if (n === 1) {
return 1;
}
return n * factorial(n - 1);
}
// 尾递归版本
function factorial(n, acc = 1) {
if (n === 1) {
return acc;
}
return factorial(n - 1, n * acc);
}
手写 instanceof
js
function _instanceof(obj, constructor) {
// 获取对象的原型
let proto = obj.__proto__;
// 获取构造函数的原型
let prototype = constructor.prototype;
// 循环查找原型链
while (proto) {
if (proto === prototype) {
// 如果找到相同的原型,则返回true
return true;
}
proto = proto.__proto__;
}
// 如果原型链末端都没有找到,则返回false
return false;
}
手写 new
js
function _new () {
// 1. 创建一个空对象
const obj = {};
// 获取构造函数
const con = [].shift().call(arguments);
// 2. 新对象的隐式原型 __proto__ 链接到构造函数的显式原型 prototype
obj.__proto__ = con.prototype;
// 3.构造函数内部的 this 绑定到这个新创建的对象 执行构造函数
const result = con.apply(obj, arguments);
// 4.如果构造函数没有返回非空对象,则返回创建的新对象
return result instanceof Object ? result : obj;
}
手写 call
js
Function.prototype._call = function (ctx = window) {
ctx._fn = this
const args = Array.from(arguments).slice(1)
const res = ctx._fn(...args)
delete ctx._fn
return res
}
手写 bind
js
Function.prototype._bind = function(thisArg, ...args){
let that = this
return function () {
return that.call(thisArg, ...args)
}
}
深拷贝
js
function deepCopy(obj) {
if (!obj || typeof obj !== "object") return obj;
let newObj = Array.isArray(obj) ? [] : {};
for (let key in obj) {
// 用来检测属性是否为对象的自有属性
if (obj.hasOwnProperty(key)) {
newObj[key] = deepCopy(obj[key]);
}
}
return newObj;
}
function deepClone(obj, hash = new WeakMap()) {
if (!object || typeof object !== "object") return object;
// 是对象的话就要进行深拷贝,遇到循环引用,将引用存储起来,如果存在就不再拷贝
if (hash.get(obj)) return hash.get(obj);
let cloneObj = Array.isArray(object) ? [] : {};
hash.set(obj, cloneObj);
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
// 实现一个递归拷贝
cloneObj[key] = deepClone(obj[key], hash);
}
}
return cloneObj;
}
快速排序
js
function quickSort(arr = []) {
const len = arr.length;
if (arr.length <= 1) {
return arr;
}
const pivotIndex = Math.floor(len / 2);
const pivot = arr[pivotIndex];
const less = [];
const greater = [];
for (let i = 0; i < len; i++) {
if (i === pivotIndex) {
continue;
}
if (arr[i] < pivot) {
less.push(arr[i]);
} else {
greater.push(arr[i]);
}
}
return [...quickSort(less), pivot, ...quickSort(greater)];
}
数组转树
js
function array2Tree(arr = []) {
const mapping = {};
arr.forEach((it) => (mapping[it?.id] = it));
const roots = [];
arr.forEach((it) => {
const parent = mapping[it.pid];
if (parent) {
(parent.children || (parent.children = [])).push(it);
} else {
roots.push(it);
}
});
return roots;
}
var list = [
{ id: 1, pid: 0, name: "1" },
{ id: 2, pid: 1, name: "1-1" },
{ id: 3, pid: 1, name: "1-2" },
{ id: 4, pid: 2, name: "1-1-1" },
];
冒泡排序
js
function bubbleSort(arr) {
let len = arr.length;
let temp;
for (let i = 0; i < len; i++) {
for (let j = 0; j < len - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
temp = arr[j + 1];
arr[j + 1] = arr[j];
arr[j] = temp;
}
}
}
return arr;
}
function bubbleSort() {
let low = 0;
let high = arr.length - 1;
let tmp;
let j;
while (low < high) {
// 正向冒泡,找到最大者
for (j = low; j < high; ++j)
if (arr[j] > arr[j + 1]) {
tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
// 修改high值, 前移一位
--high;
// 反向冒泡,找到最小者
for (j = high; j > low; --j)
if (arr[j] < arr[j - 1]) {
tmp = arr[j];
arr[j] = arr[j - 1];
arr[j - 1] = tmp;
}
// 修改low值,后移一位
++low;
}
return arr;
}
手写一个 redux
js
function createStore(initState, reducer) {
let state = initState
let listeners = []
function subscribe(listener) {
listeners.push(listener)
}
function dispatch(action) {
listeners.forEach(it => it())
state = reducer(state, action())
}
function getState() {
return state
}
return {
subscribe,
dispatch,
getState
}
}
const initState = {
count: 1
}
function reducer(state, action) {
switch (action.type) {
case 'ADD':
return {
...state,
count: state.count + 1
}
default:
return state
}
}
const store = createStore(initState, reducer)
const ADD = 'ADD'
function add() {
return {
type: ADD
}
}
store.dispatch(add())
回文数
js
function isPlalindrome(input = "") {
if (typeof input !== "string") return false;
return input.split("").reverse().join("") === input;
}
全排列
js
function permute(arr, memo = []) {
let result = [];
// 如果数组为空,则当前memo是一个有效的全排列
if (arr.length === 0) {
result.push(memo);
} else {
for (let i = 0; i < arr.length; i++) {
let current = arr.slice(); // 复制当前数组
let next = current.splice(i, 1); // 移除当前元素
let remaining = permute(current, memo.concat(next)); // 递归处理剩余元素
result = result.concat(remaining); // 合并结果
}
}
return result;
}
function permute(nums = []) {
let len = nums.length;
const result = [];
const visited = new Array(len).fill(false);
const dfs = (nums, len, depth, path, visited) => {
// 遍历到叶子结点了,可以返回了
if (depth === len) {
result.push([...path]);
}
for (let i = 0; i < len; i++) {
// 如果没遍历过
if (!visited[i]) {
// 压入 path 数组,然后是否遍历过的数组此下标处变为 true
path.push(nums[i]);
visited[i] = true;
// 继续 dfs,直到最后一层
dfs(nums, len, depth + 1, path, visited);
// 进行回溯,还原,以便下一次使用
visited[i] = false;
path.pop();
}
}
};
dfs(nums, len, 0, [], visited);
return result;
}
翻转二叉树
js
function invertTree(root) {
if (root === null) {
return null;
}
const left = invertTree(root.left);
const right = invertTree(root.right);
root.left = right;
root.right = left;
return root;
}
洗牌算法
js
[1, 2, 3, 4, 5, 6].sort(function () {
return 0.5 - Math.random();
});
function shuffle(arr) {
let length = arr.length;
let temp;
let random;
while (0 != length) {
random = Math.floor(Math.random() * length);
length--;
// swap
temp = arr[length];
arr[length] = arr[random];
arr[random] = temp;
}
return arr;
}
function shuffle(arr) {
let len = arr.length;
let random;
while (0 != len) {
random = (Math.random() * len--) >>> 0; // 无符号右移位运算符向下取整
[arr[len], arr[random]] = [arr[random], arr[len]]; // ES6的结构赋值实现变量互换
}
return arr;
}
EventBus
js
class EventBus {
constructor () {
this._eq = {}
}
on (ev, cb) {
this._eq?.[ev] ? this._eq?.[ev].push(cb) : this._eq?.[ev] = [cb]
}
emit (ev, ...args) {
this._eq?.[ev].forEach(cb => cb?.(...args))
}
off (ev) {
delete this._eq?.[ev]
}
once (ev, cb) {
this.on(ev, (...args) => {
cb(...args)
this.off(ev)
})
}
}
最短编辑距离问题
js
function minEditDistance(source, target) {
const m = source.length;
const n = target.length;
const dp = Array(m + 1).fill(0).map(() => Array(n + 1).fill(0));
// 初始化第一行和第一列
for (let i = 0; i <= m; i++) {
dp[i][0] = i;
}
for (let j = 0; j <= n; j++) {
dp[0][j] = j;
}
// 动态规划计算最短编辑距离
for (let i = 1; i <= m; i++) {
for (let j = 1; j <= n; j++) {
const cost = (source[i - 1] === target[j - 1]) ? 0 : 1;
dp[i][j] = Math.min(
dp[i - 1][j] + 1, // 删除
dp[i][j - 1] + 1, // 插入
dp[i - 1][j - 1] + cost // 替换
);
}
}
return dp[m][n];
}
路径总和
js
function hasPathSum(root, targetSum) {
if (!root) {
return false;
}
if (!root.left && !root.right) {
return root.val === targetSum;
}
return (
hasPathSum(root.left, targetSum - root.val) ||
hasPathSum(root.right, targetSum - root.val)
);
}
防抖
js
function debounce(fn, wait) {
let timer = null;
return function () {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, arguments);
}, wait);
};
}
once
js
function once(fn) {
let called = false;
return function () {
if (!called) {
called = true;
fn.apply(this, arguments);
}
};
}
位运算
js
function swapNumber(a, b) {
a = a ^ b;
b = a ^ b;
a = a ^ b;
return {
a,
b,
};
}
function oddCountNumber(arr = []) {
let num = 0;
const len = arr.length;
for (let i = 0; i < len; i++) {
num = num ^ arr[i];
}
return num;
}
sleep
js
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
// 性能很差
function sleep(delay) {
for (let timer = Date.now(); Date.now() - timer <= delay; );
}
生成随机数
js
function randomNum(min, max) {
return min + Math.floor(Math.random() * (max - min + 1));
}
flat
js
function flat(list = []) {
return list.reduce((R, c) => {
return R.concat(Array.isArray(c) ? flat(c) : c);
}, []);
}
function flat(arr = []) {
return arr.reduce((R, c) => {
return Array.isArray(c) ? [...R, ...flat(c)] : [...R, c];
}, []);
}
uniq
js
function uniq(list = []) {
return list.reduce((R, c) => {
R.indexOf(c) < 0 ? R.push(c) : null;
return R;
}, []);
}
counter
js
function counter(list = []) {
return list.reduce((R, c) => {
R[c] ? R[c]++ : (R[c] = 1);
}, {});
}
max
js
function max(list = []) {
return list.reduce((R, c) => {
return Math.max(R, c);
});
}
compose
js
const compose = (...args) => {
let start = args.length - 1
return function () {
let result = args[start].apply(this, arguments)
while(start) {
result = args[--start].call(this, result)
}
return result
}
}
function compose(...fns) {
return fns.reduce(
(a, b) =>
(...args) =>
a(b(...args))
);
}
defineProperty
js
var a = {
value: 1,
toString () {
return this.value ++
}
}
if (a == 1 && a == 2 && a == 3) {
console.info(`i am ok.`)
}
let num = 0
Object.defineProperty(window, 'a', {
get () {
return ++num
}
})
if (a == 1 && a == 2 && a == 3) {
console.log('i am ok.')
}
isRepeat
js
const isRepeat = (list = []) {
let hash = {}
for (let i of list) {
if(hash[i]) {
return true
}
hash[i] = true
}
return false
}
不同类型的数组去重
js
function uniqArray (arr = []) {
const cache = new Set();
const result = [];
arr.forEach(it => {
let str = JSON.stringify(it);
if (!cache.has(str)) {
cache.add(str);
result.push(it);
}
})
return result
}
嵌套数组扁平化
js
function flatObj(obj, prefix = "") {
return Object.keys(obj).reduce((acc, key) => {
const value = obj[key];
const keyPrefix = prefix ? `${prefix}.${key}` : key;
if (Array.isArray(value)) {
acc = value.reduce((acc, item, index) => {
return typeof item === "number"
? { ...acc, [`${keyPrefix}[${index}]`]: item }
: Object.assign(acc, flatObj(item, `${keyPrefix}[${index}]`));
}, acc);
} else if (typeof value === "object" && value !== null) {
acc = Object.assign(acc, flatObj(value, keyPrefix));
} else {
acc[keyPrefix] = value;
}
return acc;
}, {});
}