溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何打印Proxy對(duì)象和ref對(duì)象的包

發(fā)布時(shí)間:2022-11-18 10:01:00 來(lái)源:億速云 閱讀:149 作者:iii 欄目:開(kāi)發(fā)技術(shù)

本文小編為大家詳細(xì)介紹“如何打印Proxy對(duì)象和ref對(duì)象的包”,內(nèi)容詳細(xì),步驟清晰,細(xì)節(jié)處理妥當(dāng),希望這篇“如何打印Proxy對(duì)象和ref對(duì)象的包”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來(lái)學(xué)習(xí)新知識(shí)吧。

目標(biāo)

我希望新的console.log可以像現(xiàn)在的console.log一模一樣,只是當(dāng)打印Proxyref對(duì)象時(shí)可以直接輸出它的源對(duì)象或ref.value。并且,還保留記錄當(dāng)前文件和行數(shù)的功能,可以讓我看到到底是哪個(gè)文件哪個(gè)步驟執(zhí)行的打印。

結(jié)果

如何打印Proxy對(duì)象和ref對(duì)象的包

但退而求其次,我用console.traceError.stack兩種方式十分簡(jiǎn)陋的完成了這個(gè)目標(biāo)。

如何打印Proxy對(duì)象和ref對(duì)象的包

實(shí)現(xiàn)(直接看源碼的同學(xué)可以略過(guò))

判斷一個(gè)對(duì)象是否是Proxy

這個(gè)不好判斷,Vue3添加了isProxy 方法,但如果不是Vue環(huán)境的話,那這個(gè)方法就失效了。 而且就這么一個(gè)簡(jiǎn)單的小功能,實(shí)在沒(méi)必要依賴其他的包。 最終是選擇在用戶new Proxy之前,把Proxy對(duì)象改造。

// 記錄用戶new Proxy操作的所有對(duì)象
// WeakSet,WeakMap,都是弱引用,不干預(yù)其他模塊的垃圾回收機(jī)制
export const proxyMap = new WeakMap()
let OriginalProxy = null
export function listenProxy() {
    if (OriginalProxy) { // 防止用戶多次調(diào)用監(jiān)聽(tīng)
        return
    }
    OriginalProxy = window.Proxy
    window.Proxy = new Proxy(Proxy, {
        construct(target, args) {
            const newProxy = new OriginalProxy(...args)
            proxyInstances.set(newProxy, target)
            return newProxy
        },
        get(obj, prop) {
            // https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Symbol/hasInstance
            if (prop === Symbol.hasInstance) { // 監(jiān)控 `instanceof` 關(guān)鍵字
                return instance => proxyMap.has(instance)
            }
            // https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect/get
            return Reflect.get(...arguments)
        }
    })
}
export function unListenProxy() {
    window.Proxy = OriginalProxy || window.Proxy
}

輸出用戶log的源對(duì)象

按說(shuō)我們上一步已經(jīng)監(jiān)控了用戶動(dòng)作,可以獲取源對(duì)象,等用戶log的時(shí)候,我們直接輸出源對(duì)象就可以了。但這也有個(gè)問(wèn)題,Proxy畢竟不是普通的對(duì)象,通過(guò)Proxy獲取的結(jié)果,很可能跟源對(duì)象沒(méi)有一毛錢關(guān)系。所以只能通過(guò)深克隆返回源對(duì)象的值,但這也有個(gè)問(wèn)題,就是對(duì)于某些不能遍歷的對(duì)象或?qū)傩裕痛蛴〔涣肆?amp;hellip;…

問(wèn)題貌似鎖死了,但,我們實(shí)際運(yùn)用中,只是為了簡(jiǎn)簡(jiǎn)單單輸出一個(gè)不用展開(kāi)的源對(duì)象而已,甚至運(yùn)用場(chǎng)景都特別單一:Vue3! 用戶如果覺(jué)得打印的不準(zhǔn)確,換一個(gè)api不完了嗎,比如我們監(jiān)控的是console.log,那用戶就用console.info一樣能輸出相同的結(jié)果。 把選擇權(quán)交給用戶就好了。在引用包的時(shí)候,再寫多一個(gè)配置項(xiàng),讓用戶自己選平時(shí)的使用場(chǎng)景哪個(gè)正確結(jié)果比較多,就選哪個(gè)。想要完全正確,就換一個(gè)其他的api。

我簡(jiǎn)直是個(gè)天才,哈哈哈

export function getOrg(obj) {
    return proxyMap.get(obj)
}
// 深克隆
export function clone(obj, _refs = new WeakSet()) {
    if (obj === null || obj === undefined) return null
    if (typeof obj !== 'object') return obj
    if (obj.constructor === Date) return new Date(obj)
    if (obj.constructor === RegExp) return new RegExp(obj)
    const newObj = new obj.constructor() //保持繼承的原型
    for (const key in obj) {
        if (obj.hasOwnProperty(key)) {
            const val = obj[key]
            if (typeof val === 'object' && !_refs.has(val)) {
                newObj[key] = clone(val)
            } else {
                newObj[key] = val
            }
        }
    }
    return newObj
}

最后暴露出去給用戶調(diào)用

import { listenProxy, unListenProxy, clone, getOrg } from "./until";
let config = {
    key: 'log', // any String
    type: 'trace', // 'trace' | 'error' | 'any String'
    cloneProxy: getOrg
}
let Vue = {}
export default function (obj = {}, vue) {
    Vue = vue || {}
    config = { ...config, ...obj }
    if (obj.copy === 'clone') {
        config.cloneProxy = clone
    }
    listenLog(config)
}
// ----------------------------------------
const { groupCollapsed, groupEnd, trace, log } = console
// const type = 'trace' | 'error' | ''
function listenLog() {
    const isRef = Vue.isRef || (obj => {
        return typeof obj === 'object' && !!obj.constructor && obj.constructor.name === 'RefImpl'
    })
    const unref = Vue.unref || (obj => obj.value)
    const { key, type, cloneProxy } = config
    if (!key) {
        console.error('Missing required parameter: key')
    }
    listenProxy() // 為 new Proxy 對(duì)象添加 `instanceof` 支持
    console[key] = function (...arr) {
        const newArr = arr.map(i => {
            if (isRef(i)) {
                return unref(i)
            } else if (i instanceof Proxy) {
                return cloneProxy(i)
            } else {
                return i
            }
        })
        groupCollapsed(...newArr)
        // 以 trace
        if (type === 'trace') {
            // trace(...newArr)
            console.log('第二行即為調(diào)用者所在的文件位置')
            trace('The second line is the file location of the caller')
            groupEnd()
            return
        }
        let stack = new Error().stack || ''
        // stack = stack.replace('Error', 'Log')
        if (type === 'error') {
            log('%c這不是一個(gè)錯(cuò)誤,請(qǐng)點(diǎn)擊第二行的"at",跳轉(zhuǎn)到對(duì)應(yīng)的文件', 'color: #008000')
            log('%cThis is not an error. Please click "at" in the second line to jump to the corresponding file', 'color: #008000')
            log(stack)
            groupEnd()
            return;
        }
        // 簡(jiǎn)單輸入模式,控制臺(tái)看起來(lái)是簡(jiǎn)單了,卻失去了點(diǎn)擊鏈接直接跳轉(zhuǎn)到對(duì)應(yīng)文件的功能
        const stackArr = stack.match(/at.*\s/g) || []
        log(stackArr[1])
        groupEnd()
    }
}

再加上一點(diǎn)ts的解釋文件,那這個(gè)庫(kù)就能運(yùn)行在所有平臺(tái)了

讀到這里,這篇“如何打印Proxy對(duì)象和ref對(duì)象的包”文章已經(jīng)介紹完畢,想要掌握這篇文章的知識(shí)點(diǎn)還需要大家自己動(dòng)手實(shí)踐使用過(guò)才能領(lǐng)會(huì),如果想了解更多相關(guān)內(nèi)容的文章,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI