2024面经编程题

121 阅读5分钟

用setTimeout实现setInterval

function customSetInterval(callback: () => void, delay: number): () => void {
    let timer;

    const interval = () => {
        callback();
        timer = setTimeout(interval, delay)
    }

    timer = setTimeout(interval, delay)

    // 返回清除定时器的函数
    return () => {
        clearTimeout(timer)
    }
}

// 示例用法
const callback = () => console.log("Hello, world!");
const delay = 1000; // 每隔1000毫秒执行一次
const clearIntervalFunc = customSetInterval(callback, delay);

// 用法:在需要的时候清除定时器
// clearIntervalFunc();

var实现let

function demo() {
    (function() {
        var x = "Hello, world!";
        console.log(x); // 输出 "Hello, world!"
    })();

    try {
        console.log(x); // 抛出 ReferenceError,因为x在这个作用域内未定义
    } catch (error) {
        console.error(error); // 输出错误信息
    }
}

demo();

实现所有的TypeScript Utility Types

// 1. Partial<T>
type Partial<T> = { [P in keyof T]?: T[P] };

// 2. Required<T>
type Required<T> = { [P in keyof T]-?: T[P] };

// 3. Readonly<T>
type Readonly<T> = { readonly [P in keyof T]: T[P] };

// 4. Pick<T, K>
type Pick<T, K extends keyof T> = { [P in K]: T[P] };

// 5. Omit<T, K>
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;

// 6. Exclude<T, U>
type Exclude<T, U> = T extends U ? never : T;

// 7. Extract<T, U>
type Extract<T, U> = T extends U ? T : never;

// 8. NonNullable<T>
type NonNullable<T> = Exclude<T, null | undefined>;

// 9. ReturnType<T>
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;

// 10. InstanceType<T>
type InstanceType<T extends new (...args: any) => any> = T extends new (...args: any) => infer R ? R : any;

// 11. ThisParameterType<T>
type ThisParameterType<T> = T extends (this: infer U, ...args: any[]) => any ? U : unknown;

// 12. OmitThisParameter<T>
type OmitThisParameter<T> = T extends (this: any, ...args: infer A) => infer R ? (...args: A) => R : T;

// 13. ThisType<T>
// `ThisType` is a marker utility type and can't be implemented like other utility types.

防抖debounce

function debounce(func: (...args: any[]) => void, wait: number): (...args: any[]) => void {
    let timeout: ReturnType<typeof setTimeout> | null = null;

    return (...args: any[]) => {
        if (timeout) {
            clearTimeout(timeout);
        }

        timeout = setTimeout(() => {
            func.apply(null, args);
        }, wait);
    };
}

节流throttle

function throttle(func: (...args: any[]) => void, limit: number): (...args: any[]) => void {
    let lastCall = 0; // 记录上次调用的时间戳

    return (...args: any[]) => {
        const now = Date.now(); // 获取当前时间戳

        // 如果当前时间与上次调用的时间差大于等于设定的限制时间,执行函数并更新上次调用时间
        if (now - lastCall >= limit) {
            func.apply(null, args);
            lastCall = now;
        }
    };
}

New

function customNew(constructorFn: Function, ...args: any[]): object {
    const obj = Object.create(constructorFn.prototype); // 创建一个新对象,并将其原型链设置为构造函数的prototype
    const result = constructorFn.apply(obj, args); // 调用构造函数并将this绑定到新创建的对象

    // 如果构造函数返回了一个对象,那么返回这个对象,否则返回创建的新对象
    return (typeof result === "object" && result !== null) ? result : obj;
}

// 示例用法
function Person(name: string, age: number) {
    this.name = name;
    this.age = age;
}

const alice = customNew(Person, "Alice", 30) as Person;
console.log(alice.name); // 输出 "Alice"
console.log(alice.age);  // 输出 30

数组去重

// First
const uniqueArray = (arr: any[]) => {
    return [...new Set(arr)]
}

// Second
const uniqueArray = (arr: any[]) => {
	const result = [];
  for (const item of arr) {
    if (result.indexOf(item) === -1) {
      result.push(item);
    }
  }
  return result;
}

// Third
const uniqueArray = (arr: any[]) => {
  return arr.filter((item, index) => {
    return arr.indexOf(item) === index
  })
}

console.log(uniqueArray([1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5])); // [1, 2, 3, 4, 5, 6]

实现正则切分千分位

function formatThousands(n: number): string {
  const reg = /\d{1,3}(?=(\d{3})+$)/g;
  const num = n.toString();
  const formattedNum = num.replace(reg, '$&,');
  return formattedNum;
}

// 测试
console.log(formatThousands(123456789)); // 输出: 123,456,789
console.log(formatThousands(1000000)); // 输出: 1,000,000
console.log(formatThousands(9876543210)); // 输出: 9,876,543,210

call

// 实现自定义call方法
Function.prototype.myCall = function (thisArg: any, ...args: any[]): any {
    const fn = this;
    const uniqueKey = Symbol("uniqueKey");
    thisArg[uniqueKey] = fn;

    const result = thisArg[uniqueKey](...args);
    delete thisArg[uniqueKey];

    return result;
};

const obj = {
    name: 'Alice'
}

// 示例用法
function greet(greeting: string, punctuation: string) {
    console.log(`${greeting}, ${this.name}${punctuation}`);
}

greet.myCall(obj, "Hello", "!"); // 输出 "Hello, Alice!"

apply

// 实现自定义apply方法
Function.prototype.myApply = function (thisArg: any, args: any[]): any {
    const fn = this;
    const uniqueKey = Symbol("uniqueKey");
    thisArg[uniqueKey] = fn;

    const result = thisArg[uniqueKey](...args);
    delete thisArg[uniqueKey];

    return result;
};

const obj = {
    name: 'Alice'
}

// 示例用法
function greet(greeting: string, punctuation: string) {
    console.log(`${greeting}, ${this.name}${punctuation}`);
}

greet.myApply(obj, ["Hi", "!"]); // 输出 "Hi, Alice!"

bind

// 实现自定义bind方法
Function.prototype.myBind = function (thisArg: any, ...args1: any[]): (...args2: any[]) => any {
    const fn = this;

    return function (...args2: any[]) {
        return fn.myApply(thisArg, args1.concat(args2));
    };
};

const obj = {
    name: 'Alice'
}

// 示例用法
function greet(greeting: string, punctuation: string) {
    console.log(`${greeting}, ${this.name}${punctuation}`);
}

const boundGreet = greet.myBind(obj, "Hey");
boundGreet("?"); // 输出 "Hey, Alice?"

深拷贝

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

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

    if (obj instanceof Date) {
        return new Date(obj.getTime());
    }

    if (obj instanceof Function) {
        return function(...args: any[]) {
            obj.apply(this, args)
        }
    }

    if (obj instanceof RegExp) {
        return new RegExp(obj);
    }

    if (obj instanceof Array) {
        const clonedArr: any[] = [];
        cache.set(obj, clonedArr);
        for (let i = 0; i < obj.length; ++i) {
            clonedArr[i] = deepClone(obj[i], cache);
        }
        return clonedArr;
    }

    const clonedObj: { [key: string]: any } = {};
    cache.set(obj, clonedObj);
    for (const key in obj) {
        if (Object.prototype.hasOwnProperty.call(obj, key)) {
            clonedObj[key] = deepClone(obj[key], cache);
        }
    }

    return clonedObj;
}

// 示例用法
const original: { [key: string]: any } = {
    name: "Alice",
    age: 30,
    dateOfBirth: new Date("1993-01-01"),
    preferences: {
        color: "blue",
        food: "pizza"
    },
    sum() {
        console.log(this.name + '-' + this.age);
    }
};
original.original = original
original.originalArr = [original, original]

const cloned = deepClone(original);
console.log(cloned); // 输出与 original 相同但不是同一个引用的对象

柯里化

function curry(fn: (...args: any[]) => any): (...args: any[]) => any {
    const arity = fn.length; // 获取原函数的参数个数

    function curried(...args: any[]): any {
        if (args.length >= arity) {
            return fn.apply(null, args);
        }

        return (...restArgs: any[]) => curried.apply(null, args.concat(restArgs));
    }

    return curried;
}

// 示例用法
function add(a: number, b: number, c: number): number {
    return a + b + c;
}

const curriedAdd = curry(add);
console.log(curriedAdd(1)(2)(3)); // 输出 6

es5和es6继承

// ES5继承(构造函数 + 原型链)
function AnimalES5(name: string) {
  this.name = name;
}

AnimalES5.prototype.sayName = function () {
  console.log("My name is " + this.name);
};

function DogES5(name: string, breed: string) {
  AnimalES5.call(this, name); // 调用父类构造函数
  this.breed = breed;
}

DogES5.prototype = Object.create(AnimalES5.prototype); // 设置原型链
DogES5.prototype.constructor = DogES5; // 修复构造函数

DogES5.prototype.sayBreed = function () {
  console.log("My breed is " + this.breed);
};

// 示例用法
const dogES5 = new DogES5("Max", "Golden Retriever");
dogES5.sayName(); // 输出 "My name is Max"
dogES5.sayBreed(); // 输出 "My breed is Golden Retriever"

// ES6继承(使用class和extends关键字)
class AnimalES6 {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
  sayName() {
    console.log("My name is " + this.name);
  }
}

class DogES6 extends AnimalES6 {
  breed: string;
  constructor(name: string, breed: string) {
    super(name); // 调用父类构造函数
    this.breed = breed;
  }
  sayBreed() {
    console.log("My breed is " + this.breed);
  }
}
// 示例用法
const dogES6 = new DogES6("Max", "Golden Retriever");
dogES6.sayName(); // 输出 "My name is Max"
dogES6.sayBreed(); // 输出 "My breed is Golden Retriever"

instanceof

// 自定义实现 instanceof
function myInstanceOf(target: any, constructorFunc: Function): boolean {
  // 参数校验
  if (typeof target !== 'object' || target === null || typeof constructorFunc !== 'function') {
    return false;
  }

  // 获取目标对象的原型
  let targetProto = Object.getPrototypeOf(target);

  // 获取构造函数的原型
  const constructorProto = constructorFunc.prototype;

  // 遍历原型链,查找目标对象是否是构造函数的实例
  while (targetProto !== null) {
    if (targetProto === constructorProto) {
      return true;
    }
    targetProto = Object.getPrototypeOf(targetProto);
  }

  return false;
}

// 测试用例
class Animal {}
class Dog extends Animal {}
class Cat extends Animal {}

const dog = new Dog();
const cat = new Cat();

console.log(myInstanceOf(dog, Dog)); // true
console.log(myInstanceOf(dog, Animal)); // true
console.log(myInstanceOf(cat, Dog)); // false
console.log(myInstanceOf(cat, Animal)); // true
console.log(myInstanceOf(123, Number)); // false

数组扁平化

// 自定义实现数组扁平化
function flattenArray(arr: any[]): any[] {
  const result: any[] = [];

  // 递归处理每个元素
  function processItem(item: any) {
    // 如果元素是数组,则递归处理
    if (Array.isArray(item)) {
      item.forEach(processItem);
    } else {
      // 如果元素不是数组,直接添加到结果数组中
      result.push(item);
    }
  }

  arr.forEach(processItem);
  return result;
}

// 测试用例
const nestedArray = [1, [2, [3, 4], 5, [6, [7, 8]]], 9, 10];

console.log(flattenArray(nestedArray)); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

const nestedArray2 = [1, [2, 3], 4, [[5], 6, [7, [8, 9, [10]]]]];

console.log(flattenArray(nestedArray2)); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

对象扁平化

function flattenObject(obj: { [key: string]: any }, prefix = ""): { [key: string]: any } {
    const flattened: { [key: string]: any } = {};

    for (const key in obj) {
        if (Object.prototype.hasOwnProperty.call(obj, key)) {
            const newKey = prefix ? `${prefix}.${key}` : key;

            if (typeof obj[key] === "object" && obj[key] !== null && !Array.isArray(obj[key])) {
                Object.assign(flattened, flattenObject(obj[key], newKey));
            } else {
                flattened[newKey] = obj[key];
            }
        }
    }

    return flattened;
}

// 示例用法
const nestedObj = {
    a: {
        b: {
            c: 1,
            d: {
                e: 2
            }
        },
        f: 3
    },
    g: {
        h: 4
    }
};

const flattenedObj = flattenObject(nestedObj);
console.log(flattenedObj);
// 输出 { 'a.b.c': 1, 'a.b.d.e': 2, 'a.f': 3, 'g.h': 4 }

JSON.parse

const myJSONParse = (target) => {
  return eval(`(${target})`);
};

// 测试用例
const jsonString = '{"name": "John", "age": 30, "city": "New York"}';
const parsedObject = myJSONParse(jsonString);
console.log(parsedObject); // { name: 'JOHN', age: 30, city: 'NEW YORK' }

EventEmitter事件触发器

class EventEmitter {
    private events: Map<string, Array<(...args: any[]) => void>>;

    constructor() {
        this.events = new Map(); // 存储事件名和对应的回调函数列表
    }

    // 添加事件监听
    on(event: string, listener: (...args: any[]) => void): void {
        if (!this.events.has(event)) {
            this.events.set(event, []);
        }
        this.events.get(event)!.push(listener);
    }

    // 移除事件监听
    off(event: string, listener: (...args: any[]) => void): void {
        const listeners = this.events.get(event);
        if (listeners) {
            const index = listeners.indexOf(listener);
            if (index !== -1) {
                listeners.splice(index, 1);
            }
        }
    }

    // 触发事件
    emit(event: string, ...args: any[]): void {
        const listeners = this.events.get(event);
        if (listeners) {
            listeners.forEach(listener => listener.apply(null, args));
        }
    }

    // 添加只执行一次的事件监听
    once(event: string, listener: (...args: any[]) => void): void {
        const wrappedListener = (...args: any[]) => {
            listener.apply(null, args);
            this.off(event, wrappedListener);
        };
        this.on(event, wrappedListener);
    }
}

// 示例用法
const eventEmitter = new EventEmitter();

function hello(name: string) {
    console.log(`Hello, ${name}!`);
}

eventEmitter.on("greet", hello);
eventEmitter.emit("greet", "Alice"); // 输出 "Hello, Alice!"

eventEmitter.off("greet", hello);
eventEmitter.emit("greet", "Bob"); // 不会输出,因为监听器已被移除

eventEmitter.once("welcome", hello);
eventEmitter.emit("welcome", "Carol"); // 输出 "Hello, Carol!"
eventEmitter.emit("welcome", "David"); // 不会输出,因为监听器只执行一次

async/await

function customAsync(generatorFn: (...args: any[]) => Generator) {
    return function (...args: any[]) {
        const generator = generatorFn.apply(null, args);

        function handle(result: IteratorResult<any>): Promise<any> {
            if (result.done) {
                return Promise.resolve(result.value);
            }

            return Promise.resolve(result.value)
                .then(value => handle(generator.next(value)))
                .catch(error => handle(generator.throw!(error)));
        }

        return handle(generator.next());
    };
}

// 示例用法
function* myGenerator() {
    const result1 = yield new Promise(resolve => setTimeout(() => resolve("First result"), 1000));
    console.log(result1);

    const result2 = yield new Promise(resolve => setTimeout(() => resolve("Second result"), 1000));
    console.log(result2);

    return "Done";
}

const myAsyncFunction = customAsync(myGenerator);
myAsyncFunction().then(result => console.log(result)); // 依次输出 "First result", "Second result", "Done"

正则获取url params

// 自定义实现获取 URL 参数
function getUrlParams(url: string): Record<string, string> {
  const params: Record<string, string> = {};
  const regex = /[?&]([^=&#]+)=([^&#]*)/g;
  let match: RegExpExecArray | null;

  // 使用正则表达式匹配 URL 参数
  while ((match = regex.exec(url)) !== null) {
    // 将匹配到的参数名称和值添加到结果对象中
    params[decodeURIComponent(match[1])] = decodeURIComponent(match[2]);
  }

  return params;
}

// 测试用例
const testUrl1 = 'https://www.example.com/test?name=John&age=30&city=New%20York';
const result1 = getUrlParams(testUrl1);
console.log(result1); // { name: 'John', age: '30', city: 'New York' }

const testUrl2 = 'https://www.example.com/test?query=test&page=1&filter=active';
const result2 = getUrlParams(testUrl2);
console.log(result2); // { query: 'test', page: '1', filter: 'active' }

jsonp

function jsonp(url: string, params: { [key: string]: any }, callbackName: string): Promise<any> {
    return new Promise((resolve, reject) => {
        // 创建一个全局回调函数,用于接收请求返回的数据
        (window as any)[callbackName] = (data: any) => {
            delete (window as any)[callbackName]; // 请求完成后删除全局回调函数
            document.body.removeChild(script); // 移除script标签
            resolve(data); // 解析Promise,返回数据
        };

        // 将请求参数和回调函数名添加到URL
        const queryString = Object.entries(params)
            .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
            .join("&");
        const finalUrl = `${url}?${queryString}&callback=${callbackName}`;

        // 创建并插入一个script标签,用于发起JSONP请求
        const script = document.createElement("script");
        script.src = finalUrl;
        script.onerror = () => reject(new Error("JSONP request failed")); // 监听错误事件以处理请求失败的情况
        document.body.appendChild(script);
    });
}

// 示例用法
const url = "https://api.example.com/data";
const params = {
    userId: 123,
    accessToken: "abcdefgh"
};
const callbackName = "jsonpCallback";

jsonp(url, params, callbackName)
    .then(data => console.log(data))
    .catch(error => console.error(error));

JSON.stringify

function customJSONStringify(obj: any): string | undefined {
    const seenObjects: any[] = [];

    function stringify(value: any): string | undefined {
        if (typeof value === "number" || typeof value === "boolean" || value === null) {
            return String(value);
        }

        if (typeof value === "string") {
            return `"${value}"`;
        }

        if (typeof value === "undefined" || typeof value === "function" || value instanceof Symbol) {
            return undefined;
        }

        if (seenObjects.indexOf(value) !== -1) {
            throw new TypeError("Converting circular structure to JSON");
        }
        seenObjects.push(value);

        if (Array.isArray(value)) {
            const arr = value.map(item => stringify(item) ?? "null");
            return `[${arr.join(",")}]`;
        }

        const keys = Object.keys(value).filter(key => typeof value[key] !== "function" && typeof value[key] !== "undefined");
        const keyValuePairs = keys.map(key => `"${key}":${stringify(value[key]) ?? "null"}`);
        return `{${keyValuePairs.join(",")}}`;
    }

    return stringify(obj);
}

// 示例用法
const obj = {
    name: "Alice",
    age: 30,
    sayHello: function() {
        console.log("Hello");
    },
    preferences: {
        color: "blue",
        food: "pizza"
    }
};

console.log(customJSONStringify(obj)); // 输出 '{"name":"Alice","age":30,"preferences":{"color":"blue","food":"pizza"}}'

Promise

// 定义Promise的三种状态常量
enum PromiseStatus {
    Pending = "PENDING",
    Fulfilled = "FULFILLED",
    Rejected = "REJECTED"
}

class CustomPromise {
    status: PromiseStatus;
    value: any;
    reason: any;
    onFulfilledCallbacks: Array<(...args: any[]) => void>;
    onRejectedCallbacks: Array<(...args: any[]) => void>;

    constructor(executor: (resolve: (value?: any) => void, reject: (reason?: any) => void) => void) {
        this.status = PromiseStatus.Pending; // 初始状态为Pending
        this.value = null; // 存储成功时的值
        this.reason = null; // 存储失败时的原因
        this.onFulfilledCallbacks = []; // 存储成功时的回调函数
        this.onRejectedCallbacks = []; // 存储失败时的回调函数

        const resolve = (value?: any) => {
            if (this.status === PromiseStatus.Pending) {
                this.status = PromiseStatus.Fulfilled;
                this.value = value;
                this.onFulfilledCallbacks.forEach(callback => callback());
            }
        };

        const reject = (reason?: any) => {
            if (this.status === PromiseStatus.Pending) {
                this.status = PromiseStatus.Rejected;
                this.reason = reason;
                this.onRejectedCallbacks.forEach(callback => callback());
            }
        };

        try {
            executor(resolve, reject);
        } catch (error) {
            reject(error);
        }
    }

    then(onFulfilled?: (value: any) => any, onRejected?: (reason: any) => any): CustomPromise {
        onFulfilled = typeof onFulfilled === "function" ? onFulfilled : value => value;
        onRejected = typeof onRejected === "function" ? onRejected : reason => { throw reason; };

        const promise = new CustomPromise((resolve, reject) => {
            const handleFulfilled = () => {
                try {
                    const result = onFulfilled!(this.value);
                    if (result === promise) {
                        throw new TypeError("Chaining cycle detected for promise");
                    }
                    if (result instanceof CustomPromise) {
                        result.then(resolve, reject);
                    } else {
                        resolve(result);
                    }
                } catch (error) {
                    reject(error);
                }
            };

            const handleRejected = () => {
                try {
                    const result = onRejected!(this.reason);
                    if (result === promise) {
                        throw new TypeError("Chaining cycle detected for promise");
                    }
                    if (result instanceof CustomPromise) {
                        result.then(resolve, reject);
                    } else {
                        resolve(result);
                    }
                } catch (error) {
                    reject(error);
                }
            };

            if (this.status === PromiseStatus.Fulfilled) {
                queueMicrotask(handleFulfilled);
            } else if (this.status === PromiseStatus.Rejected) {
                queueMicrotask(handleRejected);
            } else {
                this.onFulfilledCallbacks.push(() => queueMicrotask(handleFulfilled));
                this.onRejectedCallbacks.push(() => queueMicrotask(handleRejected));
            }
        });

        return promise;
    }

    catch(onRejected?: (reason: any) => any): CustomPromise {
        return this.then(undefined, onRejected);
    }
}

写一个通用的方法来获取地址栏的某个参数对应的值,不能使用正则表达式

// 方法一
function getQueryParam(paramName) {
  const urlParams = new URLSearchParams(window.location.search);
  return urlParams.get(paramName);
}

// 使用示例
const myParamValue = getQueryParam('myParam');
console.log(myParamValue); // 输出myParam参数的值
// 方法二
function getQueryParam(paramName) {
  var params = window.location.search.substr(1).split('&');
  for (let i = 0; i < params.length; i++) {
    let keyValuePair = params[i].split('=');=
    if (keyValuePair[0] === paramName) {
      return decodeURIComponent(keyValuePair[1]);
    }
  }
  return null;
}

// 使用示例
const myParamValue = getQueryParam('myParam');
console.log(myParamValue); // 输出myParam参数的值