JS手写代码

100 阅读6分钟

手写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);

image.png

发布订阅模式

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', "种树浇水");

image.png

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({});