【js】常见的手写代码问题合集

334 阅读5分钟

(一)算法

1. 二分法

2. 扁平结构转换为嵌套结构:递归

(二)函数

1. 什么是防抖?什么是节流?手写一个⭐⭐⭐⭐⭐

2. JS实现限制函数并发数限制

  • 要在JavaScript中实现限制函数的并发数,可以使用Promiseasync/await结合的方式。
  • 原理:
    • 创建一个ConcurrentFunction类,传入limit用于限制函数的并发执行数量。
    • 通过调用run方法,可以将要执行的任务以Promise形式传入,
    • 并发数超过限制时会自动加入等待队列,当某个任务完成时,会从队列中取出下一个任务来执行,直到所有任务都完成。
  • 方法示例:
class ConcurrentFunction {
  constructor(limit) {
    this.limit = limit; // 并发数限制
    this.running = 0; // 当前正在运行的任务数
    this.queue = []; // 等待执行的任务队列
  }

  async run(fn) {
    return new Promise((resolve, reject) => {
      const task = () => {
        this.running++;
        // 执行函数
        fn().then(() => {
          resolve();
        }).catch((error) => {
          reject(error);
        }).finally(() => {
          this.running--;
          if (this.queue.length > 0) {
            this.queue.shift()(); // 取出队列中的下一个任务执行
          }
        });
      };

      // 如果当前运行的任务数小于限制的并发数,则立即执行任务
      if (this.running < this.limit) {
        task();
      } else {
        // 否则将任务加入队列等待执行
        this.queue.push(task);
      }
    });
  }
}

  • 使用示例:
// 创建一个最多允许同时运行2个任务的实例
const concurrentFunc = new ConcurrentFunction(2);

// 模拟需要进行的异步操作
function asyncOperation(id) {
  return new Promise(resolve => {
    setTimeout(() => {
      console.log(`Task ${id} finished`);
      resolve();
    }, Math.random() * 1000);
  });
}

// 执行多个任务
for (let i = 1; i <= 5; i++) {
  concurrentFunc.run(() => asyncOperation(i));
}

3.手写 ajax⭐⭐⭐⭐

思路分析:

  1. 创建 XMLHttpRequest 对象xhrxhr = new XMLHttpRequest()
    XMLHttpRequest 用于在后台与服务器交换数据
  2. 建立一个 HTTP 请求xhr.open(method, url, async, username, password)
    使用 XMLHttpRequest 对象的 open() 方法可以建立一个 HTTP 请求,open() 方法包含 5 个参数,说明如下:
    参数说明
    method① HTTP 请求方法,必须参数,值包括 POSTGET 和 HEAD,大小写不敏感。
    ② 设置第一个参数为 POST,然后使用 setRequestHeader() 方法设置请求消息的内容类型为“application/x-www-form-urlencoded”,它表示传递的是表单值,一般使用 POST 发送请求时都必须设置该选项,否则服务器无法识别传递过来的数据
    url请求的 URL 字符串,必须参数,大部分浏览器仅支持同源请求。
    async① 指定请求是否为异步方式,默认为 true。
    ② 如果为 false,当状态改变时会立即调用 onreadystatechange 属性指定的回调函数。
    username可选参数,如果服务器需要验证,该参数指定用户名,如果未指定,当服务器需要验证时,会弹出验证窗口。
    password可选参数,验证信息中的密码部分,如果用户名为空,则该值将被忽略。
  3. 发送请求xhr.send(body) :建立连接后,可以使用 send() 方法发送请求。
    参数 body 表示将通过该请求发送的数据,如果不传递信息,可以设置为 null 或者省略。
  4. 接收响应数据:发送请求后,可以使用 XMLHttpRequest 对象的 responseBody、responseStream、responseText 或 responseXML 等待接收响应数据。
    响应信息说明
    responseBody将响应信息正文以Unsigned Byte数组形式返回
    responseStream 以 ADO Stream 对象的形式返回响应信息
    responseText将响应信息作为字符串返回
    responseXML将响应信息格式化为 XML 文档格式返回
  5. 设置请求头(POST):
    • 一般使用 POST 发送请求时都必须设置该选项,否则服务器无法识别传递过来的数据
    • 使用 setRequestHeader() 方法设置请求消息的内容类型为“application/x-www-form-urlencoded”,它表示传递的是表单值,

代码:

const Ajax = {
    // ------------------- get请求 -------------------
    get: function (url, callback) {
        // 1.创建 XMLHttpRequest 对象
        let xhr = new XMLHttpRequest();
        // 2.建立一个 HTTP 请求
        xhr.open('get', url, false); // open(method, url, async, username, password)
        // async为 false,当状态改变时立即调用onreadystatechange属性指定的回调函数
        xhr.onreadystatechange = function () {
            if (xhr.readyState == 4) {
                if (xhr.status == 200 || xhr.status == 304) {
                    // 4. 接收响应数据:responseText
                    console.log(xhr.responseText); //
                    callback(xhr.responseText);
                }
            }
        };
        // 3.发送请求:建立连接后,使用 send() 方法发送请求。
        xhr.send();
    },
    // ------------------- post请求 -------------------
    post: function (url, data, callback) {
        // 1.创建 XMLHttpRequest 对象
        let xhr = new XMLHttpRequest();
        // 2.建立一个 HTTP 请求
        xhr.open('post', url, true); // 第三个参数为是否异步执行
        // 添加http头,设置编码类型
        xhr.setRequestHeader('Content-type', 'x-www-form-urlencoded');
        xhr.onreadystatechange = function () {
            if (xhr.readyState == 4) {
                if (xhr.status == 200 || xhr.status == 304) {
                    // 4. 接收响应数据:responseText
                    console.log(xhr.responseText);
                    callback(xhr.responseText);
                }
            }
        };
        // 5.一般使用 POST 发送请求时都必须设置该选项,否则服务器无法识别传递过来的数据。
        xhr.setRequestHeader('Content-type', 'application/x-www-urlencoded'); //一般我们设置的是:content-type,传输数据类型,即服务器需要我们传送的数据类型
        // 3.发送请求:建立连接后,使用 send() 方法发送请求。
        xhr.send(data);
    }
};

???3. 函数柯里化原理⭐⭐⭐⭐⭐

“函数柯里化的原理就是将要柯里化的函数作为柯里化标准函数的返回值,将要保存的内容作为外部函数的变量。* 也就是利用闭包创建一个私有域,使内部变量可以长期保存在内存中,直到内部返回的函数执行完成。(也就是要被柯里化的函数的n个参数都传入完成,一次性完成了计算操作,那么闭包中保存的变量就被释放)”

function add() {
    var args = Array.prototype.slice.call(arguments);

    var adder = function () {
        args.push(...arguments);
        return adder;
    };

    adder.toString = function () {
        return args.reduce((prev, curr) => {
            return prev + curr;
        }, 0);
    };

    return adder;
}

let a = add(1, 2, 3);
let b = add(1)(2)(3);
console.log(a);
console.log(b);
console.log(add(1, 2)(3));
console.log(Function.toString);

// --------普通函数转为柯里化函数------
function createCurry(fn, args = []) {
    return function () {
        let _args = args.concat(...arguments);
        if (_args.length < fn.length) {
            return createCurry.call(this, fn, _args);
        }
        return fn.apply(this, _args);
    };
}

function add(a, b, c) {
    return a + b + c;
}

var _add = createCurry(add);

console.log(_add(1, 2, 3));
console.log(_add(1)(2, 3));
console.log(_add(1)(2)(3));

4. 手写new

思路分析:

  1. 创建:创建一个新的空对象let obj = {}

  2. 继承:继承了构造函数的原型obj.__proto__ = fn.prototype
    (即 让新对象的__proto__指向原函数的prototype)。

  3. 执行:执行构造函数方法let result = fn.apply(obj, args)
    把构造函数方法的属性和方法都添加到this引用的对象
    中,让this指向这个新的对象

  4. 返回:返回这个新对象return result instanceof Object ? result : obj (如果构造函数中没有返回新对象,那么返回this,即创建新对象;否则,返回构造函数中返回的对象。).

代码:

function myNew(fn, ...args) {
      // 1.创建一个空对象
      let obj = {}
      // 2.使空对象的隐式原型指向原函数的显式原型
      obj.__proto__ = fn.prototype
      // 3.执行构造函数里面的代码(执行结果保存起来作为result ),
      // 给这个新对象添加属性和方法。
      let result = fn.apply(obj, args)// 让this指向这个新的对象obj。
      // 4.返回
      // 判断执行函数的结果是不是null或Undefined,
      // 如果是则返回之前的新对象,如果不是则返回result 
      return result instanceof Object ? result : obj
 }