02.20 手搓js笔记

手动实现 call

call 是 JavaScript 中的一个方法,用于改变函数内部 this 的指向,并传入参数。以下是手动实现 call 的代码:

Function.prototype.myCall = function (context, ...args) {
    // 如果没有传入context, 默认为window
    context = context || window
    // 创建一个唯一的符号,避免与对象其他属性冲突
    let fn = Symbol('fn')
    // 将当前函数赋值给context对象的临时方法
    context[fn] = this
    // 调用context上的临时方法并传入参数
    const result = context[fn](...args)
    // 删除临时方法,避免污染context
    delete context[fn]
    // 返回函数的执行结果
    return result
}

思路:

  1. this 指向调用 myCall 的函数。
  2. 如果没有传入 context,默认为 window
  3. 通过 Symbol 创建一个唯一的属性,来避免和其他属性冲突。
  4. 最后调用 context 上的这个临时方法,并删除它。

手动实现 apply

applycall 类似,区别在于参数传递方式为数组。以下是手动实现 apply 的代码:

Function.prototype.myApply = function (context, args = []) {
    // 如果没有传入context, 默认为globalThis(当前环境的全局对象)
    context = context || globalThis
    // 创建一个唯一的符号,避免与对象其他属性冲突
    const fn = Symbol('fn')
    // 将当前函数赋值给context对象的临时方法
    context[fn] = this
​
    // 调用context上的临时方法并传入参数数组
    const result = context[fn](...args)
    // 删除临时方法,避免污染context
    delete context[fn]
    // 返回函数的执行结果
    return result
}

思路:

  • 通过 Symbol 创建一个唯一属性,临时改变 this 的指向,调用函数并传入参数数组。

手动实现 bind

bind 返回一个新的函数,新的函数可以在调用时指定 this 的指向,并且支持链式调用。以下是手动实现 bind 的代码:

Function.prototype.myBind = function (context, ...args) {
    // 保留原函数的引用
    const that = this
​
    // 创建一个新的函数
    const fnC = function (...argsC) {
        // 判断新函数是否是通过new调用的(实例化了)
        return that.apply(this instanceof fnC ? this : context, [...args, ...argsC])
    }
​
    // 继承原函数的原型链
    fnC.prototype = Object.create(this.prototype)
    // 返回新的函数
    return fnC
}

思路:

  1. bind 返回一个新函数,保证能够在调用时重新指定 this
  2. 利用 apply 来确保参数合并,并支持链式调用。

手动实现 new

手动实现 new 的过程如下:

function myNew(constructor, ...args) {
    // 1. 创建一个空对象
    const obj = {}
    // 2. 空对象的原型指向构造函数的原型
    obj.__proto__ = constructor.prototype
    // 3. 执行构造函数,并将新对象作为this传入
    const result = constructor.apply(obj, args)
​
    // 4. 如果构造函数返回的是对象,则返回该对象;否则,返回新创建的对象
    return result instanceof Object ? result : obj
}

思路:

  1. 创建一个空对象。
  2. 将空对象的 __proto__ 指向构造函数的 prototype
  3. 调用构造函数,保证 this 指向新创建的对象。
  4. 如果构造函数返回的是对象,则返回该对象;否则,返回创建的空对象。

实现一个简易 Promise

以下是一个简易实现的 Promise 类,支持链式调用并且遵循 Promise/A+ 规范:

class NewPromise {
    // 根据Promise/A+规范 定义状态值
    static PENDING = '待定'
    static FULFILLED = '解决'
    static REJECT = '拒绝'
​
    constructor(fn) {
        // 定义结果值和初始状态
        this.result = null
        this.status = NewPromise.PENDING
        // 创建成功/失败回调数组
        this.resolveCallback = []
        this.rejectCallback = []
        // 执行传入的函数并传入resolve和reject
        fn(this.resolve, this.reject)
    }
​
    // 定义成功方法
    resolve = result => {
        // 使用setTimeout确保resolve在事件循环末尾执行
        setTimeout(() => {
            // 状态不可逆,只有PENDING状态才能继续执行
            if (this.status !== NewPromise.PENDING) return
            this.status = NewPromise.FULFILLED
            this.result = result
            // 调用所有成功回调
            this.resolveCallback.forEach(callback => callback(result))
        })
    }
​
    // 定义失败方法
    reject = result => {
        setTimeout(() => {
            if (this.status !== NewPromise.PENDING) return
            this.status = NewPromise.REJECT
            this.result = result
            // 调用所有失败回调
            this.rejectCallback.forEach(callback => callback(result))
        })
    }
​
    // 定义then方法,支持链式调用
    then = (onResolve, onReject) => {
        // 如果then的参数不是function,则使用默认函数
        typeof onResolve !== 'function' ? (onResolve = value => value) : null
        typeof onReject !== 'function'
            ? (onReject = reason => {
                  throw new Error(reason instanceof Error ? reason.message : reason)
              })
            : null
​
        // 返回新的Promise对象
        return new NewPromise((resolve, reject) => {
            // 处理成功的回调
            const resolveFn = res => {
                try {
                    const x = onResolve(res)
                    // 如果返回值是Promise,继续链式调用
                    x instanceof NewPromise ? x.then(resolve, reject) : resolve(x)
                } catch (error) {
                    reject(error)
                }
            }
​
            // 处理失败的回调
            const rejectFn = err => {
                try {
                    const x = onReject(err)
                    // 如果返回值是Promise,继续链式调用
                    x instanceof NewPromise ? x.then(resolve, reject) : resolve(x)
                } catch (error) {
                    reject(error)
                }
            }
​
            // 根据状态执行对应的回调
            switch (this.status) {
                case NewPromise.PENDING:
                    // 如果是PENDING状态,保存回调
                    this.resolveCallback.push(resolveFn)
                    this.rejectCallback.push(rejectFn)
                    break
​
                case NewPromise.FULFILLED:
                    // 如果是已解决状态,直接执行成功回调
                    setTimeout(() => onResolve(this.result))
                    break
​
                case NewPromise.REJECT:
                    // 如果是已拒绝状态,直接执行失败回调
                    setTimeout(() => onReject(this.result))
                    break
            }
        })
    }
}

思路:

  • Promise 支持链式调用,采用 then 方法处理成功和失败的回调。
  • 通过 setTimeout 确保 resolvereject 在事件循环末尾执行。
  • 支持 PENDINGFULFILLEDREJECT 三个状态的转换。

手动实现深拷贝

以下是手动实现深拷贝的代码,使用 WeakMap 来处理循环引用:

function deepClone(obj, map = new WeakMap()) {
    // 如果不是对象,则直接返回原值
    if (typeof obj !== 'object') return obj
​
    // 如果WeakMap中已记录该对象,说明是循环引用,直接返回对应的克隆结果
    if (map.has(obj)) return map.get(obj)
​
    // 判断是数组还是对象
    const isArray = Array.isArray(obj) || Object.prototype.toString(obj) === '[object Array]'
    
    // 创建目标对象或数组
    const result = isArray ? [] : {}
​
    // 将当前对象和对应的克隆对象加入WeakMap
    map.set(obj, result)
​
    // 如果是数组,则迭代数组;如果是对象,则迭代对象的所有属性
    const iteratorArray = isArray ? obj : Object.keys(obj)
​
    iteratorArray.forEach((val, key) => {
        // 如果是对象,key为属性名;如果是数组,key为索引
        if (!isArray) key = val
        // 递归深拷贝
        result[key] = deepClone(obj[key], map)
    })
​
    return result
}

思路:

  • 使用 WeakMap 来避免循环引用导致的死循环。
  • 使用递归来拷贝对象和数组,确保深拷贝。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值