206 lines
4.5 KiB
Markdown
206 lines
4.5 KiB
Markdown
# 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);
|
|
}
|
|
}
|
|
```
|