JS基础知识总结(持续更新)

66 阅读3分钟

1、instanceof的原理是什么?

概念:instanceof 运算符 用于检测 构造函数的 prototype 属性 是否出现在某个 实例对象原型链 上。

基本概念

  • 函数的 prototype 属性:函数的原型对象,用于定义实例共享属性和方法。
  • 构造函数:用于创建和初始化对象。
  • 构造函数的实例对象:通过 new 一个 构造函数 得到的 对象。

构造函数 VS 构造函数的 prototype 属性

  • 分离职责:构造函数负责初始化实例属性,prototype 负责存储共享方法。
  • 原型链机制:通过 __proto__ 连接实例和 prototype,实现属性和方法的继承。
  • 内存优化:共享方法只需存储一次,避免重复定义。

速记

  • 可以把 instanceof 理解为 “是否是…… 的实例”。
  • arr instanceof Array 就可以理解为 “数组 arr 是否是 Array 这个构造函数创建的实例”。

缺点

  • 由于instanceof是基于原型链的检查,因此如果某个对象的原型链比较深,那么检查的效率会比较低。
  • 只能用于检查对象是否是某个构造函数的实例,不能用于基本类型(如字符串、数字等)的检查。

手写

    // let arr = [1,2,3]; arr instanceof Array; => true
    function myInstanceof(obj, constructor) {
        let proto = Object.getPrototypeOf(obj)
        while (proto) {
            if(proto === constructor.prototype) return true
            proto = Object.getPrototypeOf(proto)
        }
        return false
    }

2、new 及 对象实例创建的三种方式

概念:运算符,用于创建 构造函数或类的实例。其核心流程如下:

  1. 创建空对象:在内存中分配一个新对象。
  2. 设置原型链:将新对象的 __proto__ 指向构造函数的 prototype
  3. 绑定 this:将构造函数中的 this 绑定到新对象。
  4. 执行构造函数:初始化实例属性。
  5. 返回实例:默认返回新对象(除非构造函数显式地返回其他对象)。

手写

function myNew (Constructor, ...args) {
    var obj = Object.create(Constructor.prototype); // 1,2
    var result = Constructor.apply(obj, args); // 3,4
    return (typeof result === 'object' && result !== null) ? result : obj; // 5
}

拓展: 创建对象一共有三种方式:

  • new操作符:用于创建构造函数或类的实例,原型链固定为构造函数的 prototype
  • 对象字面量:隐式调用 Object 构造函数,原型链固定为 Object.prototype
  • Object.create():显式指定原型,灵活性更高,不依赖构造函数。
const prototype = { a: 1 };
const obj = Object.create(prototype, { b: { value: 2 } }); // b 默认为不可枚举属性,如果想要能够被枚举,需要设置b 的 enumerable属性 为 true。

// 原型链:obj.__proto__ === prototype

三者的创建原理不同,new 和 {} 基于构造函数,而 Object.create() 直接控制原型链。

易错点加深apply:应用的意思,把一个函数应用到某个对象上,并以数组的形式传递参数。

3. defer 和 async

概念

  • defer(延迟的顺序执行,执行被延迟到 HTML 解析完成后)。
  • async(异步的无序执行,加载完成立即执行,执行顺序取决于网络加载速度)。

实际应用建议

  1. 优先使用 async:若脚本无依赖且无需按顺序执行,可提升首屏渲染速度。
  2. 使用 defer:若脚本需按顺序执行或依赖 DOM 结构(如 jQuery 插件)。
  3. 避免同时使用:两者不可叠加,否则以 async 为准。
  4. 内联脚本无效defer 和 async 仅对外部脚本(src 属性)生效。

4、手写防抖

function debounce(func, delay, immediate = false) {
    let timer = null

    return function(...arg) {
        const execNow = immediate && !timer
        if(timer) clearTimeout(timer)
        timer = setTimeout(() => {
            if(!immediate) {
                func.apply(this ,arg)
            }
            timer = null
        }, delay)
        if(execNow) {
            func.apply(this ,arg)
        }
    }
}

5、手写去重

function unique(arr) {
    const result = []
    const seen = new Map()
    for (let item of arr) {
        if(!seen.has(item)) {
            seen.set(item, true)
            result.push(item)
        }
    }
    return result
}

6、实现EventBus

class EventBus {
    constructor() {
        this.events = {}
    }
    on(eventName, callback) {
        if (!this.events[eventName]) {
            this.events[eventName] = []
        }
        this.events[eventName].push(callback)
    }
    emit(eventName, ...args) {
        if(this.events[eventName]){
            [...this.events[eventName]].forEach(fn => fn(...args))
        }
    }
    off(eventName, callback) {
        if(this.events[eventName]){
             this.events[eventName].filter(cb => cb != callback)
        }
         
        if(!this.events[eventName].length) {
            delete this.events[eventName]
        }
    }
    once(eventName. callback) {
        
    }
}