Appearance
手写题
手写 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],未被修改