前端手写笔试题(干货版)

2022-05-11 10:08:11 浏览数 (1)

以下是纯干货总结的手写版笔试题代码,拿走不谢!

Promise 变体

promise.first

第一个返回成功的 Promise

代码语言:javascript复制
if (!Promise.first) {
  Promise.first = (promiseList) => {
    return new Promise((resolve, reject) => {
      let rejectNum = 0;
      const len = promiseList.length;

      promiseList.forEach((pr) => {
        Promise.resolve(pr)
          .then(resolve)
          .catch(() => {
            rejectNum  ; // 记录reject的个数
            if (rejectNum == len) {
              reject("all promise is reject");
            }
          });
      });
    });
  };
}

promise.last

最后一个返回成功的 Promise

代码语言:javascript复制
if (!Promise.last) {
  Promise.last = (promiseList) => {
    return new Promise((resolve, reject) => {
      let num = 0;
      const len = promiseList.length;
      let lastResult;

      const fn = () => {
        if (num !== len) {
          return;
        }
        lastResult ? resolve(lastResult) : reject("all promise is reject");
      };

      promiseList.forEach((pr) => {
        Promise.resolve(pr)
          .then((data) => {
            lastResult = data;
            num  ;
            fn();
          })
          .catch(() => {
            num  ;
            fn();
          });
      });
    });
  };
}

promise.none

与 Promise.all 相反,所有的 promise 都被拒绝,则 Promise.none 变成完成状态

代码语言:javascript复制
if (!Promise.none) {
  Promise.none = (promiseList) => {
    return Promise.all(
      promiseList.map((pr) => {
        return new Promise((resolve, reject) => {
          return Promise.resolve(pr).then(reject, resolve);
        });
      })
    );
  };
}

Promise.any

表示只获取所有的 promise 中进入完成状态的结果,被拒绝的则忽略掉

代码语言:javascript复制
if (!Promise.any) {
  Promise.any = (promiseList) => {
    return new Promise((resolve, reject) => {
      const result = [];
      let num = 0;
      const len = promiseList.length;

      const fn = () => {
        if (num == len) {
          result.length < 1 ? reject("all promise is reject") : resolve(result);
        }
      };

      promiseList.forEach((pr) => {
        Promise.resolve(pr)
          .then((data) => {
            result.push(data);
            num  ;
            fn();
          })
          .catch(() => {
            num  ;
            fn();
          });
      });
    });
  };
}

Promise.every

所有 promise 都进入完成状态,则返回 true,否则返回 false

代码语言:javascript复制
if (!Promise.every) {
  Promise.every = (promiseList) => {
    return new Promise((resolve, reject) => {
      Promise.all(promiseList)
        .then(() => Promise.resolve(true))
        .catch(() => Promise.reject(false));
    });
  };
}

Promisify

题意

代码语言:javascript复制
// 使用前

fs.readFile("./index.js", (err, data) => {
  if (!err) {
    console.log(data.toString());
  }

  console.log(err);
});

// 使用promisify后

const readFile = promisify(fs.readFile);

readFile("./index.js")
  .then((data) => {
    console.log(data.toString());
  })

  .catch((err) => {
    console.log("error:", err);
  });

解法

代码语言:javascript复制
function promisify(fn) {
  return (...args) => {
    return new Promise((resolve, reject) => {
      fn.apply(this, [
        ...args,
        (err, data) => {
          if (!err) {
            return resolve(data);
          }

          return reject(err);
        },
      ]);
    });
  };
}

模拟实现 sleep

代码语言:javascript复制
async function sleep(sleepTime) {
  return new Promise((resolve) => {
    setTimeout(resolve, sleepTime);
  });
}

JS 实现一个带有并发限制的异步调度器

题意

JS 实现一个带有并发限制的异步调度器 Scheduler,保证同时运行的任务最多有十个,完善代码中 Scheduler 类:

代码语言:javascript复制
Class Scheduler{
	constructor(max){}
	run(callback){/*...*/}
}

let s = new Scheduler(10);
s.run(async () => {
  /* some async operations */
});

解法

代码语言:javascript复制
Class Scheduler{
	COUNT = 0;
	LIST = [];
	constructor(max){
		this.LIMIT = 10;
	}
	async run(callback){
		if(this.COUNT >= this.LIMIT){
			// 通过await,只要不resolve,代码运行就会阻塞在这里
			await new Promise((resolve) => {
				this.list.push(resolve);
			});

		}
		this.COUNT  ;

		const result = await callback();

		this.COUNT--;

		if(this.list.length > 0){
			this.list.shift()(); // 解锁代码阻塞
		}

		return result;
	}
}

let s = new Scheduler(10);
s.run(async () => {
  /* some async operations */
});
s.run(async () => {
  /* some async operations */
});
s.run(async () => {
  /* some async operations */
});

手写 Promise

Promise 要点

  • Promise 是一个类,传入的参数是一个执行器,会立即执行
  • Promise 有三种状态
    • pending 等待
    • fulfilled 完成
    • rejected 失败
  • 状态只能改变一次,不能逆向。
    • pending -> fulfilled
    • pending -> rejected
  • Promise 使用 resolve 和 reject 两个函数来改变状态
  • then 方法内部做状态判断,执行对应的方法
  • 有静态方法 Promise.resolve 和 Promise.reject

手写版

代码语言:javascript复制
// 状态常量
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

class Promise {
  state = PENDING;
  value = undefined;
  reason = undefined;
  onResolveCallback = [];
  onRejectCallback = [];

  constructor(executor) {
    try {
      executor(this.resolve, this.reject);
    } catch (err) {
      this.reject(err);
    }
  }
  resolve(data) {
    if (this.state !== PENDING) {
      return;
    }
    this.state = FULFILLED;
    this.value = data;

    while (this.onResolveCallback.length > 0) {
      const currentResolve = this.onResolveCallback.shift();
      currentResolve(this.value);
    }
  }
  reject(err) {
    if (this.state !== PENDING) {
      return;
    }
    this.state = REJECTED;
    this.reason = err;

    while (this.onRejectCallback.length > 0) {
      const currentReject = this.onRejectCallback.shift();
      currentReject(this.reason);
    }
  }
  static resolve(param) {
    // param是Promise对象,直接返回
    if (param instanceof Promise) {
      return param;
    }

    // 转成普通的Promise
    return new Promise((resolve) => {
      resolve(param);
    });
  }
  static reject(reason) {
    return new Promise((resolve, reject) => {
      reject(reason);
    });
  }
  catch(fn) {
    if (this.state == REJECTED) {
      typeof fn == "function" && fn(this.reason);
    }
  }
  then(resolve, reject) {
    const realResolve =
      typeof resolve == "function" ? resolve : (value) => value;
    const realReject =
      typeof reject == "function"
        ? reject
        : (reason) => {
            throw reason;
          };

    // 链式调用,需要返回新的Promise实例

    const newPromise = new Promise((resolve, reject) => {
      // 创建一个微任务,等待Promise初始化完成

      const microResolve = () => {
        queueMicrotask(() => {
          try {
            const x = realResolve(this.value);
            resolvePromise(newPromise, x, resolve, reject);
          } catch (err) {
            reject(err);
          }
        });
      };

      const microReject = () => {
        queueMicrotask(() => {
          try {
            const x = realReject(this.reason);
            resolvePromise(newPromise, x, reasolve, reject);
          } catch (err) {
            reject(err);
          }
        });
      };

      if (this.state == FULFILLED) {
        return microResolve(this.value);
      }
      if (this.state == REJECTED) {
        return microReject(this.reason);
      }
      if (this.state == PENDING) {
        this.onResolveCallback.push(microResolve);
        this.onRejectCallback.push(microReject);
      }
    });

    return newPromise;
  }
}

function resolvePromise(newPromise, x, resolve, reject) {
  if (newPromise == x) {
    return reject(new Error("循环引用"));
  }

  if (x instanceof Promise) {
    x.then(resolve, reject);
  } else {
    resolve(x);
  }
}

模拟 bind 的实现

ES5 版本

代码语言:javascript复制
Function.prototype.bind =
  Function.prototype.bind ||
  function (context) {
    if (typeof this !== "function") {
      throw new Error("can't bind");
    }

    let self = this;
    let args = Array.prototype.slice(arguments, 1); // 缓存参数

    const fnOP = function () {};

    const bound = function () {
      let innerArgs = Array.prototype.slice(arguments);

      return self.apply(
        this instanceof fnOP ? this : context,
        args.concat(innerArgs)
      );
    };

    fnOP.prototype = this.prototype;
    bound.prototype = new fnOP();

    return bound;
  };

ES6 版本

代码语言:javascript复制
Function.prototype.bind =
  Function.prototype.bind ||
  function () {
    if (typeof this !== "function") {
      throw new Error("can't bind");
    }

    const args = Array.from(arguments);
    const context = args.shift();
    const self = this;

    const fnOP = Object.create(this.prototype);

    const fn = function () {
      const innerArgs = Array.from(arguments);
      return self.apply(
        this instanceof fnOP ? this : context,
        args.concat(innerArgs)
      );
    };

    fn.prototype = fnOP;

    return fn;
  };

模拟 new 的实现

代码语言:javascript复制
function Otaku(name, age) {
  this.name = name;
  this.age = age;

  this.habit = "Games";
}

Otaku.prototype.strength = 60;

Otaku.prototype.sayYourName = function () {
  console.log("I am "   this.name);
};

// 调用
myNew(Otaku, "Kevin", "18");

console.log(person.name); // Kevin
console.log(person.habit); // Games
console.log(person.strength); // 60

person.sayYourName(); // I am Kevin
代码语言:javascript复制
// 实现
function myNew(fn, ...args) {
  const obj = Object.create(fn.prototype);

  const ret = fn.apply(obj, args); // ret有可能为null

  return typeof ret === "object" ? ret || obj : obj;
}

深拷贝

深拷贝注意点

  • 注意类型,7 种基本类型: booleanstringnumberundefinednullSymbolBigInt
  • Object类型的可能情况(Object.prototype.toString.call):
    • object Object
    • object Function - typeof function == function
    • object Null
    • object Array
    • object RegExp

解法

代码语言:javascript复制
function deepCopy(data, hash = new WeakMap()) {
  if (typeof data !== "object" || data == null) {
    return data;
  }
  // 判断传入的待拷贝对象是否已经存在hash中,避免循环引用造成死循环
  if (hash.has(data)) {
    return hash.get(data);
  }

  const newData = Array.isArray(data) ? [] : {};
  const dataKeys = Object.keys(data);
  dataKeys.forEach((key) => {
    const current = data[key];
    const typeString = Object.prototype.toString.call(current).toLowerCase();

    if (typeof current !== "object" || current == null) {
      // 基本类型
      newData[key] = current;
      return;
    }

    switch (typeString) {
      case "[object array]":
        newData[key] = [...deepCopy(current, hash)];
        break;
      case "[object set]":
        newData[key] = new Set([...current.values()]);
        break;
      case "[object map]":
        newData[key] = new Map([...current]);
        break;
      default:
        // 普通对象进行递归操作
        hash.set(data, data);
        newData[key] = deepCopy(current, hash);
    }
  });

  return newData;
}

数组去重

ES6 Set

代码语言:javascript复制
const uniqueArr = [...new Set(arr)];
const uniqueArr = Array.from(new Set(arr));

reduce

代码语言:javascript复制
function unique(arr) {
  // 先排序,如果重复,则上一个下标的内容一样
  return arr.sort().reduce((acc, current) => {
    if (acc.length == 0 || acc[acc.length - 1] !== current) {
      acc.push(current);
    }
    return acc;
  }, []);
}

filter

代码语言:javascript复制
function unique(arr) {
  return arr.filter((element, index, array) => {
    return array.indexOf(element) == index;
  });
}

扁平化数组

使用 reduce 方法进行扁平化

代码语言:javascript复制
function flattenDeep(arr) {
  return Array.isArray(arr)
    ? arr.reduce((acc, current) => {
        return [...acc, ...flattenDeep(current)];
      }, [])
    : [arr];
}

模拟栈

代码语言:javascript复制
function flattenDeep(arr) {
  const stack = [...arr]; // 先平展复制到stack
  const result = [];

  while (stack.length) {
    // 取出末尾的值
    const current = stack.pop();
    if (Array.isArray(current)) {
      // 如果值是数组,平展后,重新推入stack
      stack.push(...current);
    } else {
      // 末尾是普通值,则存入result
      result.unshift(current);
    }
  }
  return result;
}

柯里化

代码语言:javascript复制
function curry(fn) {
  return function curried(...args) {
    if (args.length >= fn.length) {
      // 传入参数已覆盖所有形参
      fn.apply(this, args);
    } else {
      return function (...args2) {
        return curried.apply(this, args.concat(args2));
      };
    }
  };
}

防抖

代码语言:javascript复制
function debounce(fn, wait) {
  let timer = null;
  return () => {
    clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(this, Array.from(arguments));
    }, wait);
  };
}

节流

代码语言:javascript复制
function throttle(fn, wait) {
  let timer = null;

  return () => {
    if (timer !== null) {
      return;
    }

    timer = setTimeout(() => {
      fn.apply(this.Array.from(arguments));
      timer = null;
    }, wait);
  };
}

最近笔者在整理第一本电子书书稿《前端面试手册》,有兴趣的同学可以关注下~

喜欢我文章的朋友,可以通过以下方式关注我:

  • 「star」「watch」 我的GitHub blog - RSS订阅我的个人博客:王先生的基地
关注关注

0 人点赞