JS手写合集

浅比较

/**
 * 浅比较(如果是对象,只比较第一层属性,与深比较/深拷贝相比性能更好,平常够用)
 * @param obj1 任意基本类型或引用类型
 * @param obj2 任意基本类型或引用类型
 * @returns 是否同一对象
 */
function shallowEqual(obj1: any, obj2: any): boolean {
    // 同基本类型,或同引用地址,返回true
    if (obj1 === obj2) return true;

    // 非对象类型 或 为null 返回 false
    if (typeof obj1 !== 'object' || obj1 === null || typeof obj2 !== 'object' || obj2 == null) return false

    const keys1 = Object.keys(obj1)
    const keys2 = Object.keys(obj2)
    if (keys1.length !== keys2.length) return false

    // 如果obj2中没有obj1中的某个属性,或该属性值不相等返回false
    for (let key of keys1) {
        if (!obj2.hasOwnProperty(key) || obj1[key] !== obj2[key]) {
            return false
        }
    }

    return true
}

const obj11 = { a: 1 }
const obj12 = { a: 1 }
console.log(shallowEqual(obj11, obj12)); // true

手写深拷贝

浅拷贝,如果复制的对象是基本数据类型,拷贝的就是值,如果是引用类型,拷贝的就是内存地址,一个对象改变会影响另一个对象

JSON.parse(JSON.stringify()) 基本能用版

  1. JSON.parse(JSON.stringify()),写法简单,但无法拷贝函数,循环引用,或特殊引用类型.

forIn遍历,递归自身,丐版

function clone(target) {
    if (typeof target !== 'object') return target;

    let cloneTarget = {};
    for (const key in target) {
        cloneTarget[key] = clone(target[key]);
    }
    return cloneTarget
};

cloneTarget=[]兼容数组,map解决循环引用,够用了版

/**
 * 深拷贝
 * @param {Object} target 要拷贝的对象
 * @param {WeakMap} map 用于存储循环引用对象的地址
 */
function deepClone(target, map = new WeakMap()) {
    if (typeof target !== 'object') return target

    if (map.get(target)) {
        return map.get(target)
    }

    const cloneTarget = Array.isArray(target) ? [] : {}
    map.set(target, cloneTarget)

    for (const key in target) {
        if (Object.hasOwnProperty.call(target, key)) {
            cloneTarget[key] = deepClone(target[key], map);
        }
    }
    return cloneTarget
}

WeakMap弱引用 与 {}强引用

Map 和 WeakMap 的区别

WeakMap是ES6中新增的一种集合类型,叫做弱映射。它和Map是兄弟关系,与Map的区别在于这个弱字,API还是Map那套API。

Map的键可以是任意类型,WeakMap只接受对象作为键,不接受其它类型的值作为键

Map的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键;WeakMap的键是弱引用,如果创建了一个弱引用对象,不会被垃圾回收关注,如果不再需要,WeakMap 中的键名对象和所对应的键值对会自动消失,不再手动删除引用。

Map可以被遍历,WeakMap不能被遍历

forIn循环效率低,while循环效率高,性能优化版

函数类型及特殊引用类型得专门判断

MapSet 等类型得专门判断

手写防抖节流

防抖和节流都是防止某一事件频繁触发

防抖(debounce)

施法前摇,在读条期间再次触发会打断施法,重新读条,直到正常读条结束,触发函数。

function debounce(fn, wait) {
    let timer;
    return function () {
        clearTimeout(timer);
        timer = setTimeout(() => {
            fn.apply(this, arguments);
        }, wait);
    };
}

场景: 浏览器窗口resize,文本编辑器自动保存,输入框智能提示
防抖重在清零 clearTimeout(timer)

节流

节流是加锁 限流防止频繁触发 单位时间内只发生一次

function throttle(fn, wait) {
    let timer;
    return function () {
        if (timer) return
        timer = setTimeout(() => {
            fn.apply(this, arguments);
            timer = null;
        }, wait);
    };
}

防抖首次触发

function debounce(fn, wait, immediate = false) {
    let timer;
    return function (...args) {
        const callNow = immediate && !timer; // 首次立即执行
        clearTimeout(timer);
        timer = setTimeout(() => {
            timer = null; // 允许下次立即执行
            if (!immediate) fn.apply(this, args); // 非立即模式执行
        }, wait);
        if (callNow) fn.apply(this, args);
    };
}

实现一个节流函数? 如果想要最后一次必须执行的话怎么实现?

加上对上下文的处理

手写排序

冒泡排序 插入排序 选择排序

快速排序 归并排序

类的继承

手写扁平数组转tree