1.手写实现new
function myNew(constructor, ...args) {
// 步骤1: 创建一个空对象,并且这个对象继承自构造函数的prototype属性。
const obj = Object.create(constructor.prototype);
// 步骤2: 将构造函数的作用域赋给新对象(因此this就指向了这个新对象)。
const result = constructor.apply(obj, args);
// 步骤3: 如果构造函数返回了一个对象,则返回这个对象;否则,返回刚创建的新对象。
return result instanceof Object ? result : obj;
}
2.手写 instanceof 方法
function myInstanceof(left, right) {
// 获取对象的原型
let proto = Object.getPrototypeOf(left);
// 获取构造函数的 prototype 对象
const prototype = right.prototype;
// 遍历原型链
while (proto) {
// 检查构造函数的 prototype 是否出现在实例对象的原型链上
if (proto === prototype) {
return true;
}
// 沿原型链向上移动
proto = Object.getPrototypeOf(proto);
}
// 如果没有找到,返回 false
return false;
}
3.手写promise
class MyPromise {
constructor(executor) {
this.state = 'pending'; // Promise的初始状态
this.value = undefined; // 成功时的值
this.reason = undefined; // 失败时的原因
// 成功
const resolve = (value) => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
this.onFulfilledCallbacks.forEach(fn => fn());
}
};
// 失败
const reject = (reason) => {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn());
}
};
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
// 存储成功和失败的回调
onFulfilledCallbacks = [];
onRejectedCallbacks = [];
then(onFulfilled, onRejected) {
// 在then中根据状态执行相应操作
if (this.state === 'fulfilled') {
onFulfilled(this.value);
}
if (this.state === 'rejected') {
onRejected(this.reason);
}
// 处理异步
if (this.state === 'pending') {
this.onFulfilledCallbacks.push(() => {
onFulfilled(this.value);
});
this.onRejectedCallbacks.push(() => {
onRejected(this.reason);
});
}
}
}
4. 手写 Promise.all
function promiseAll(promises) {
return new Promise((resolve, reject) => {
if (!Array.isArray(promises)) {
return reject(new TypeError('arguments must be an array'));
}
let resolvedCounter = 0;
let promiseNum = promises.length;
let resolvedValues = new Array(promiseNum);
for (let i = 0; i < promiseNum; i++) {
// 直接调用 Promise.resolve 确保每个元素都被当作 Promise 对待
Promise.resolve(promises[i]).then(value => {
resolvedCounter++;
resolvedValues[i] = value;
// 当所有元素都被解决后,Promise.all 返回的 promise 状态变为 resolved
if (resolvedCounter === promiseNum) {
return resolve(resolvedValues);
}
}, reason => {
// 任何一个元素被 reject 时,Promise.all 返回的 promise 状态变为 rejected
return reject(reason);
});
}
// 如果输入的是空数组,直接解决
if (promiseNum === 0) {
resolve(resolvedValues);
}
});
}
5.手写 Promise.race
function promiseRace(promises) {
return new Promise((resolve, reject) => {
// 检查输入是否为数组
if (!Array.isArray(promises)) {
return reject(new TypeError('arguments must be an array'));
}
// 遍历所有的 Promise 对象
for (let i = 0; i < promises.length; i++) {
// 对每个 Promise 使用 Promise.resolve() 包装以确保它们是 Promise 对象
// 然后使用 .then 方法订阅其解决或拒绝状态
Promise.resolve(promises[i]).then(resolve, reject);
}
});
}
6.手写防抖函数
function debounce(fn, delay) {
let timeoutID = null;
return function(...args) {
// 如果此前已经设置了延时调用,则取消之前的调用
if (timeoutID) {
clearTimeout(timeoutID);
}
// 设置一个新的延时调用
timeoutID = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}
7.手写节流函数
function throttle(fn, interval) {
let lastTime = 0;
return function(...args) {
const now = new Date().getTime();
if (now - lastTime > interval) {
lastTime = now;
fn.apply(this, args);
}
};
}
8.手写类型判断函数
function getType(value) {
// 首先排除 null 的情况
if (value === null) {
return "Null";
}
// 排除非对象类型的情况
else if (typeof value !== "object") {
return typeof value;
}
// 对于 Array, Function, RegExp 等,使用 Object.prototype.toString
else {
return Object.prototype.toString.call(value).slice(8, -1);
}
}
9.手写 call 函数
Function.prototype.myCall = function(context = window, ...args) {
if (typeof this !== 'function') {
throw new TypeError('Type error');
}
// 避免与现有属性冲突,使用 Symbol
const fnSymbol = Symbol();
// this 指向调用 call 的函数
context[fnSymbol] = this;
// 执行函数
const result = context[fnSymbol](...args);
// 删除属性
delete context[fnSymbol];
// 返回执行结果
return result;
}
10.手写 apply 函数
Function.prototype.myApply = function(context = window, args = []) {
if (this === Function.prototype) {
return undefined; // 防止 Function.prototype.myApply() 直接调用
}
context = context || window;
const fnSymbol = Symbol(); // 使用 Symbol 确保唯一性
context[fnSymbol] = this; // 将调用函数设为对象的方法
let result;
// 判断 args 是否为有效数组
if (Array.isArray(args)) {
result = context[fnSymbol](...args); // 执行函数
} else {
throw new TypeError('CreateListFromArrayLike called on non-object');
}
delete context[fnSymbol]; // 删除刚才赋值的属性
return result; // 返回执行结果
};
11.手写 bind 函数
Function.prototype.myBind = function(context, ...args) {
if (this === Function.prototype) {
throw new TypeError('Error');
}
const _this = this; // 保存当前函数的引用
return function F(...bindArgs) {
// 处理函数使用 new 操作符调用的情况
if (this instanceof F) {
return new _this(...args, ...bindArgs);
}
// ***** context
return _this.apply(context, [...args, ...bindArgs]);
};
};
12.函数柯里化的实现
重点返回两层函数
一层记录
一层判断执行
function curry(fn) {
// 检查需要的参数数量 .length
const arity = fn.length;
// `nextCurried` 函数负责收集参数,直到参数数量足够,然后执行 `fn`
function nextCurried(prevArgs) {
return function(arg) {
// 合并之前收集的参数和当前参数
const args = [...prevArgs, arg];
// 如果参数数量足够,则执行 `fn`
if (args.length >= arity) {
return fn(...args);
}
// 否则,返回一个新的柯里化函数,继续收集参数
else {
return nextCurried(args);
}
};
}
// 开始柯里化过程,初始参数为空
return nextCurried([]);
}
13.实现深拷贝
function deepCopy(obj, hash = new WeakMap()) {
// 处理 null、undefined 和基本数据类型
if (obj === null || typeof obj !== 'object') return obj;
// 处理日期类型
if (obj instanceof Date) return new Date(obj);
// 处理 Map 类型
if (obj instanceof Map) return new Map([...obj]);
// 避免循环引用
if (hash.has(obj)) return hash.get(obj);
// 处理数组和对象,使用 Array.isArray() 检查是否为数组
const result = Array.isArray(obj) ? [] : {};
// 保存拷贝的对象,用于处理循环引用
hash.set(obj, result);
// 递归拷贝所有属性
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
result[key] = deepCopy(obj[key], hash);
}
}
// 返回拷贝后的对象
return result;
}
15.实现数组的扁平化
let arr = [1, [2, [3, 4, 5]]];
function flatten(arr) {
let result = [];
for(let i = 0; i < arr.length; i++) {
if(Array.isArray(arr[i])) {
result = result.concat(flatten(arr[i]));
} else {
result.push(arr[i]);
}
}
return result;
}
flatten(arr); // [1, 2, 3, 4,5]
16.发布订阅模式
class EventEmitter {
constructor() {
this.events = {};
}
// 实现订阅
on(type, callBack) {
if (!this.events[type]) {
this.events[type] = [callBack];
} else {
this.events[type].push(callBack);
}
}
// 删除订阅
off(type, callBack) {
if (!this.events[type]) return;
this.events[type] = this.events[type].filter((item) => {
return item !== callBack;
});
}
// 只执行一次订阅事件
once(type, callBack) {
function fn() {
callBack();
this.off(type, fn);
}
this.on(type, fn);
}
// 触发事件
emit(type, ...rest) {
this.events[type] &&
this.events[type].forEach((fn) => fn.apply(this, rest));
}
}
// 使用如下
// const event = new EventEmitter();
// const handle = (...rest) => {
// console.log(rest);
// };
// event.on("click", handle);
// event.emit("click", 1, 2, 3, 4);
// event.off("click", handle);
// event.emit("click", 1, 2);
// event.once("dbClick", () => {
// console.log(123456);
// });
// event.emit("dbClick");
// event.emit("dbClick");
17.数组去重
function uniqueArr(arr) {
return [...new Set(arr)];
}
18.数组扁平化
function flatter(arr) {
if (!arr.length) return;
return arr.reduce(
(pre, cur) =>
Array.isArray(cur) ? [...pre, ...flatter(cur)] : [...pre, cur],
[]
);
}
function flatter(arr) {
if (!arr.length) return;
while (arr.some((item) => Array.isArray(item))) {
arr = [].concat(...arr);
}
return arr;
}
19.寄生组合继承
function Parent(name) {
this.name = name;
this.say = () => {
console.log(111);
};
}
Parent.prototype.play = () => {
console.log(222);
};
function Children(name) {
Parent.call(this);
this.name = name;
}
Children.prototype = Object.create(Parent.prototype);
Children.prototype.constructor = Children;
// let child = new Children("111");
// // console.log(child.name);
// // child.say();
// // child.play();
20.实现有并行限制的 Promise 调度器
class Scheduler {
constructor(limit) {
this.queue = [];
this.maxCount = limit;
this.runCounts = 0;
}
add(time, order) {
const promiseCreator = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(order);
resolve();
}, time);
});
};
this.queue.push(promiseCreator);
}
taskStart() {
for (let i = 0; i < this.maxCount; i++) {
this.request();
}
}
request() {
if (!this.queue || !this.queue.length || this.runCounts >= this.maxCount) {
return;
}
this.runCounts++;
this.queue
.shift()()
.then(() => {
this.runCounts--;
this.request();
});
}
}
const scheduler = new Scheduler(2);
const addTask = (time, order) => {
scheduler.add(time, order);
};
addTask(1000, "1");
addTask(500, "2");
addTask(300, "3");
addTask(400, "4");
scheduler.taskStart();