手写JavaScript面向对象API底层代码实现

注:本篇博客续上一篇“异步编程函数式编程底层代码手写”,因此编号从13开始

13. instanceof操作符的实现

function imitateInstanceof(left, right) {
    // 开启尾调用优化条件
    "use strict";

    // 检查右值是否有[Symbol.hasInstance]属性
    if (right && !right[Symbol.hasInstance])
        throw new TypeError("类型检查函数的第二个参数必须是类")

    // 如果左值不是引用类型,那么不存在原型链,直接返回false
    if(!left || left && typeof left !== "object" && typeof left !== "function")
        return false

    // 初始值设置
    // 如果getPrototypeOf传入一个原始类型,那么会经历装箱,变成引用类型来寻找原型链,所以之前必须检查left是否是原始类型
    let leftProto = Object.getPrototypeOf(left)
    let rightProto = right.prototype;

    // 使用尾调用优化
    return (function check() {
        if(!leftPrototype)
            return false
        else if(leftPrototype === rightPrototype)
            return true
        else {
            leftPrototype = Object.getPrototypeOf(leftPrototype)
            return check()
        }
    })();
}

14.new操作符的实现

function imitateNew(constructor, ...args) {
    if(typeof constructor !== "function")
        throw new TypeError("构造函数必须是函数")

    // 如果是ES6之前,默认使用组合模式创建对象,构造函数的prototype中只允许有方法,不允许有其它非方法的引用类型
    // 第一步:创建一个新对象
    let target = Object.create(constructor.prototype)

    // 第二步:绑定this
    let result = constructor.call(target, ...args)

    // 第三步:绑定prototype
    Object.setPrototypeOf(target, constructor.prototype)

    // 第四步:判断返回值
    return result instanceof Object ? result : target
}

15.对象浅拷贝实现

function copy(obj) {
    // 如果obj不是引用类型,那么进行装箱操作
    obj = obj instanceof Object ? obj : Object(obj)

    // 初始化浅拷贝目标对象和枚举键
    let target = null, key

    // 实现最基础的常见对象类型:”数组,函数和非JavaScript特殊内置对象“的浅拷贝
    if(obj instanceof Array)
        target = []
    else if(obj instanceof Function)
        // 单个函数对象的浅复制没有明确实际意义,这里返回一个相同的引用
        return obj
    else
        target = new Object

    // 将自有可枚举属性复制到浅拷贝目标对象,不包括Symbol
    for([key, target[key]] of Object.entries(obj));

    // 将自有可枚举的Symbol属性复制到浅拷贝目标对象
    for(let p of Object.getOwnPropertySymbols(obj))
        if(Object.getOwnPropertyDescriptor(obj, p).enumerable)
            target[p] = obj[p]

    return target
}

16.Object.assign()实现

Object.$assign = function(target, ...sources) {
    // 处理参数,如果参数不是引用类型,那么进行装箱
    target = target instanceof Object ? target : Object(target)
    sources = sources.map(el => el instanceof Object ? el : Object(el))

    // 下面操作类似于单个对象的浅拷贝
    let key
    sources.forEach(obj => {
        // 自有可枚举属性不包括符号属性复制到目标对象target
        for([key, target[key]] of Object.entries(obj));
        // 自有可枚举符号属性复制到目标对象target
        for(let p of Object.getOwnPropertySymbols(obj))
            if(Object.getOwnPropertyDescriptor(obj, p).enumerable)
                target[p] = obj[p]
    })

    return target
}

17.扩展操作符合并对象实现

Object.$expand = function(target, ...sources) {
    // 处理参数,如果参数不是引用类型,那么进行装箱
    target = target instanceof Object ? target : Object(target)
    sources = sources.map(el => el instanceof Object ? el : Object(el))

    // 扩展操作符会返回一个新对象
    let res = {}

    // 复制操作
    function copy(obj) {
        for(let [key, value] of Object.entries(obj))
            // 扩展操作符复制后不存在访问器属性,在此设置属性特性,使之一定不存在
            Object.defineProperty(res, key, {
                value: value,
                enumerable: true,
                configurable: true,
                writable: true
            })
        // 自有可枚举符号属性复制到目标对象target
        for(let p of Object.getOwnPropertySymbols(target))
            if(Object.getOwnPropertyDescriptor(target, p).enumerable)
                Object.defineProperty(obj, p, {
                    value: target[p],
                    enumerable: true,
                    configurable: true,
                    writable: true
                })
    }

    // 复制源对象
    sources.forEach(copy)

    // 复制目标对象
    copy(target)

    return res
}

18.函数的深复制(实际应用中没有任何意义,实现纯属娱乐)

// 函数的属性都是只读的,在这里重新复制一个函数没什么意义
// 如果考虑构造函数上定义静态方法,还有的考虑,但下面不会考虑这一点
function copyFunction(target) {
    let func = target.toString(), args, body

    // 获取非箭头函数的参数,注意ES6增强写法的函数也没有prototype
    if(target.prototype || func.startsWith(`${target.name}`)) {
        args = func.slice(func.indexOf("(") + 1, func.indexOf(")")).split(",")
        // 获取函数体
        body = func.slice(func.indexOf("{") + 1, func.lastIndexOf("}"))
        // 递归调用时函数名无法使用,将其改为arguments.callee调用递归
        body = body.replaceAll("this." + target.name, "arguments.callee")
    } else {
        if(func.startsWith("("))
            // 箭头函数参数非简写形式
            args = func.slice(func.indexOf("(") + 1, func.indexOf(")")).split(",")
        else
            // 箭头函数单参数简写形式
            args = func.slice(0, func.indexOf("=>")).trim().split(",")

        if(func.endsWith("}"))
            // 箭头函数函数体非简写形式
            // 对象中的箭头函数方法中无法使用函数名调用自己来进行递归,这里不用做递归处理
            body = func.slice(func.indexOf("{") + 1, func.lastIndexOf("}"))
        else
            // 箭头函数函数体简写形式
            // 对象中的箭头函数方法中无法使用函数名调用自己来进行递归,这里不用做递归处理
            body = "return " + func.slice(func.indexOf("=>") + 2).trim()
    }
    // Function只能创建匿名函数
    return new Function(...args, body)
}

19.对象的深复制

// 由于18中提到函数深复制没有意义,所以当类型是函数时设置为递归边界,直接返回就行
function deepClone(obj) {
    let wm = new WeakMap()

    // 深复制非函数属性
    function copy(key, value, target, obj) {
        // 深复制要复制属性的特性
        if (value instanceof Object) {
            let descriptor = Object.getOwnPropertyDescriptor(obj, key)
            // 如果是引用类型,那么就递归继续复制
            descriptor.value = clone(value)
            Object.defineProperty(target, key, descriptor)
        } else
            Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(obj, key))
    }

    function clone(obj) {
        // 深复制函数参数必须是对象
        obj = obj instanceof Object ? obj : Object(obj)

        // JavaScript特殊内置对象处理
        if (obj instanceof Date) return new Date(obj)
        if (obj instanceof RegExp) return new RegExp(obj)

        if (wm.has(obj))
            return wm.get(obj)

        let target
        // JavaScript非特殊对象处理
        if (typeof obj === "object")
            // 保持继承链
            target = new obj.constructor()
        else if (typeof obj === "function")
            return obj
        else if (Array.isArray(obj))
            target = new Array

        // 解决循环引用
        wm.set(obj, target)

        // 复制所有非符号自有可枚举属性及其特性
        Object.entries(obj).forEach(([key, value]) => copy(key, value, target, obj))

        // 复制所有符号自有可枚举属性及其特性
        Object.getOwnPropertySymbols(obj).forEach(key => {
            if (Object.getOwnPropertyDescriptor(obj, key).enumerable)
                copy(key, obj[key], target, obj)
        })

        return target
    }

    return clone(obj)
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Vanghua

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值