Skip to content

手写题

手写 Call

js
var value = 2;

var obj = {
  value: 1,
};

function bar(name, age) {
  console.log(this.value);
  return {
    value: this.value,
    name: name,
    age: age,
  };
}

bar.call2(null);

bar.call2(obj, "kevin", 18);
答案
js
var value = 2;

var obj = {
  value: 1,
};

function bar(name, age) {
  console.log(this.value);
  return {
    value: this.value,
    name: name,
    age: age,
  };
}
Funtion.prototype.call2 = function (context, ...args) {
    if (context === null || typeof context === undefined) {
        context = window
    }

    const fn = Symbol()

    context[fn] = this

    const res = context[fn](...args)

    delete context[fn]

    return res
}

手写 Apply

js
var value = 2;

var obj = {
  value: 1,
};

function bar(name, age) {
  console.log(this.value);
  return {
    value: this.value,
    name: name,
    age: age,
  };
}
答案
js
var value = 2;

var obj = {
  value: 1,
};

function bar(name, age) {
  console.log(this.value);
  return {
    value: this.value,
    name: name,
    age: age,
  };
}

Funtion.prototype.call2 = function (context, args) {
    if (context === null || typeof context === undefined) {
        context = window
    }

    const fn = Symbol()

    context[fn] = this

    const res = context[fn](...args)

    delete context[fn]

    return res
}

手写 Bind

js
const foo = {
  value: 1,
};

function bar(name, age) {
  console.log(this.value);
  console.log(name);
  console.log(age);
}
答案
js
const foo = {
  value: 1,
};

function bar(name, age) {
  console.log(this.value);
  console.log(name);
  console.log(age);
}

Function.prototype.bind2 = function (context, ...args) {
  const self = this

  return function boundFunc (...bindArgs) {
    const isNew = this instanceof boundFunc
    const finalContext = isNew ? this : context

    return self.apply(finalContext, args.concat(bindArgs))
  }
}

const bindFoo = bar.bind2(foo, "daisy");
bindFoo("18"); // 输出: 1, daisy, 18

const newBindFoo = new bindFoo("18"); // 输出: undefined, daisy, 18

手写 new

js
function Person(name, age) {
  this.name = name;
  this.age = age;

  this.habit = "Games";
}

Person.prototype.strength = 60;

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

function objectFactory() {

}
答案
js
function Person(name, age) {
  this.name = name;
  this.age = age;

  this.habit = "Games";
}

Person.prototype.strength = 60;

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

function objectFactory() {
  const obj = new Object()

  const Constructor = [].shift.apply(arguments)

  obj.__proto__ = Constructor.prototype

  Constructor.apply(obj, arguments)

  return obj
}

var person = objectFactory(Person, "Kevin", "18");

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

person.sayYourName(); // I am Kevin

手写 debounce

js
function debounce(fn, ms) {

}

const handleResize = () => {
  console.log("Window resized");
};

const debouncedResize = debounce(handleResize, 300);

window.addEventListener("resize", debouncedResize);
答案
js
function debounce(func, wait) {
  let timeout;
  return function (...args) {
    const context = this;
    clearTimeout(timeout);
    timeout = setTimeout(() => {
      func.apply(context, args);
    }, wait);
  };
}

const handleResize = () => {
  console.log("Window resized");
};

const debouncedResize = debounce(handleResize, 300);

window.addEventListener("resize", debouncedResize);

手写 throttle

js
function throttle(fn, ms) {

}

const handleScroll = () => {
  console.log("Scroll event triggered");
};

const throttledScroll = throttle(handleScroll, 2000);

window.addEventListener("resize", throttledScroll);
答案
js
function throttle(fn, ms) {
  let last = 0

  return function(...args) {
    const now = new Date()
    if (now - last > ms) {
      fn.apply(this, args)
      last = now
    }
  }
}

const handleScroll = () => {
  console.log("Scroll event triggered");
};

const throttledScroll = throttle(handleScroll, 200);

window.addEventListener("resize", throttledScroll);

手写 Object.create

js
function create(obj) {
}

const person = {
  isHuman: false,
  printIntroduction: function () {
    console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`);
  },
};

const me = create(person);
me.name = "Matthew";
me.isHuman = true;

me.printIntroduction();
答案
js
function create(obj) {
  function F() {}
  F.prototype = obj
  return new F()
}

const person = {
  isHuman: false,
  printIntroduction: function () {
    console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`);
  },
};

const me = create(person);
me.name = "Matthew";
me.isHuman = true;

me.printIntroduction(); // 输出: My name is Matthew. Am I human? true

TIPS

Object.create()和直接赋值{}有一些区别,通过Object.create()创建的对象是纯净的,避免 Object.prototype 方法干扰,是一种继承的方式 因此,如果想实现键值对字典的场景,推荐使用Object.create(null)

特性Object.create(null){}
是否有 Object.prototype❌ 无✅ 有
是否有 toString、hasOwnProperty 等方法❌ 无✅ 有
适合作为字典对象✅ 推荐❌ 可能有问题
适合普通对象❌ 不推荐✅ 推荐

手写 instanceof

js
function myInstanceof(left, right) {
  const prototype = right.prototype;
  left = Object.getPrototypeOf(left);

  while (true) {
    if (left === prototype) return true;
    if (left === null) return false;

    left = Object.getPrototypeOf(left);
  }
}

function Person() {}

const person = new Person();

console.log(myInstanceof(person, Person)); // 输出: true
console.log(myInstanceof(person, Object)); // 输出: true
console.log(myInstanceof(person, Array)); // 输出: false
答案
js
function myInstanceof(left, right) {
  const prototype = right.prototype
  left = Object.getPrototypeOf(left)

  while (true) {
    if (left === prototype) return true
    if (left === null) return false

    left = Object.getPrototypeOf(left)
  }
}

function Person() {}

const person = new Person();

console.log(myInstanceof(person, Person)); // 输出: true
console.log(myInstanceof(person, Object)); // 输出: true
console.log(myInstanceof(person, Array)); // 输出: false

手写深拷贝

js
const obj = {
  a: 1,
  b: {
    c: 2,
    d: [3, 4],
  },
  e: new Date(),
  f: /abc/g,
};

// TODO deepClone

const clonedObj = deepClone(obj);

console.log(clonedObj);
console.log(clonedObj.b === obj.b); // 输出: false
console.log(clonedObj.e === obj.e); // 输出: false
console.log(clonedObj.f === obj.f); // 输出: false
答案
js
const obj = {
  a: 1,
  b: {
    c: 2,
    d: [3, 4],
  },
  e: new Date(),
  f: /abc/g,
};

function deepClone(obj, hash = new WeakMap()) {
  if (obj === null || typeof obj !== "object") {
    return obj;
  }

  if (hash.has(obj)) {
    return hash.get(obj);
  }

  // 处理特殊对象
  if (obj instanceof Date) {
    return new Date(obj);
  }
  
  if (obj instanceof RegExp) {
    return new RegExp(obj.source, obj.flags);
  }

  let clonedObj = Array.isArray(obj) ? [] : {};
  hash.set(obj, clonedObj);

  for (let key in obj) {
    clonedObj[key] = deepClone(obj[key], hash);
  }

  return clonedObj;
}

const clonedObj = deepClone(obj);

console.log(clonedObj);
console.log(clonedObj.b === obj.b); // 输出: false
console.log(clonedObj.e === obj.e); // 输出: false
console.log(clonedObj.f === obj.f); // 输出: false

手写 sleep

js
async function example() {
  console.log("Start");
  await sleep(2000); // 暂停 2 秒
  console.log("End");
}

// TODO sleep

example();
答案
js
async function example() {
  console.log("Start");
  await sleep(2000); // 暂停 2 秒
  console.log("End");
}

const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

example();

手写 flat

js
const arr = [1, [2, [3, [4, 5]]], 6];

const flatDeep = (arr) => {
  // todo...
};

console.log(flatDeep(arr)); // 输出: [1, 2, 3, 4, 5, 6]
答案
js
const arr = [1, [2, [3, [4, 5]]], 6];

const flatDeep = (arr) => {
  return arr.reduce((acc, cur) => {
    if (Array.isArray(cur)) {
      acc.push(...flatDeep(cur));
    } else {
      acc.push(cur);
    }

    return acc;
  }, []);
};

console.log(flatDeep(arr)); // 输出: [1, 2, 3, 4, 5, 6]

手写 reduce

js
// todo...
// Array.prototype.myReduce = 

const arr = [1, 2, 3, 4, 5];

const sum = arr.myReduce((acc, val) => acc + val, 0);
console.log(sum); // 输出: 15

const product = arr.myReduce((acc, val) => acc * val, 1);
console.log(product); // 输出: 120
答案
js
Array.prototype.myReduce = function (callback, initialValue) {
  let accumulator = initialValue;
  let startIndex = 0;

  if (initialValue === undefined) {
    accumulator = this[0];
    startIndex = 1;
  }

  for (let i = startIndex; i < this.length; i++) {
    accumulator = callback(accumulator, this[i], i, this);
  }

  return accumulator;
};

const arr = [1, 2, 3, 4, 5];

const sum = arr.myReduce((acc, val) => acc + val, 0);
console.log(sum); // 输出: 15

const product = arr.myReduce((acc, val) => acc * val, 1);
console.log(product); // 输出: 120

手写 curry

js
// todo function curry...

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

const curriedAdd = curry(add);

console.log(curriedAdd(1)(2)(3)); // 输出: 6
console.log(curriedAdd(1, 2)(3)); // 输出: 6
console.log(curriedAdd(1, 2, 3)); // 输出: 6
答案
js
function curry(fn) {
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn.apply(this, args);
    } else {
      return function (...nextArgs) {
        return curried.apply(this, args.concat(nextArgs));
      };
    }
  };
}

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

const curriedAdd = curry(add);

console.log(curriedAdd(1)(2)(3)); // 输出: 6
console.log(curriedAdd(1, 2)(3)); // 输出: 6
console.log(curriedAdd(1, 2, 3)); // 输出: 6

手写 event bus

js
class EventBus {
  // todo...
}

// 示例用法
const eventBus = new EventBus();

const callback = (msg) => console.log(msg);

eventBus.on("test", callback);
eventBus.emit("test", "Hello, EventBus!"); // 输出: Hello, EventBus!

eventBus.off("test", callback);
eventBus.emit("test", "This will not be logged"); // 没有输出

eventBus.once("onceTest", (msg) => console.log(msg));
eventBus.emit("onceTest", "This will be logged once"); // 输出: This will be logged once
eventBus.emit("onceTest", "This will not be logged"); // 没有输出
答案
js
class EventBus {
  constructor() {
    this.events = {};
  }

  // 注册事件
  on(event, callback) {
    if (!this.events[event]) {
      this.events[event] = [];
    }
    this.events[event].push(callback);
  }

  // 移除事件
  off(event, callback) {
    if (!this.events[event]) return;

    this.events[event] = this.events[event].filter((cb) => cb !== callback);
  }

  // 触发事件
  emit(event, ...args) {
    if (!this.events[event]) return;

    this.events[event].forEach((callback) => {
      callback.apply(this, args);
    });
  }

  // 注册只执行一次的事件
  once(event, callback) {
    const onceCallback = (...args) => {
      callback.apply(this, args);
      this.off(event, onceCallback);
    };
    this.on(event, onceCallback);
  }
}

// 示例用法
const eventBus = new EventBus();

const callback = (msg) => console.log(msg);

eventBus.on("test", callback);
eventBus.emit("test", "Hello, EventBus!"); // 输出: Hello, EventBus!

eventBus.off("test", callback);
eventBus.emit("test", "This will not be logged"); // 没有输出

eventBus.once("onceTest", (msg) => console.log(msg));
eventBus.emit("onceTest", "This will be logged once"); // 输出: This will be logged once
eventBus.emit("onceTest", "This will not be logged"); // 没有输出

手写数组乱序

js
function shuffle(arr) {
  // todo...
}

// 示例用法
const arr = [1, 2, 3, 4, 5];
const shuffledArr = shuffle(arr);

console.log(shuffledArr); // 输出: 乱序后的数组,例如 [3, 5, 1, 4, 2]
console.log(arr); // 输出: 原数组 [1, 2, 3, 4, 5],未被修改
答案
js
function shuffle(arr) {
  let shuffledArray = arr.slice(); // 创建数组的副本,以免修改原数组
  for (let i = shuffledArray.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [shuffledArray[i], shuffledArray[j]] = [shuffledArray[j], shuffledArray[i]]; // 交换元素
  }
  return shuffledArray;
}

// 示例用法
const arr = [1, 2, 3, 4, 5];
const shuffledArr = shuffle(arr);

console.log(shuffledArr); // 输出: 乱序后的数组,例如 [3, 5, 1, 4, 2]
console.log(arr); // 输出: 原数组 [1, 2, 3, 4, 5],未被修改