手写call
Function.prototype.myCall = function (context: any, ...args: any[]) {
if (typeof this !== 'function') {
return;
}
if (context === null || context === undefined) {
// 指定为 null 和 undefined 的 this 值会自动指向全局对象(浏览器中为window)
context = window
} else {
context = Object(context) // 值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的实例对象
}
// 生成唯一的fn
const fn = Symbol('myCall');
// contextObj增加fn属性为this,this就是要调用的函数。其实也就是为了后面调用时改变函数的this指向,因为函数的this是谁调用指向谁
contextObj[fn] = this;
// 调用函数
const result = contextObj[fn](...args);
// 删除fn属性
delete contextObj[fn];
// 返回结果
return result;
};
// 测试
const test = {
name: "xxx",
hello: function () {
console.log(`hello,${this.name}!`);
},
add: function (a, b) {
return a + b;
},
};
const obj = { name: "world" };
test.hello.myCall(obj); //hello,world!
test.hello.call(obj);//hello,world!
console.log(test.add.myCall(null, 1, 2));//3
console.log(test.add.call(null, 1, 2));//3
手写apply
Function.prototype.myApply = function (context: any, argsArr: any[]) {
if (typeof this !== 'function') {
return;
}
if (!argsArr || !Array.isArray(argsArr)) {
return;
}
if (context === null || context === undefined) {
// 指定为 null 和 undefined 的 this 值会自动指向全局对象(浏览器中为window)
context = window
} else {
context = Object(context) // 值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的实例对象
}
// 生成唯一的fn
const fn = Symbol('myApply');
// contextObj增加fn属性为this,this就是要调用的函数。其实也就是为了后面调用时改变函数的this指向,因为函数的this是谁调用指向谁
contextObj[fn] = this;
// 调用函数
const result = contextObj[fn](...argsArr);
// 删除fn属性
delete contextObj[fn];
// 返回结果
return result;
};
// 测试
const test = {
name: "xxx",
hello: function () {
console.log(`hello,${this.name}!`);
},
};
const obj = { name: "world" };
test.hello.myApply(obj); //hello,world!
test.hello.apply(obj); //hello,world!
const arr = [2,3,6,5,1,7,9,5,0]
console.log(Math.max.myApply(null,arr));//9
console.log(Math.max.apply(null,arr));//9
手写bind
Function.prototype.myBind = function (context, ...args) {
if (typeof this !== 'function') {
throw 'fn must be a function';
}
// 获取当前上下文
const conttextObj = context || globalThis;
// 保存this,也就是最后要调用的函数
const _this = this;
// 返回一个函数作为最后调用的函数
return function fn(...arguments) {
// 判断是不是把函数new了(new调用时this指向实例对象)
if (this instanceof fn) {
return new _this(...args, ...arguments);
}
return _this.apply(conttextObj, [...args, ...arguments]);
};
};
Function.prototype.myBind = function (context, ...args) {
if (typeof this !== 'function') {
throw 'fn must be a function';
}
// 获取当前上下文
const conttextObj = context || globalThis;
// 生成唯一的fn
const fn = Symbol('myBind');
// 保存this,也就是最后要调用的函数
conttextObj[fn] = this;
// 返回一个函数作为最后调用的函数
return function fn(...arguments) {
// 判断是不是把函数new了(new调用时this指向实例对象)
if (this instanceof fn) {
return new conttextObj[fn](...args, ...arguments);
}
return conttextObj[fn](...args, ...arguments);
};
};
// 测试
const test = {
name: "xxx",
hello: function (a,b,c) {
console.log(`hello,${this.name}!`,a+b+c);
},
};
const obj = { name: "world" };
let hello1 = test.hello.myBind(obj,1);
let hello2 = test.hello.bind(obj,1);
hello1(2,3)//hello,world! 6
hello2(2,3)//hello,world! 6
console.log(new hello1(2,3));
//hello,undefined! 6
// hello {}
console.log(new hello2(2,3));
//hello,undefined! 6
// hello {}
手写new
function myNew(fn, ...args) {
if (typeof fn !== 'function') {
throw 'fn must be a function';
}
// 创建一个js对象,并且将该js对象的隐式原型指向构造函数的原型
const obj = Object.create(fn.prototype);
// 执行构造函数,并且还更新了this指向
const result = fn.apply(obj, args);
// 判断函数是否返回有返回对象,如果有就返回该函数的返回对象,否则返回新对象
return result && typeof result === 'object' ? result : obj;
}
function myNew(fn, ...args) {
if (typeof fn !== 'function') {
throw 'fn must be a function';
}
// 创建一个j空对象
const obj = Object.create(null);
// 将对象的原型设为fn的原型
Object.setPrototypeOf(obj, fn.prototype);
// 执行构造函数,并且还更新了this指向
const result = fn.apply(obj, args);
// 判断函数是否返回有返回对象,如果有就返回该函数的返回对象,否则返回新对象
return result && typeof result === 'object' ? result : obj;
}
function myNew(fn, ...args) {
if (typeof fn !== 'function') {
throw 'fn must be a function';
}
// 1.创建一个新对象
const obj = {};
// 2.新对象原型指向构造函数原型对象
obj.__proto__ = fn.prototype;
// 3.将构建函数的this指向新对象
const result = fn.apply(obj, args);
// 4.根据返回值判断
return result instanceof Object ? result : obj;
}
手写instanceof
// instanceof 运算符用于判断构造函数的 prototype 属性是否出现在对象的原型链中的任何位置。
function myInstanceof(obj, ctor) {
let proto = Object.getPrototypeOf(obj);
const ctorProto = ctor.prototype;
while (true) {
if (!proto) {
return false;
}
if (proto === ctorProto) {
return true;
}
proto = Object.getPrototypeOf(proto);
}
}
手写Promise
class MyPromise {
static PENDING = 'pending';
static FULFILLED = 'fulfilled';
static REJECTED = 'rejected';
constructor(executor) {
this.PromiseState = MyPromise.PENDING;
this.PromiseResult = null;
this.onFulfilledCallbacks = []; // 保存成功回调
this.onRejectedCallbacks = []; // 保存失败回调
// 这里是第一个坑,resolve和rejiect是在外部调用的,所以这里需要bind一下,否则this指向不对
// 这里有第二个坑,如果回调里面发生错误,是会触发then方法的第二个参数的,所以这里需要try catch
try {
executor(this.resolve.bind(this), this.reject.bind(this));
} catch (error) {
// 因为这里的reject是马上调用,this就是指向的MyPromise的实例,所以不用bind
this.reject(error);
}
}
// 定义resolve方法
resolve(value) {
if (this.PromiseState === MyPromise.PENDING) {
// 状态变为
this.PromiseState = MyPromise.FULFILLED;
// 保存结果
this.PromiseResult = value;
this.onFulfilledCallbacks.forEach((callback) => {
callback(result);
});
}
}
// 定义rejiect方法
reject(reason) {
if (this.PromiseState === MyPromise.PENDING) {
// 状态变为
this.PromiseState = MyPromise.REJECTED;
// 保存结果
this.PromiseResult = value;
}
}
// 定义then方法
then(onFulfilled, onRejected) {
// 第三个坑,Promise 规范如果 onFulfilled 和 onRejected 不是函数,就忽略他们。所谓“忽略”并不是什么都不干,对于onFulfilled来说“忽略”就是将value原封不动的返回,对于onRejected来说就是返回reason,onRejected因为是错误分支,我们返回reason应该throw一个Error:
if (this.PromiseState === MyPromise.FULFILLED) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (value) => value;
// 如果状态为fulfilled,执行onFulfilled
// 第四个坑,需要异步执行,因为then是需要在最后执行的
setTimeout(() => {
onFulfilled(this.PromiseResult);
});
}
if (this.PromiseState === MyPromise.REJECTED) {
onRejected =
typeof onRejected === 'function'
? onRejected
: (reason) => {
throw reason;
};
// 如果状态为rejected,执行onRejected
setTimeout(() => {
onRejected(this.PromiseResult);
});
}
// 第五个坑,调用then的时候,状态可能还是pending,导致无法执行then,
if (this.PromiseState === MyPromise.PENDING) {
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
onFulfilled(this.PromiseResult);
});
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
onRejected(this.PromiseResult);
});
});
}
}
}
手写Promise 2
class MyPromise {
static PENDING = 'pending';
static FULFILLED = 'fulfilled';
static REJECTED = 'rejected';
constructor(executor) {
this.state = MyPromise.PENDING;
this.value = undefined;
this.reason = undefined;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = (value) => {
if (this.state === MyPromise.PENDING) { // fulfilled状态的上一种状态只能是 pending,状态一经变更就不在逆转
this.state = MyPromise.FULFILLED;
this.value = value; // 保存resolve的参数,留作then中使用
this.onFulfilledCallbacks.forEach(callback => callback(value)); // then中的回调之在此处已经调用,并接受了参数
}
};
const reject = (reason) => {
if (this.state === MyPromise.PENDING) { // 同上
this.state = MyPromise.REJECTED;
this.reason = reason; // 保存reject的参数,留作then中使用
this.onRejectedCallbacks.forEach(callback => callback(reason));
}
};
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
then(onFulfilled, onRejected) {
// 判断传入 then 中的参数是否为函数类型,如果是那顺利执行,否则我们人为写入一个函数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason };
// then的执行结果要返回一个新的promise
return new MyPromise((resolve, reject) => {
if (this.state === MyPromise.FULFILLED) { // 调用then的Promise对象状态已经变更为 fulfilled
setTimeout(() => {
try{
const result = onFulfilled(this.value); // then自行调用自己的回调函数,接收这次then回调函数里面return出来的值
resolve(result); // 并把下一个then的状态改为fulfilled,把下次then的回调函数里面的参数保存好
}catch(error){
reject(error)
}
});
}
if (this.state === MyPromise.REJECTED) {
setTimeout(() => {
try{
const result = onRejected(this.reason)
resolve(result);
}catch(error){
reject(error)
}
});
}
if (this.state === MyPromise.PENDING) { // 调用then的Promise对象状态没有变更,则缓存then中的回调
this.onFulfilledCallbacks.push(value => {
setTimeout(() => {
try{
const result = onFulfilled(value); //if判断是不是promise,这里可能return出一个promise
resolve(result);
}catch(error){
reject(error)
}
});
});
this.onRejectedCallbacks.push(reason => {
setTimeout(() => {
try{
const result = onRejected(reason);
reject(result);
}catch(error){
reject(error)
}
});
});
}
});
}
}
手写Promise.all
// 全部成功就resolve全部的成功结果,一个失败就rejiect失败原因
// Promise.all,所有成功就返回所有结果的数组,有一个失败就返回这个失败的原因
Promise.all = function (promises) {
if (!Array.isArray(promises)) {
throw new TypeError('argument must be a array');
}
return new Promise((resolve, reject) => {
const resolvedResult = []; // resolved的结果
let resolvedCounter = 0; // resolved的结果的计数
const promiseCount = promises.length;
promises.forEach((promise, index) => {
// 执行每个promise的结果
Promise.resolve(promise).then(
(value) => {
// 如果是成功的结果,保存
resolvedResult[index] = value;
// 成功的数量加1
resolvedCounter++;
// 如果成功的数量的数目等于promise的数量,则全部执行完毕
if (resolvedCounter === promiseCount) {
resolve(resolvedResult);
}
},
// 一旦有一个失败,马上执行rejiect,Promise.all的结果就是rejected
(reason) => {
reject(reason);
},
);
});
});
};
手写Promise.race
// 一个成功就马上resolve 一个失败马上就reject
Promise.race = function (promises) {
if (!Array.isArray(promises)) {
throw new TypeError('argument must be a array');
}
return new Promise((resolve, reject) => {
promises.forEach((promise) => {
// 执行每个promise的结果
// 返回第一个成功或者失败的结果
// 这里为什么这样写,因为then的回调就是resolve和reject,所以才这样写
Promise.resolve(promise).then(resolve, reject);
// 也可以像下面这样写
Promise.resolve(promise).then(
(value) => {
resolve(value);
},
(error) => {
reject(error);
},
);
});
});
};
手写Promise.allSrttled
全部都有了结果,不管是成功还是失败,全部的结果都resolve出去,allSettled永远不会reject
Promise.allSettled = function (promises) {
if (!Array.isArray(promises)) {
throw new TypeError('argument must be a array');
}
return new Promise((resolve, reject) => {
const promiseResult = []; // resolved和reject的结果
const promiseCount = promises.length;
promises.forEach((promise, index) => {
// 执行每个promise的结果
Promise.resolve(promise).then(
(value) => {
// 如果是成功的结果,保存
promiseResult[index] = value;
// 全部结果执行完,返回resolve结果,allSettled永远不会reject
if (promiseResult.length === promiseCount) {
resolve(promiseResult);
}
},
(reason) => {
// 如果是失败的结果,保存
promiseResult[index] = reason;
// 全部结果执行完,返回resolve结果,allSettled永远不会reject
if (promiseResult.length === promiseCount) {
resolve(promiseResult);
}
},
);
});
});
};
手写Promise.any
// 一个成功就resolve成功,全部失败才reject所有失败
Promise.any = function (promises) {
if (!Array.isArray(promises)) {
throw new TypeError('argument must be a array');
}
return new Promise((resolve, reject) => {
if (!promises.length) {
reject(new AggregateError('All promises were rejected'));
}
const rejectedResult = []; // reject的结果
const promiseCount = promises.length;
promises.forEach((promise, index) => {
// 执行每个promise的结果
Promise.resolve(promise).then(resolve, (reason) => {
// 如果是失败的结果,保存
rejectedResult[index] = reason;
// 全部结果执行完,返回resolve结果,allSettled永远不会reject
if (rejectedResult.length === promiseCount) {
reject(new AggregateError(rejectedResult));
}
});
});
});
};
简单的双向绑定
const input = document.getElementById('input');
const span = document.getElementById('span')
let obj = {};
input.onchange = function inputChange(e) {
obj.text = e.target.value
}
Object.defineProperty(obj, 'text', {
configurable: true,
enumerable: true,
get() {
return obj.text;
},
set(newVal) {
span.innerText = newVal
}
})
const input = document.getElementById('input');
const p = document.getElementById('p');
const obj = {};
const newObj = new Proxy(obj, {
get: function(target, key, receiver) {
console.log(`getting ${key}!`);
return Reflect.get(target, key, receiver);
},
set: function(target, key, value, receiver) {
console.log(target, key, value, receiver);
if (key === 'text') {
input.value = value;
p.innerHTML = value;
}
return Reflect.set(target, key, value, receiver);
},
});
input.addEventListener('keyup', function(e) {
newObj.text = e.target.value;
});
手写async 函数
// 定义了一个promise,用来模拟异步请求,作用是传入参数++
function getNum(num){
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(num+1)
}, 1000)
})
}
//自动执行器,如果一个Generator函数没有执行完,则递归调用
function asyncFun(func){
var gen = func();
function next(data){
var result = gen.next(data);
if (result.done) return result.value;
result.value.then(function(data){
next(data);
});
}
next();
}
// 所需要执行的Generator函数,内部的数据在执行完成一步的promise之后,再调用下一步
var func = function* (){
var f1 = yield getNum(1);
var f2 = yield getNum(f1);
console.log(f2) ;
};
asyncFun(func);
手写Object.create
function create(obj) {
function F() {}
F.prototype = obj;
return new F();
}
手写数组的flat
function _flat(arr, depth) {
if(!Array.isArray(arr) || depth <= 0) {
return arr;
}
return arr.reduce((prev, cur) => {
if (Array.isArray(cur)) {
return prev.concat(_flat(cur, depth - 1))
} else {
return prev.concat(cur);
}
}, []);
}
手写数组的map
Array.prototype.myMap = function (fn) {
if (typeof fn !== 'function') {
throw Error('参数必须是一个函数');
}
const res = [];
for (let i = 0; i < this.length; i++) {
res.push(fn(this[i], i, this));
}
};
手写数组的filter
Array.prototype.myFilter = function (fn) {
if (typeof fn !== 'function') {
throw Error('参数必须是一个函数');
}
const res = [];
for (let i = 0; i < this.length; i++) {
fn(this[i]) && res.push(fn(this[i], i, this));
}
};
手写数组forEach
Array.prototype.myForEach = function (fn, context) {
if (typeof fn !== 'function') {
throw Error('参数必须是一个函数');
}
for (let i = 0; i < this.length; i++) {
fn.call(context, this[i], i, this);
}
};
手写数组reduce
Array.prototype.myReduce = function (fn, initValue) {
if (typeof fn !== 'function') {
throw Error('参数必须是一个函数');
}
let res = initValue || this[0];
const start = initValue ? 0 : 1;
for (let i = start; i < this.length; i++) {
res = fn(res, this[i], i, this);
}
return res;
};
手写数组some
Array.prototype.mySome = function (fn) {
if (typeof fn !== 'function') {
throw Error('参数必须是一个函数');
}
for (let i = 0; i < this.length; i++) {
if (fn(this[i], i, this)) {
return ture
}
}
return false;
};
手写数组every
Array.prototype.myEvery = function (fn) {
if (typeof fn !== 'function') {
throw Error('参数必须是一个函数');
}
for (let i = 0; i < this.length; i++) {
if (!fn(this[i], i, this)) {
return false;
}
}
return true;
};
手写数组find
Array.prototype.myFind = function (fn) {
if (typeof fn !== 'function') {
throw Error('参数必须是一个函数');
}
for (let i = 0; i < this.length; i++) {
if (fn(this[i], i, this)) {
return this[i];
}
}
return;
};
a == 1 && a == 2 && a == 3 为true
let a = {
[Symbol.toPrimitive]: (function(hint) {
let i = 1;
//闭包的特性之一:i 不会被回收
return function() {
return i++;
}
})()
}
console.log(a == 1 && a == 2 && a == 3); //true
let a = {
valueOf: (function() {
let i = 1;
//闭包的特性之一:i 不会被回收
return function() {
return i++;
}
})()
}
console.log(a == 1 && a == 2 && a == 3); //true
let i = 1;
Object.defineProperty(window, 'a', {
get: function() {
return i++;
}
});
console.log(a == 1 && a == 2 && a == 3); //true
let a = new Proxy({}, {
i: 1,
get: function () {
return () => this.i++;
}
});
console.log(a == 1 && a == 2 && a == 3); // true
数组乱序
var arr = [1,2,3,4,5,6,7,8,9,10];
for (var i = 0; i < arr.length; i++) {
const randomIndex = Math.round(Math.random() * (arr.length - 1 - i)) + i;
[arr[i], arr[randomIndex]] = [arr[randomIndex], arr[i]];
}
console.log(arr)
偏函数
偏函数就是将一个 n 参的函数转换成固定 x 参的函数,剩余参数(n - x)将在下次调用全部传入
function partial(fn, ...args) {
return (...arg) => {
return fn(...args, ...arg)
}
}
函数组合函数
const compose = (...fns) => (x) => fns.reduceRight((y, f) => f(y), x);
const pipe = (...fns) => (x) => fns.reduce((y, f) => f(y), x);
观察者模式
class Observer {
constructor(name) {
this.name = name;
}
update({taskType, taskInfo}) {
// 假设任务分为日常route和战斗war
if (taskType === "route") {
console.log(`${this.name}不需要日常任务`);
return;
}
this.goToTaskHome(taskInfo);
}
goToTaskHome(info) {
console.log(`${this.name}去任务大殿抢${info}任务`);
}
}
class Subject {
constructor() {
this.observerList = []
}
addObserver(observer) {
this.observerList.push(observer);
}
notify(task) {
console.log("发布五星任务");
this.observerList.forEach(observer => observer.update(task))
}
}
const subject = new Subject();
const stu1 = new Observer("弟子1");
const stu2 = new Observer("弟子2");
// stu1 stu2 购买五星任务通知权限
subject.addObserver(stu1);
subject.addObserver(stu2);
// 任务殿发布五星战斗任务
const warTask = {
taskType: 'war',
taskInfo: "猎杀时刻"
}
// 任务大殿通知购买权限弟子
subject.notify(warTask);
// 任务殿发布五星日常任务
const routeTask = {
taskType: 'route',
taskInfo: "种树浇水"
}
subject.notify(routeTask);
发布订阅模式
class PubSub {
constructor() {
// 事件中心
// 存储格式: warTask: [], routeTask: []
// 每种事件(任务)下存放其订阅者的回调函数
this.events = {}
}
// 订阅方法
subscribe(type, cb) {
if (!this.events[type]) {
this.events[type] = [];
}
this.events[type].push(cb);
}
// 发布方法
publish(type, ...args) {
if (this.events[type]) {
this.events[type].forEach(cb => cb(...args))
}
}
// 取消订阅方法
unsubscribe(type, cb) {
if (this.events[type]) {
const cbIndex = this.events[type].findIndex(e=> e === cb)
if (cbIndex != -1) {
this.events[type].splice(cbIndex, 1);
}
}
if (this.events[type].length === 0) {
delete this.events[type];
}
}
unsubscribeAll(type) {
if (this.events[type]) {
delete this.events[type];
}
}
}
// 创建一个中介公司
let pubsub = new PubSub();
// 弟子一订阅战斗任务
pubsub.subscribe('warTask', function (taskInfo){
console.log("宗门殿发布战斗任务,任务信息:" + taskInfo);
})
// 弟子一订阅战斗任务
pubsub.subscribe('routeTask', function (taskInfo) {
console.log("宗门殿发布日常任务,任务信息:" + taskInfo);
});
// 弟子三订阅全类型任务
pubsub.subscribe('allTask', function (taskInfo) {
console.log("宗门殿发布五星任务,任务信息:" + taskInfo);
});
// 发布战斗任务
pubsub.publish('warTask', "猎杀时刻");
pubsub.publish('allTask', "猎杀时刻");
// 发布日常任务
pubsub.publish('routeTask', "种树浇水");
pubsub.publish('allTask', "种树浇水");
class EventRmitter {
constructor() {
this.events = {};
}
on(eventName, fn) {
if (!this.events[eventName]) {
this.events[eventName] = [];
}
this.events[eventName].push(fn);
}
emit(eventName, ...args) {
this.events[eventName]?.forEach((fn) => fn(...args));
}
once(eventName, fn) {
const cb = () => {
fn();
this.off(eventName, fn);
};
this.on(eventName, cb);
}
off(eventName, fn) {
if (!this.events[eventName]) {
return;
}
this.events[eventName] = this.events[eventName].filter((fn) => fn !== fn);
}
}
// 运行示例
let ev = new EventEmitter();
const fun1 = (str) => {
console.log(str);
}
ev.on('say', fun1);
ev.once('say', fun1)
ev.emit('say', 'visa');
ev.off('say', fun1);
请求并发控制
function concurrentReq(urls, limit) {
return new Promise((resolve, reject) => {
let num = 0; // 当前在跑的请求数量(在跑的请求数量要小于限制的数量)
let task = urls; // 并发任务数组
let results = []; // 最终并发请求结果存放的数组
// 递归闭包函数调用发请求,Promise返回最终结果
function goNext() {
if (task.length === 0 && num === 0 ) {
// 当没有更多任务且没有请求正在进行时
resolve(results); // 所有请求已完成,resolve吐出去返回结果
return;
}
while (num < limit && task.length > 0) { // 当请求任务小于3且还有任务继续干时,goNext
let url = task.shift(); // 把并发任务数组中第一项剔除掉,并拿到第一项(请求接口)
num = num + 1 // 并记录一下当前的请求数量
axios.get(url) // 发请求
.then((res) => {
num = num - 1 // 请求成功,就把计数减一
results.push(res.data); // 把请求的结果依次存起来
goNext(); // 递归调用,继续执行下一个请求任务
})
.catch((err) => {
num = num - 1 // 请求失败,也把计数减一
console.error(`此接口:${url}请求失败,报错信息:${err}`);
goNext(); // 递归调用,继续执行下一个请求任务
})
}
}
goNext();
});
}
// 笔者提供的请求接口
let urls = [
'http://ashuai.work/api/getIdName?id=1',
'http://ashuai.work/api/getIdName?id=2',
'http://ashuai.work/api/getIdName?id=3',
'http://ashuai.work/api/getIdName?id=4',
'http://ashuai.work/api/getIdName?id=5',
'http://ashuai.work/api/getIdName?id=6',
'http://ashuai.work/api/getIdName?id=7',
'http://ashuai.work/api/getIdName?id=8',
'http://ashuai.work/api/getIdName?id=9',
'http://ashuai.work/api/getIdName?id=10',
'http://ashuai.work/api/getIdName?id=11',
'http://ashuai.work/api/getIdName?id=12',
'http://ashuai.work/api/getIdName?id=13',
'http://ashuai.work/api/getIdName?id=14',
'http://ashuai.work/api/getIdName?id=15',
'http://ashuai.work/api/getIdName?id=16',
'http://ashuai.work/api/getIdName?id=17',
'http://ashuai.work/api/getIdName?id=18',
]
let limit = 3; // 假设控制并发请求数量每次发三个
concurrentReq(urls, limit)
.then((results) => {
console.log('所有请求执行结果:', results);
})
.catch((error) => {
console.error('请求执行出错:', error);
});
使用Promise封装Ajax请求
// promise 封装实现:
function getJSON(url) {
// 创建一个 promise 对象
let promise = new Promise(function(resolve, reject) {
let xhr = new XMLHttpRequest();
// 新建一个 http 请求
xhr.open("GET", url, true);
// 设置状态的监听函数
xhr.onreadystatechange = function() {
if (this.readyState !== 4) return;
// 当请求成功或失败时,改变 promise 的状态
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
// 设置错误监听函数
xhr.onerror = function() {
reject(new Error(this.statusText));
};
// 设置响应的数据类型
xhr.responseType = "json";
// 设置请求头信息
xhr.setRequestHeader("Accept", "application/json");
// 发送 http 请求
xhr.send(null);
});
return promise;
}
手写判断类型函数
function getType(value) {
if (typeof value !== 'object') {
return typeof value;
}
return Object.prototype.toString.call(value).slice(8, -1).toLocaleLowerCase();
}
解析URL为对象
function parseParam(url) {
const paramsStr = /.+\?(.+)$/.exec(url)[1]; // 将 ? 后面的字符串取出来
const paramsArr = paramsStr.split('&'); // 将字符串以 & 分割后存到数组中
let paramsObj = {};
// 将 params 存到对象中
paramsArr.forEach(param => {
if (/=/.test(param)) { // 处理有 value 的参数
let [key, val] = param.split('='); // 分割 key 和 value
val = decodeURIComponent(val); // 解码
val = /^\d+$/.test(val) ? parseFloat(val) : val; // 判断是否转为数字
if (paramsObj.hasOwnProperty(key)) { // 如果对象有 key,则添加一个值
paramsObj[key] = [].concat(paramsObj[key], val);
} else { // 如果对象没有这个 key,创建 key 并设置值
paramsObj[key] = val;
}
} else { // 处理没有 value 的参数
paramsObj[param] = true;
}
})
return paramsObj;
}
用泛型将对象中的某些key提取为一个新的对象
function pickObjectKeys<T, K extends keyof T>(obj: T, keys: K[]) {
let result = {} as Pick<T, K>
for (const key of keys) {
if (key in obj) {
result[key] = obj[key]
}
}
return result
}
const language = {
name: "TypeScript",
age: 8,
extensions: ['ts', 'tsx']
}
const ageAndExtensions = pickObjectKeys(language, ['age', 'extensions'])
Promise实现图片加载
function loadImage(url) {
return new Promise((resolve, reject) => {
let img = new Image();
img.onload = () => {
resolve(img);
};
img.onerror = (error) => {
reject(error);
};
img.src = url;
});
}
// 使用方式
loadImage('https://example.com/path/to/image.jpg')
.then(img => {
document.body.appendChild(img);
console.log('图片加载成功');
})
.catch(error => {
console.error('图片加载失败', error);
});
实现obj[1][2][3] + 4 === 10
const createProxy = (value = 0) => {
return new Proxy(
{},
{
get(target, key, receiver) {
if (key === Symbol.toPrimitive) {
return () => value;
}
return createProxy(value + Number(key));
},
},
);
};
const obj = createProxy();
console.log(obj[1][2][3] + 4);
实现Sum函数链式调用实现多数之和
// 方法1
function Sum(init) {
this.total = init;
}
Sum.prototype.add = function (val) {
this.total += val;
return this;
};
// 方法2
function sum(value) {
let total = value;
function add(number) {
total += number;
return add;
}
// 触发隐式转换,会调用valueOf方法
add.valueOf = function () {
return total;
};
return add;
}
列表转树
function listsToTree(data) {
const map = new Map();
data.forEach((item) => {
map.set(item.id, item);
});
const res = [];
map.forEach((item) => {
if (!item.parent_id) {
res.push(item);
} else {
const parent = map.get(item.parent_id);
if (parent) {
if (!parent.children) {
parent.children = [];
}
parent.children.push(item);
}
}
});
}
function listToTreeSimple(data) {
const res = [];
data.forEach((item) => {
// 当到当前节点的父节点
const parent = data.find((node) => node.id === item.parentId);
if (parent) {
parent.children = parent.children || [];
parent.children.push(item);
} else {
// * 根节点
res.push(item);
}
});
return res;
}
实现get方法
let obj = {a: [{b: {c: 3}}]}
_.get(obj, "a[0].b.c") // => 3
_.get(obj, "a.0.b.c") // => 3
_.get(obj, "a.b.c", "default") // => defalut
const obj = { a: [{ b: { c: 3 } }] };
function get(obj: any, path: string) {
const keys = path.replace(/\[(\d+)\]/g, '.$1').split('.');
return (
keys.reduce((pre, cur) => {
if (pre?.hasOwnProperty(cur)) {
return pre[cur];
}
return undefined;
}, obj) ?? 'default'
);
}
console.log(get(obj, 'a[0].b.c'));
console.log(get(obj, 'a.0.b.c'));
console.log(get(obj, 'a.b.c'));
实现Promise数组链式调用
function serialPromiseExecution(promiseArray) {
return promiseArray.reduce((chain, currentPromiseFn) => {
// 确保 currentPromiseFn 是一个返回 Promise 的函数
if (typeof currentPromiseFn !== 'function') {
throw new Error('All elements in the array must be functions that return a Promise');
}
// 使用上一个 Promise 的结果作为下一个 Promise 的输入(如果需要的话)
return chain.then((previousResult) =>
currentPromiseFn(previousResult)
.catch((error) => { // 捕获并传播错误,保持整个链的稳定性
throw error;
})
);
}, Promise.resolve()); // 初始化为 resolved Promise,初始值为空
}
封装Axios
// index.ts
import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
import axios from 'axios';
type Result<T> = {
code: number;
message: string;
result: T;
};
// 导出Request类,可以用来自定义传递配置来创建实例
export class Request {
// axios 实例
instance: AxiosInstance;
// 基础配置,url和超时时间
baseConfig: AxiosRequestConfig = { baseURL: '/api', timeout: 60000 };
// * 存放取消请求控制器Map
abortControllerMap: Map<string, AbortController> = new Map();
constructor(config: AxiosRequestConfig) {
// 使用axios.create创建axios实例
this.instance = axios.create(Object.assign(this.baseConfig, config));
this.instance.interceptors.request.use(
(config: AxiosRequestConfig) => {
// 一般会请求拦截里面加token,用于后端的验证
const token = localStorage.getItem('token') as string;
if (token) {
config.headers!.Authorization = token;
}
const controller = new AbortController();
const url = config.url || '';
config.signal = controller.signal;
this.abortControllerMap.set(url, controller);
return config;
},
(err: any) => {
// 请求错误,这里可以用全局提示框进行提示
return Promise.reject(err);
},
);
this.instance.interceptors.response.use(
(res: AxiosResponse) => {
// 直接返回res,当然你也可以只返回res.data
// 系统如果有自定义code也可以在这里处理
// 删除取消请求控制器
const url = res.config.url || '';
this.abortControllerMap.delete(url);
return res;
},
(err: any) => {
// 可以在这里处理一些逻辑
// 这里是AxiosError类型,所以一般我们只reject我们需要的响应即可
return Promise.reject(err.response);
},
);
}
// 定义请求方法
public request(config: AxiosRequestConfig): Promise<AxiosResponse> {
return this.instance.request(config);
}
public get<T = any>(url: string, config?: AxiosRequestConfig): Promise<AxiosResponse<Result<T>>> {
return this.instance.get(url, config);
}
public post<T = any>(
url: string,
data?: any,
config?: AxiosRequestConfig,
): Promise<AxiosResponse<Result<T>>> {
return this.instance.post(url, data, config);
}
public put<T = any>(
url: string,
data?: any,
config?: AxiosRequestConfig,
): Promise<AxiosResponse<Result<T>>> {
return this.instance.put(url, data, config);
}
public delete<T = any>(
url: string,
config?: AxiosRequestConfig,
): Promise<AxiosResponse<Result<T>>> {
return this.instance.delete(url, config);
}
/**
* 取消全部请求
*/
cancelAllRequest() {
for (const [, controller] of this.abortControllerMap) {
controller.abort();
}
this.abortControllerMap.clear();
}
/**
* 取消指定的请求
* @param url 待取消的请求URL
*/
cancelRequest(url: string | string[]) {
const urlList = Array.isArray(url) ? url : [url];
for (const _url of urlList) {
this.abortControllerMap.get(_url)?.abort();
this.abortControllerMap.delete(_url);
}
}
}
// 默认导出Request实例
export default new Request({});