no message
This commit is contained in:
parent
1ec3a43ab6
commit
49843e4d7a
205
algorithm/01-js-basics.md
Normal file
205
algorithm/01-js-basics.md
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
# JS 基础与手写实现
|
||||||
|
|
||||||
|
## 1. 手写 Promise
|
||||||
|
|
||||||
|
```js
|
||||||
|
class MyPromise {
|
||||||
|
constructor(executor) {
|
||||||
|
this.state = 'pending';
|
||||||
|
this.value = undefined;
|
||||||
|
this.callbacks = [];
|
||||||
|
|
||||||
|
const resolve = (value) => {
|
||||||
|
if (this.state !== 'pending') return;
|
||||||
|
this.state = 'fulfilled';
|
||||||
|
this.value = value;
|
||||||
|
this.callbacks.forEach(cb => cb.onFulfilled(value));
|
||||||
|
};
|
||||||
|
|
||||||
|
const reject = (reason) => {
|
||||||
|
if (this.state !== 'pending') return;
|
||||||
|
this.state = 'rejected';
|
||||||
|
this.value = reason;
|
||||||
|
this.callbacks.forEach(cb => cb.onRejected(reason));
|
||||||
|
};
|
||||||
|
|
||||||
|
try { executor(resolve, reject); } catch (e) { reject(e); }
|
||||||
|
}
|
||||||
|
|
||||||
|
then(onFulfilled, onRejected) {
|
||||||
|
return new MyPromise((resolve, reject) => {
|
||||||
|
const handle = (callback, fallback) => {
|
||||||
|
try {
|
||||||
|
const result = (callback || fallback)(this.value);
|
||||||
|
result instanceof MyPromise ? result.then(resolve, reject) : resolve(result);
|
||||||
|
} catch (e) { reject(e); }
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.state === 'fulfilled') handle(onFulfilled, v => v);
|
||||||
|
else if (this.state === 'rejected') handle(onRejected, e => { throw e; });
|
||||||
|
else this.callbacks.push({
|
||||||
|
onFulfilled: () => handle(onFulfilled, v => v),
|
||||||
|
onRejected: () => handle(onRejected, e => { throw e; })
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. Promise.all / Promise.race
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Promise.all - 所有成功才成功
|
||||||
|
Promise.myAll = (promises) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const results = [];
|
||||||
|
let count = 0;
|
||||||
|
promises.forEach((p, i) => {
|
||||||
|
Promise.resolve(p).then(val => {
|
||||||
|
results[i] = val;
|
||||||
|
if (++count === promises.length) resolve(results);
|
||||||
|
}, reject);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Promise.race - 第一个完成就返回
|
||||||
|
Promise.myRace = (promises) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
promises.forEach(p => Promise.resolve(p).then(resolve, reject));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. 防抖与节流
|
||||||
|
|
||||||
|
```js
|
||||||
|
// 防抖:停止触发后执行
|
||||||
|
function debounce(fn, delay) {
|
||||||
|
let timer = null;
|
||||||
|
return function(...args) {
|
||||||
|
clearTimeout(timer);
|
||||||
|
timer = setTimeout(() => fn.apply(this, args), delay);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 节流:固定间隔执行
|
||||||
|
function throttle(fn, delay) {
|
||||||
|
let last = 0;
|
||||||
|
return function(...args) {
|
||||||
|
const now = Date.now();
|
||||||
|
if (now - last >= delay) {
|
||||||
|
last = now;
|
||||||
|
fn.apply(this, args);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. 深拷贝(处理循环引用)
|
||||||
|
|
||||||
|
```js
|
||||||
|
function deepClone(obj, map = new WeakMap()) {
|
||||||
|
if (obj === null || typeof obj !== 'object') return obj;
|
||||||
|
if (obj instanceof Date) return new Date(obj);
|
||||||
|
if (obj instanceof RegExp) return new RegExp(obj);
|
||||||
|
|
||||||
|
if (map.has(obj)) return map.get(obj); // 处理循环引用
|
||||||
|
|
||||||
|
const clone = Array.isArray(obj) ? [] : {};
|
||||||
|
map.set(obj, clone);
|
||||||
|
|
||||||
|
for (const key in obj) {
|
||||||
|
if (obj.hasOwnProperty(key)) {
|
||||||
|
clone[key] = deepClone(obj[key], map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. 函数柯里化
|
||||||
|
|
||||||
|
```js
|
||||||
|
function curry(fn) {
|
||||||
|
return function curried(...args) {
|
||||||
|
if (args.length >= fn.length) {
|
||||||
|
return fn.apply(this, args);
|
||||||
|
}
|
||||||
|
return (...nextArgs) => curried(...args, ...nextArgs);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用示例
|
||||||
|
const add = (a, b, c) => a + b + c;
|
||||||
|
const curriedAdd = curry(add);
|
||||||
|
curriedAdd(1)(2)(3); // 6
|
||||||
|
curriedAdd(1, 2)(3); // 6
|
||||||
|
```
|
||||||
|
|
||||||
|
## 6. call / apply / bind 实现
|
||||||
|
|
||||||
|
```js
|
||||||
|
// call
|
||||||
|
Function.prototype.myCall = function(ctx, ...args) {
|
||||||
|
ctx = ctx || window;
|
||||||
|
const key = Symbol();
|
||||||
|
ctx[key] = this;
|
||||||
|
const result = ctx[key](...args);
|
||||||
|
delete ctx[key];
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
// apply
|
||||||
|
Function.prototype.myApply = function(ctx, args = []) {
|
||||||
|
ctx = ctx || window;
|
||||||
|
const key = Symbol();
|
||||||
|
ctx[key] = this;
|
||||||
|
const result = ctx[key](...args);
|
||||||
|
delete ctx[key];
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
// bind
|
||||||
|
Function.prototype.myBind = function(ctx, ...args) {
|
||||||
|
const fn = this;
|
||||||
|
return function(...newArgs) {
|
||||||
|
return fn.apply(ctx, [...args, ...newArgs]);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## 7. 事件总线 EventEmitter
|
||||||
|
|
||||||
|
```js
|
||||||
|
class EventEmitter {
|
||||||
|
constructor() {
|
||||||
|
this.events = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
on(event, listener) {
|
||||||
|
(this.events[event] ||= []).push(listener);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
off(event, listener) {
|
||||||
|
if (this.events[event]) {
|
||||||
|
this.events[event] = this.events[event].filter(l => l !== listener);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit(event, ...args) {
|
||||||
|
(this.events[event] || []).forEach(listener => listener(...args));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
once(event, listener) {
|
||||||
|
const wrapper = (...args) => {
|
||||||
|
listener(...args);
|
||||||
|
this.off(event, wrapper);
|
||||||
|
};
|
||||||
|
return this.on(event, wrapper);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
145
algorithm/02-array-string.md
Normal file
145
algorithm/02-array-string.md
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
# 数组与字符串操作
|
||||||
|
|
||||||
|
## 1. 数组去重(多种方法)
|
||||||
|
|
||||||
|
```js
|
||||||
|
// 方法1: Set
|
||||||
|
const unique1 = arr => [...new Set(arr)];
|
||||||
|
|
||||||
|
// 方法2: filter + indexOf
|
||||||
|
const unique2 = arr => arr.filter((v, i) => arr.indexOf(v) === i);
|
||||||
|
|
||||||
|
// 方法3: reduce
|
||||||
|
const unique3 = arr => arr.reduce((acc, v) => acc.includes(v) ? acc : [...acc, v], []);
|
||||||
|
|
||||||
|
// 方法4: 对象数组按属性去重
|
||||||
|
const uniqueByKey = (arr, key) => {
|
||||||
|
const map = new Map();
|
||||||
|
arr.forEach(item => !map.has(item[key]) && map.set(item[key], item));
|
||||||
|
return [...map.values()];
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. 扁平化数组
|
||||||
|
|
||||||
|
```js
|
||||||
|
// 递归实现
|
||||||
|
function flatten(arr, depth = Infinity) {
|
||||||
|
return arr.reduce((acc, val) => {
|
||||||
|
return acc.concat(
|
||||||
|
Array.isArray(val) && depth > 0 ? flatten(val, depth - 1) : val
|
||||||
|
);
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 迭代实现(使用栈)
|
||||||
|
function flattenIterative(arr) {
|
||||||
|
const stack = [...arr];
|
||||||
|
const result = [];
|
||||||
|
while (stack.length) {
|
||||||
|
const item = stack.pop();
|
||||||
|
Array.isArray(item) ? stack.push(...item) : result.unshift(item);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 原生方法
|
||||||
|
arr.flat(Infinity);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. 字符串反转
|
||||||
|
|
||||||
|
```js
|
||||||
|
const reverse = str => str.split('').reverse().join('');
|
||||||
|
|
||||||
|
// 或者使用扩展运算符
|
||||||
|
const reverse2 = str => [...str].reverse().join('');
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. 判断回文
|
||||||
|
|
||||||
|
```js
|
||||||
|
const isPalindrome = str => {
|
||||||
|
const clean = str.toLowerCase().replace(/[^a-z0-9]/g, '');
|
||||||
|
return clean === clean.split('').reverse().join('');
|
||||||
|
};
|
||||||
|
|
||||||
|
// 双指针法
|
||||||
|
const isPalindrome2 = str => {
|
||||||
|
const s = str.toLowerCase().replace(/[^a-z0-9]/g, '');
|
||||||
|
let left = 0, right = s.length - 1;
|
||||||
|
while (left < right) {
|
||||||
|
if (s[left++] !== s[right--]) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. 最长公共前缀
|
||||||
|
|
||||||
|
```js
|
||||||
|
function longestCommonPrefix(strs) {
|
||||||
|
if (!strs.length) return '';
|
||||||
|
|
||||||
|
let prefix = strs[0];
|
||||||
|
for (let i = 1; i < strs.length; i++) {
|
||||||
|
while (strs[i].indexOf(prefix) !== 0) {
|
||||||
|
prefix = prefix.slice(0, -1);
|
||||||
|
if (!prefix) return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 示例: ["flower","flow","flight"] => "fl"
|
||||||
|
```
|
||||||
|
|
||||||
|
## 6. 模板字符串解析
|
||||||
|
|
||||||
|
```js
|
||||||
|
function render(template, data) {
|
||||||
|
return template.replace(/\{\{(\w+)\}\}/g, (match, key) => {
|
||||||
|
return data[key] !== undefined ? data[key] : match;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 支持嵌套属性
|
||||||
|
function renderDeep(template, data) {
|
||||||
|
return template.replace(/\{\{(.+?)\}\}/g, (match, path) => {
|
||||||
|
const keys = path.trim().split('.');
|
||||||
|
let value = data;
|
||||||
|
for (const key of keys) {
|
||||||
|
value = value?.[key];
|
||||||
|
if (value === undefined) return match;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用示例
|
||||||
|
render('Hello, {{name}}!', { name: 'World' }); // "Hello, World!"
|
||||||
|
renderDeep('{{user.info.name}}', { user: { info: { name: 'Tom' } } }); // "Tom"
|
||||||
|
```
|
||||||
|
|
||||||
|
## 7. 数组按属性去重并排序
|
||||||
|
|
||||||
|
```js
|
||||||
|
function uniqueAndSort(arr, key, sortKey, order = 'asc') {
|
||||||
|
// 去重
|
||||||
|
const map = new Map();
|
||||||
|
arr.forEach(item => !map.has(item[key]) && map.set(item[key], item));
|
||||||
|
|
||||||
|
// 排序
|
||||||
|
return [...map.values()].sort((a, b) => {
|
||||||
|
return order === 'asc' ? a[sortKey] - b[sortKey] : b[sortKey] - a[sortKey];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 示例
|
||||||
|
const users = [
|
||||||
|
{ id: 1, name: 'Tom', age: 20 },
|
||||||
|
{ id: 2, name: 'Jerry', age: 18 },
|
||||||
|
{ id: 1, name: 'Tom2', age: 25 }
|
||||||
|
];
|
||||||
|
uniqueAndSort(users, 'id', 'age'); // 按id去重,按age升序排序
|
||||||
|
```
|
||||||
268
algorithm/03-data-structure.md
Normal file
268
algorithm/03-data-structure.md
Normal file
@ -0,0 +1,268 @@
|
|||||||
|
# 数据结构与算法
|
||||||
|
|
||||||
|
## 1. 链表
|
||||||
|
|
||||||
|
### 反转链表
|
||||||
|
|
||||||
|
```js
|
||||||
|
function reverseList(head) {
|
||||||
|
let prev = null, curr = head;
|
||||||
|
while (curr) {
|
||||||
|
const next = curr.next;
|
||||||
|
curr.next = prev;
|
||||||
|
prev = curr;
|
||||||
|
curr = next;
|
||||||
|
}
|
||||||
|
return prev;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 环形链表判断
|
||||||
|
|
||||||
|
```js
|
||||||
|
function hasCycle(head) {
|
||||||
|
let slow = head, fast = head;
|
||||||
|
while (fast?.next) {
|
||||||
|
slow = slow.next;
|
||||||
|
fast = fast.next.next;
|
||||||
|
if (slow === fast) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 合并有序链表
|
||||||
|
|
||||||
|
```js
|
||||||
|
function mergeTwoLists(l1, l2) {
|
||||||
|
const dummy = { next: null };
|
||||||
|
let curr = dummy;
|
||||||
|
while (l1 && l2) {
|
||||||
|
if (l1.val <= l2.val) {
|
||||||
|
curr.next = l1;
|
||||||
|
l1 = l1.next;
|
||||||
|
} else {
|
||||||
|
curr.next = l2;
|
||||||
|
l2 = l2.next;
|
||||||
|
}
|
||||||
|
curr = curr.next;
|
||||||
|
}
|
||||||
|
curr.next = l1 || l2;
|
||||||
|
return dummy.next;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 二叉树
|
||||||
|
|
||||||
|
### 遍历(前中后序)
|
||||||
|
|
||||||
|
```js
|
||||||
|
// 前序:根-左-右
|
||||||
|
const preorder = (root, res = []) => {
|
||||||
|
if (!root) return res;
|
||||||
|
res.push(root.val);
|
||||||
|
preorder(root.left, res);
|
||||||
|
preorder(root.right, res);
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 中序:左-根-右
|
||||||
|
const inorder = (root, res = []) => {
|
||||||
|
if (!root) return res;
|
||||||
|
inorder(root.left, res);
|
||||||
|
res.push(root.val);
|
||||||
|
inorder(root.right, res);
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 后序:左-右-根
|
||||||
|
const postorder = (root, res = []) => {
|
||||||
|
if (!root) return res;
|
||||||
|
postorder(root.left, res);
|
||||||
|
postorder(root.right, res);
|
||||||
|
res.push(root.val);
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 求最大深度
|
||||||
|
|
||||||
|
```js
|
||||||
|
const maxDepth = root => {
|
||||||
|
if (!root) return 0;
|
||||||
|
return 1 + Math.max(maxDepth(root.left), maxDepth(root.right));
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 路径和
|
||||||
|
|
||||||
|
```js
|
||||||
|
function hasPathSum(root, targetSum) {
|
||||||
|
if (!root) return false;
|
||||||
|
if (!root.left && !root.right) return targetSum === root.val;
|
||||||
|
return hasPathSum(root.left, targetSum - root.val)
|
||||||
|
|| hasPathSum(root.right, targetSum - root.val);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 栈与队列
|
||||||
|
|
||||||
|
### 用栈实现队列
|
||||||
|
|
||||||
|
```js
|
||||||
|
class MyQueue {
|
||||||
|
constructor() {
|
||||||
|
this.inStack = [];
|
||||||
|
this.outStack = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
push(x) { this.inStack.push(x); }
|
||||||
|
|
||||||
|
pop() {
|
||||||
|
if (!this.outStack.length) {
|
||||||
|
while (this.inStack.length) this.outStack.push(this.inStack.pop());
|
||||||
|
}
|
||||||
|
return this.outStack.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
peek() {
|
||||||
|
if (!this.outStack.length) {
|
||||||
|
while (this.inStack.length) this.outStack.push(this.inStack.pop());
|
||||||
|
}
|
||||||
|
return this.outStack[this.outStack.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
empty() { return !this.inStack.length && !this.outStack.length; }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 有效的括号
|
||||||
|
|
||||||
|
```js
|
||||||
|
function isValid(s) {
|
||||||
|
const map = { ')': '(', ']': '[', '}': '{' };
|
||||||
|
const stack = [];
|
||||||
|
for (const c of s) {
|
||||||
|
if (map[c]) {
|
||||||
|
if (stack.pop() !== map[c]) return false;
|
||||||
|
} else {
|
||||||
|
stack.push(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return !stack.length;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 哈希表
|
||||||
|
|
||||||
|
### 两数之和
|
||||||
|
|
||||||
|
```js
|
||||||
|
function twoSum(nums, target) {
|
||||||
|
const map = new Map();
|
||||||
|
for (let i = 0; i < nums.length; i++) {
|
||||||
|
const diff = target - nums[i];
|
||||||
|
if (map.has(diff)) return [map.get(diff), i];
|
||||||
|
map.set(nums[i], i);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 字母异位词分组
|
||||||
|
|
||||||
|
```js
|
||||||
|
function groupAnagrams(strs) {
|
||||||
|
const map = new Map();
|
||||||
|
for (const s of strs) {
|
||||||
|
const key = [...s].sort().join('');
|
||||||
|
map.set(key, (map.get(key) || []).concat(s));
|
||||||
|
}
|
||||||
|
return [...map.values()];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. 排序算法
|
||||||
|
|
||||||
|
### 快速排序
|
||||||
|
|
||||||
|
```js
|
||||||
|
function quickSort(arr) {
|
||||||
|
if (arr.length <= 1) return arr;
|
||||||
|
const pivot = arr[Math.floor(arr.length / 2)];
|
||||||
|
const left = arr.filter(x => x < pivot);
|
||||||
|
const middle = arr.filter(x => x === pivot);
|
||||||
|
const right = arr.filter(x => x > pivot);
|
||||||
|
return [...quickSort(left), ...middle, ...quickSort(right)];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 归并排序
|
||||||
|
|
||||||
|
```js
|
||||||
|
function mergeSort(arr) {
|
||||||
|
if (arr.length <= 1) return arr;
|
||||||
|
const mid = Math.floor(arr.length / 2);
|
||||||
|
const left = mergeSort(arr.slice(0, mid));
|
||||||
|
const right = mergeSort(arr.slice(mid));
|
||||||
|
return merge(left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
function merge(left, right) {
|
||||||
|
const result = [];
|
||||||
|
let i = 0, j = 0;
|
||||||
|
while (i < left.length && j < right.length) {
|
||||||
|
result.push(left[i] < right[j] ? left[i++] : right[j++]);
|
||||||
|
}
|
||||||
|
return result.concat(left.slice(i), right.slice(j));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. 二分查找
|
||||||
|
|
||||||
|
### 基础二分查找
|
||||||
|
|
||||||
|
```js
|
||||||
|
function binarySearch(arr, target) {
|
||||||
|
let left = 0, right = arr.length - 1;
|
||||||
|
while (left <= right) {
|
||||||
|
const mid = Math.floor((left + right) / 2);
|
||||||
|
if (arr[mid] === target) return mid;
|
||||||
|
arr[mid] < target ? (left = mid + 1) : (right = mid - 1);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 旋转数组查找
|
||||||
|
|
||||||
|
```js
|
||||||
|
function searchRotated(nums, target) {
|
||||||
|
let left = 0, right = nums.length - 1;
|
||||||
|
while (left <= right) {
|
||||||
|
const mid = Math.floor((left + right) / 2);
|
||||||
|
if (nums[mid] === target) return mid;
|
||||||
|
|
||||||
|
// 左半边有序
|
||||||
|
if (nums[left] <= nums[mid]) {
|
||||||
|
if (target >= nums[left] && target < nums[mid]) right = mid - 1;
|
||||||
|
else left = mid + 1;
|
||||||
|
} else {
|
||||||
|
// 右半边有序
|
||||||
|
if (target > nums[mid] && target <= nums[right]) left = mid + 1;
|
||||||
|
else right = mid - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
```
|
||||||
235
algorithm/04-practical-scenarios.md
Normal file
235
algorithm/04-practical-scenarios.md
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
# 实际场景与逻辑题
|
||||||
|
|
||||||
|
## 1. 并发控制(限制并发请求数)
|
||||||
|
|
||||||
|
```js
|
||||||
|
async function limitConcurrency(tasks, limit) {
|
||||||
|
const results = [];
|
||||||
|
const executing = [];
|
||||||
|
|
||||||
|
for (const [index, task] of tasks.entries()) {
|
||||||
|
const p = Promise.resolve().then(() => task()).then(res => {
|
||||||
|
results[index] = res;
|
||||||
|
executing.splice(executing.indexOf(p), 1);
|
||||||
|
});
|
||||||
|
executing.push(p);
|
||||||
|
|
||||||
|
if (executing.length >= limit) {
|
||||||
|
await Promise.race(executing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(executing);
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用示例
|
||||||
|
const tasks = urls.map(url => () => fetch(url));
|
||||||
|
await limitConcurrency(tasks, 3); // 最多3个并发
|
||||||
|
```
|
||||||
|
|
||||||
|
### 异步任务调度器
|
||||||
|
|
||||||
|
```js
|
||||||
|
class Scheduler {
|
||||||
|
constructor(limit) {
|
||||||
|
this.limit = limit;
|
||||||
|
this.queue = [];
|
||||||
|
this.running = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
add(promiseCreator) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.queue.push({ promiseCreator, resolve, reject });
|
||||||
|
this.run();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
run() {
|
||||||
|
while (this.running < this.limit && this.queue.length) {
|
||||||
|
const { promiseCreator, resolve, reject } = this.queue.shift();
|
||||||
|
this.running++;
|
||||||
|
promiseCreator()
|
||||||
|
.then(resolve, reject)
|
||||||
|
.finally(() => { this.running--; this.run(); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 缓存函数(Memoization)
|
||||||
|
|
||||||
|
```js
|
||||||
|
function memoize(fn) {
|
||||||
|
const cache = new Map();
|
||||||
|
return function(...args) {
|
||||||
|
const key = JSON.stringify(args);
|
||||||
|
if (cache.has(key)) return cache.get(key);
|
||||||
|
const result = fn.apply(this, args);
|
||||||
|
cache.set(key, result);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 支持过期时间
|
||||||
|
function memoizeWithTTL(fn, ttl = 60000) {
|
||||||
|
const cache = new Map();
|
||||||
|
return function(...args) {
|
||||||
|
const key = JSON.stringify(args);
|
||||||
|
const cached = cache.get(key);
|
||||||
|
if (cached && Date.now() - cached.time < ttl) {
|
||||||
|
return cached.value;
|
||||||
|
}
|
||||||
|
const result = fn.apply(this, args);
|
||||||
|
cache.set(key, { value: result, time: Date.now() });
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 解析 URL 参数
|
||||||
|
|
||||||
|
```js
|
||||||
|
function parseQuery(url) {
|
||||||
|
const query = url.split('?')[1] || '';
|
||||||
|
return query.split('&').reduce((acc, pair) => {
|
||||||
|
const [key, value] = pair.split('=').map(decodeURIComponent);
|
||||||
|
if (key) {
|
||||||
|
acc[key] = acc[key]
|
||||||
|
? [].concat(acc[key], value)
|
||||||
|
: value;
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用正则
|
||||||
|
function parseQueryRegex(url) {
|
||||||
|
const result = {};
|
||||||
|
url.replace(/[?&]([^=&#]+)=([^&#]*)/g, (_, key, value) => {
|
||||||
|
result[decodeURIComponent(key)] = decodeURIComponent(value);
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 示例: "https://example.com?a=1&b=2&a=3"
|
||||||
|
// => { a: ['1', '3'], b: '2' }
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. DOM 查找最近公共祖先
|
||||||
|
|
||||||
|
```js
|
||||||
|
function findCommonAncestor(node1, node2) {
|
||||||
|
const ancestors = new Set();
|
||||||
|
|
||||||
|
// 收集 node1 的所有祖先
|
||||||
|
let current = node1;
|
||||||
|
while (current) {
|
||||||
|
ancestors.add(current);
|
||||||
|
current = current.parentNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找 node2 的祖先中第一个在 ancestors 中的节点
|
||||||
|
current = node2;
|
||||||
|
while (current) {
|
||||||
|
if (ancestors.has(current)) return current;
|
||||||
|
current = current.parentNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 原生方法(现代浏览器)
|
||||||
|
// node1.compareDocumentPosition(node2)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. 懒加载/无限滚动
|
||||||
|
|
||||||
|
```js
|
||||||
|
// IntersectionObserver 实现懒加载
|
||||||
|
function lazyLoad(selector) {
|
||||||
|
const observer = new IntersectionObserver((entries) => {
|
||||||
|
entries.forEach(entry => {
|
||||||
|
if (entry.isIntersecting) {
|
||||||
|
const img = entry.target;
|
||||||
|
img.src = img.dataset.src;
|
||||||
|
observer.unobserve(img);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
document.querySelectorAll(selector).forEach(img => observer.observe(img));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 无限滚动
|
||||||
|
function infiniteScroll(container, loadMore) {
|
||||||
|
const observer = new IntersectionObserver(([entry]) => {
|
||||||
|
if (entry.isIntersecting) loadMore();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 监听底部占位元素
|
||||||
|
const sentinel = document.createElement('div');
|
||||||
|
container.appendChild(sentinel);
|
||||||
|
observer.observe(sentinel);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. LRU 缓存
|
||||||
|
|
||||||
|
```js
|
||||||
|
class LRUCache {
|
||||||
|
constructor(capacity) {
|
||||||
|
this.capacity = capacity;
|
||||||
|
this.cache = new Map();
|
||||||
|
}
|
||||||
|
|
||||||
|
get(key) {
|
||||||
|
if (!this.cache.has(key)) return -1;
|
||||||
|
const value = this.cache.get(key);
|
||||||
|
this.cache.delete(key);
|
||||||
|
this.cache.set(key, value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
put(key, value) {
|
||||||
|
if (this.cache.has(key)) this.cache.delete(key);
|
||||||
|
this.cache.set(key, value);
|
||||||
|
if (this.cache.size > this.capacity) {
|
||||||
|
this.cache.delete(this.cache.keys().next().value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. 大数相加
|
||||||
|
|
||||||
|
```js
|
||||||
|
function addBigNumbers(a, b) {
|
||||||
|
const maxLen = Math.max(a.length, b.length);
|
||||||
|
a = a.padStart(maxLen, '0');
|
||||||
|
b = b.padStart(maxLen, '0');
|
||||||
|
|
||||||
|
let carry = 0, result = '';
|
||||||
|
for (let i = maxLen - 1; i >= 0; i--) {
|
||||||
|
const sum = +a[i] + +b[i] + carry;
|
||||||
|
result = (sum % 10) + result;
|
||||||
|
carry = Math.floor(sum / 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
return carry ? carry + result : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 示例: addBigNumbers("12345678901234567890", "98765432109876543210")
|
||||||
|
```
|
||||||
245
algorithm/05-framework-algorithms.md
Normal file
245
algorithm/05-framework-algorithms.md
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
# 框架相关算法
|
||||||
|
|
||||||
|
## 1. 虚拟DOM Diff 算法
|
||||||
|
|
||||||
|
### 简化版 Diff 实现
|
||||||
|
|
||||||
|
```js
|
||||||
|
function diff(oldVNode, newVNode) {
|
||||||
|
const patches = [];
|
||||||
|
|
||||||
|
// 节点不存在
|
||||||
|
if (!newVNode) {
|
||||||
|
patches.push({ type: 'REMOVE' });
|
||||||
|
return patches;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 类型不同,替换节点
|
||||||
|
if (typeof oldVNode !== typeof newVNode ||
|
||||||
|
(typeof oldVNode === 'string' && oldVNode !== newVNode) ||
|
||||||
|
oldVNode.tag !== newVNode.tag) {
|
||||||
|
patches.push({ type: 'REPLACE', node: newVNode });
|
||||||
|
return patches;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 比较属性
|
||||||
|
if (newVNode.props) {
|
||||||
|
const propsPatches = diffProps(oldVNode.props || {}, newVNode.props);
|
||||||
|
if (Object.keys(propsPatches).length) {
|
||||||
|
patches.push({ type: 'PROPS', props: propsPatches });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 比较子节点
|
||||||
|
diffChildren(oldVNode.children || [], newVNode.children || [], patches);
|
||||||
|
|
||||||
|
return patches;
|
||||||
|
}
|
||||||
|
|
||||||
|
function diffProps(oldProps, newProps) {
|
||||||
|
const patches = {};
|
||||||
|
// 新增或修改的属性
|
||||||
|
for (const key in newProps) {
|
||||||
|
if (newProps[key] !== oldProps[key]) {
|
||||||
|
patches[key] = newProps[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 删除的属性
|
||||||
|
for (const key in oldProps) {
|
||||||
|
if (!(key in newProps)) {
|
||||||
|
patches[key] = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return patches;
|
||||||
|
}
|
||||||
|
|
||||||
|
function diffChildren(oldChildren, newChildren, patches) {
|
||||||
|
const len = Math.max(oldChildren.length, newChildren.length);
|
||||||
|
for (let i = 0; i < len; i++) {
|
||||||
|
const childPatches = diff(oldChildren[i], newChildren[i]);
|
||||||
|
if (childPatches.length) {
|
||||||
|
patches.push({ type: 'CHILDREN', index: i, patches: childPatches });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Vue 响应式原理
|
||||||
|
|
||||||
|
### Vue 2 (Object.defineProperty)
|
||||||
|
|
||||||
|
```js
|
||||||
|
function observe(obj) {
|
||||||
|
if (typeof obj !== 'object' || obj === null) return;
|
||||||
|
|
||||||
|
Object.keys(obj).forEach(key => {
|
||||||
|
let value = obj[key];
|
||||||
|
const dep = new Set();
|
||||||
|
|
||||||
|
observe(value); // 递归处理嵌套对象
|
||||||
|
|
||||||
|
Object.defineProperty(obj, key, {
|
||||||
|
get() {
|
||||||
|
if (currentEffect) dep.add(currentEffect);
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
set(newVal) {
|
||||||
|
if (newVal === value) return;
|
||||||
|
value = newVal;
|
||||||
|
observe(newVal);
|
||||||
|
dep.forEach(fn => fn());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let currentEffect = null;
|
||||||
|
function watchEffect(fn) {
|
||||||
|
currentEffect = fn;
|
||||||
|
fn();
|
||||||
|
currentEffect = null;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Vue 3 (Proxy)
|
||||||
|
|
||||||
|
```js
|
||||||
|
function reactive(obj) {
|
||||||
|
return new Proxy(obj, {
|
||||||
|
get(target, key, receiver) {
|
||||||
|
track(target, key);
|
||||||
|
const result = Reflect.get(target, key, receiver);
|
||||||
|
return typeof result === 'object' ? reactive(result) : result;
|
||||||
|
},
|
||||||
|
set(target, key, value, receiver) {
|
||||||
|
const result = Reflect.set(target, key, value, receiver);
|
||||||
|
trigger(target, key);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetMap = new WeakMap();
|
||||||
|
let activeEffect = null;
|
||||||
|
|
||||||
|
function track(target, key) {
|
||||||
|
if (!activeEffect) return;
|
||||||
|
let depsMap = targetMap.get(target);
|
||||||
|
if (!depsMap) targetMap.set(target, (depsMap = new Map()));
|
||||||
|
let dep = depsMap.get(key);
|
||||||
|
if (!dep) depsMap.set(key, (dep = new Set()));
|
||||||
|
dep.add(activeEffect);
|
||||||
|
}
|
||||||
|
|
||||||
|
function trigger(target, key) {
|
||||||
|
const depsMap = targetMap.get(target);
|
||||||
|
if (!depsMap) return;
|
||||||
|
depsMap.get(key)?.forEach(effect => effect());
|
||||||
|
}
|
||||||
|
|
||||||
|
function effect(fn) {
|
||||||
|
activeEffect = fn;
|
||||||
|
fn();
|
||||||
|
activeEffect = null;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. React useState 简单模拟
|
||||||
|
|
||||||
|
```js
|
||||||
|
let state = [];
|
||||||
|
let stateIndex = 0;
|
||||||
|
|
||||||
|
function useState(initialValue) {
|
||||||
|
const currentIndex = stateIndex;
|
||||||
|
state[currentIndex] = state[currentIndex] ?? initialValue;
|
||||||
|
|
||||||
|
const setState = (newValue) => {
|
||||||
|
state[currentIndex] = typeof newValue === 'function'
|
||||||
|
? newValue(state[currentIndex])
|
||||||
|
: newValue;
|
||||||
|
render(); // 触发重新渲染
|
||||||
|
};
|
||||||
|
|
||||||
|
stateIndex++;
|
||||||
|
return [state[currentIndex], setState];
|
||||||
|
}
|
||||||
|
|
||||||
|
function render() {
|
||||||
|
stateIndex = 0; // 重置索引
|
||||||
|
// 调用组件函数...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. useEffect 简单模拟
|
||||||
|
|
||||||
|
```js
|
||||||
|
let effectIndex = 0;
|
||||||
|
let effects = [];
|
||||||
|
|
||||||
|
function useEffect(callback, deps) {
|
||||||
|
const currentIndex = effectIndex;
|
||||||
|
const prevDeps = effects[currentIndex]?.deps;
|
||||||
|
|
||||||
|
const hasChanged = !prevDeps ||
|
||||||
|
deps.some((dep, i) => !Object.is(dep, prevDeps[i]));
|
||||||
|
|
||||||
|
if (hasChanged) {
|
||||||
|
// 执行清理函数
|
||||||
|
effects[currentIndex]?.cleanup?.();
|
||||||
|
|
||||||
|
// 延迟执行 effect
|
||||||
|
Promise.resolve().then(() => {
|
||||||
|
const cleanup = callback();
|
||||||
|
effects[currentIndex] = { deps, cleanup };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
effectIndex++;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. 简易 Redux
|
||||||
|
|
||||||
|
```js
|
||||||
|
function createStore(reducer) {
|
||||||
|
let state;
|
||||||
|
const listeners = [];
|
||||||
|
|
||||||
|
const getState = () => state;
|
||||||
|
|
||||||
|
const dispatch = (action) => {
|
||||||
|
state = reducer(state, action);
|
||||||
|
listeners.forEach(listener => listener());
|
||||||
|
};
|
||||||
|
|
||||||
|
const subscribe = (listener) => {
|
||||||
|
listeners.push(listener);
|
||||||
|
return () => {
|
||||||
|
const index = listeners.indexOf(listener);
|
||||||
|
listeners.splice(index, 1);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
dispatch({ type: '@@INIT' }); // 初始化 state
|
||||||
|
|
||||||
|
return { getState, dispatch, subscribe };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用示例
|
||||||
|
const reducer = (state = { count: 0 }, action) => {
|
||||||
|
switch (action.type) {
|
||||||
|
case 'INCREMENT': return { count: state.count + 1 };
|
||||||
|
case 'DECREMENT': return { count: state.count - 1 };
|
||||||
|
default: return state;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
269
algorithm/06-performance.md
Normal file
269
algorithm/06-performance.md
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
# 性能与优化
|
||||||
|
|
||||||
|
## 1. 图片懒加载
|
||||||
|
|
||||||
|
### IntersectionObserver 方案(推荐)
|
||||||
|
|
||||||
|
```js
|
||||||
|
function lazyLoadImages() {
|
||||||
|
const observer = new IntersectionObserver((entries, observer) => {
|
||||||
|
entries.forEach(entry => {
|
||||||
|
if (entry.isIntersecting) {
|
||||||
|
const img = entry.target;
|
||||||
|
img.src = img.dataset.src;
|
||||||
|
img.classList.remove('lazy');
|
||||||
|
observer.unobserve(img);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, {
|
||||||
|
rootMargin: '50px' // 提前50px开始加载
|
||||||
|
});
|
||||||
|
|
||||||
|
document.querySelectorAll('img.lazy').forEach(img => observer.observe(img));
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTML: <img class="lazy" data-src="real-image.jpg" src="placeholder.jpg" />
|
||||||
|
```
|
||||||
|
|
||||||
|
### 传统滚动监听方案
|
||||||
|
|
||||||
|
```js
|
||||||
|
function lazyLoadScroll() {
|
||||||
|
const lazyImages = document.querySelectorAll('img.lazy');
|
||||||
|
|
||||||
|
const loadImage = throttle(() => {
|
||||||
|
lazyImages.forEach(img => {
|
||||||
|
if (img.getBoundingClientRect().top < window.innerHeight + 100) {
|
||||||
|
img.src = img.dataset.src;
|
||||||
|
img.classList.remove('lazy');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, 200);
|
||||||
|
|
||||||
|
window.addEventListener('scroll', loadImage);
|
||||||
|
loadImage(); // 初始加载
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 虚拟列表(大数据渲染优化)
|
||||||
|
|
||||||
|
```js
|
||||||
|
class VirtualList {
|
||||||
|
constructor({ container, itemHeight, totalItems, renderItem }) {
|
||||||
|
this.container = container;
|
||||||
|
this.itemHeight = itemHeight;
|
||||||
|
this.totalItems = totalItems;
|
||||||
|
this.renderItem = renderItem;
|
||||||
|
|
||||||
|
this.visibleCount = Math.ceil(container.clientHeight / itemHeight) + 2;
|
||||||
|
this.startIndex = 0;
|
||||||
|
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
// 创建容器结构
|
||||||
|
this.container.style.overflow = 'auto';
|
||||||
|
this.container.style.position = 'relative';
|
||||||
|
|
||||||
|
// 占位元素,撑开滚动高度
|
||||||
|
this.phantom = document.createElement('div');
|
||||||
|
this.phantom.style.height = `${this.totalItems * this.itemHeight}px`;
|
||||||
|
|
||||||
|
// 实际渲染的列表容器
|
||||||
|
this.content = document.createElement('div');
|
||||||
|
this.content.style.position = 'absolute';
|
||||||
|
this.content.style.top = '0';
|
||||||
|
this.content.style.width = '100%';
|
||||||
|
|
||||||
|
this.container.appendChild(this.phantom);
|
||||||
|
this.container.appendChild(this.content);
|
||||||
|
|
||||||
|
this.container.addEventListener('scroll', throttle(() => this.onScroll(), 16));
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
onScroll() {
|
||||||
|
const scrollTop = this.container.scrollTop;
|
||||||
|
this.startIndex = Math.floor(scrollTop / this.itemHeight);
|
||||||
|
this.content.style.transform = `translateY(${this.startIndex * this.itemHeight}px)`;
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const fragment = document.createDocumentFragment();
|
||||||
|
const endIndex = Math.min(this.startIndex + this.visibleCount, this.totalItems);
|
||||||
|
|
||||||
|
for (let i = this.startIndex; i < endIndex; i++) {
|
||||||
|
const item = this.renderItem(i);
|
||||||
|
item.style.height = `${this.itemHeight}px`;
|
||||||
|
fragment.appendChild(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.content.innerHTML = '';
|
||||||
|
this.content.appendChild(fragment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用示例
|
||||||
|
new VirtualList({
|
||||||
|
container: document.getElementById('list'),
|
||||||
|
itemHeight: 50,
|
||||||
|
totalItems: 10000,
|
||||||
|
renderItem: (index) => {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.textContent = `Item ${index}`;
|
||||||
|
return div;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 函数节流在滚动事件中的应用
|
||||||
|
|
||||||
|
```js
|
||||||
|
// 节流函数
|
||||||
|
function throttle(fn, delay) {
|
||||||
|
let lastTime = 0;
|
||||||
|
let timer = null;
|
||||||
|
|
||||||
|
return function(...args) {
|
||||||
|
const now = Date.now();
|
||||||
|
const remaining = delay - (now - lastTime);
|
||||||
|
|
||||||
|
if (remaining <= 0) {
|
||||||
|
if (timer) {
|
||||||
|
clearTimeout(timer);
|
||||||
|
timer = null;
|
||||||
|
}
|
||||||
|
lastTime = now;
|
||||||
|
fn.apply(this, args);
|
||||||
|
} else if (!timer) {
|
||||||
|
timer = setTimeout(() => {
|
||||||
|
lastTime = Date.now();
|
||||||
|
timer = null;
|
||||||
|
fn.apply(this, args);
|
||||||
|
}, remaining);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 应用场景
|
||||||
|
window.addEventListener('scroll', throttle(() => {
|
||||||
|
console.log('滚动位置:', window.scrollY);
|
||||||
|
// 吸顶效果、返回顶部按钮显示等
|
||||||
|
}, 100));
|
||||||
|
|
||||||
|
window.addEventListener('resize', throttle(() => {
|
||||||
|
console.log('窗口大小:', window.innerWidth, window.innerHeight);
|
||||||
|
}, 200));
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. requestAnimationFrame 优化动画
|
||||||
|
|
||||||
|
```js
|
||||||
|
// 使用 rAF 实现平滑滚动
|
||||||
|
function smoothScrollTo(target, duration = 500) {
|
||||||
|
const start = window.scrollY;
|
||||||
|
const distance = target - start;
|
||||||
|
const startTime = performance.now();
|
||||||
|
|
||||||
|
function step(currentTime) {
|
||||||
|
const elapsed = currentTime - startTime;
|
||||||
|
const progress = Math.min(elapsed / duration, 1);
|
||||||
|
|
||||||
|
// 缓动函数
|
||||||
|
const easeProgress = 1 - Math.pow(1 - progress, 3);
|
||||||
|
window.scrollTo(0, start + distance * easeProgress);
|
||||||
|
|
||||||
|
if (progress < 1) {
|
||||||
|
requestAnimationFrame(step);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
requestAnimationFrame(step);
|
||||||
|
}
|
||||||
|
|
||||||
|
// rAF 节流
|
||||||
|
function rafThrottle(fn) {
|
||||||
|
let ticking = false;
|
||||||
|
return function(...args) {
|
||||||
|
if (!ticking) {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
fn.apply(this, args);
|
||||||
|
ticking = false;
|
||||||
|
});
|
||||||
|
ticking = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Web Worker 处理耗时任务
|
||||||
|
|
||||||
|
```js
|
||||||
|
// main.js
|
||||||
|
const worker = new Worker('worker.js');
|
||||||
|
|
||||||
|
worker.postMessage({ type: 'heavyTask', data: largeArray });
|
||||||
|
|
||||||
|
worker.onmessage = (e) => {
|
||||||
|
console.log('处理结果:', e.data);
|
||||||
|
};
|
||||||
|
|
||||||
|
// worker.js
|
||||||
|
self.onmessage = (e) => {
|
||||||
|
if (e.data.type === 'heavyTask') {
|
||||||
|
const result = e.data.data.map(item => /* 耗时计算 */);
|
||||||
|
self.postMessage(result);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. 时间切片(避免长任务阻塞)
|
||||||
|
|
||||||
|
```js
|
||||||
|
async function timeSlice(tasks, chunkSize = 5) {
|
||||||
|
const results = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < tasks.length; i += chunkSize) {
|
||||||
|
const chunk = tasks.slice(i, i + chunkSize);
|
||||||
|
|
||||||
|
// 处理一批任务
|
||||||
|
for (const task of chunk) {
|
||||||
|
results.push(task());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 让出主线程
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 0));
|
||||||
|
// 或使用 requestIdleCallback
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用 requestIdleCallback
|
||||||
|
function processInIdle(tasks) {
|
||||||
|
let index = 0;
|
||||||
|
|
||||||
|
function work(deadline) {
|
||||||
|
while (index < tasks.length && deadline.timeRemaining() > 0) {
|
||||||
|
tasks[index++]();
|
||||||
|
}
|
||||||
|
if (index < tasks.length) {
|
||||||
|
requestIdleCallback(work);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
requestIdleCallback(work);
|
||||||
|
}
|
||||||
|
```
|
||||||
23
algorithm/README.md
Normal file
23
algorithm/README.md
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# 京东前端面试算法题
|
||||||
|
|
||||||
|
> 难度:中等 | 重点:代码实现能力、逻辑清晰度、实际应用
|
||||||
|
|
||||||
|
## 📁 目录
|
||||||
|
|
||||||
|
| 分类 | 文件 |
|
||||||
|
|------|------|
|
||||||
|
| JS基础与手写实现 | [01-js-basics.md](./01-js-basics.md) |
|
||||||
|
| 数组与字符串操作 | [02-array-string.md](./02-array-string.md) |
|
||||||
|
| 数据结构与算法 | [03-data-structure.md](./03-data-structure.md) |
|
||||||
|
| 实际场景与逻辑题 | [04-practical-scenarios.md](./04-practical-scenarios.md) |
|
||||||
|
| 框架相关算法 | [05-framework-algorithms.md](./05-framework-algorithms.md) |
|
||||||
|
| 性能与优化 | [06-performance.md](./06-performance.md) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ 备考建议
|
||||||
|
|
||||||
|
1. **重视基础**:闭包、原型链、异步核心概念必须扎实
|
||||||
|
2. **动手实现**:多手写常见工具函数(深拷贝、Promise等)
|
||||||
|
3. **刷题方向**:LeetCode 简单到中等,重点:字符串、数组、链表
|
||||||
|
4. **理解原理**:框架算法需理解思想,不必死记硬背
|
||||||
Loading…
x
Reference in New Issue
Block a user