微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

为什么当我代理它时,这个 setter 没有给我截获的值?

如何解决为什么当我代理它时,这个 setter 没有给我截获的值?

我确定我创建代理的方式是正确的,因为当我将属性登录到 devtools 控制台时,我可以看到设置器已被代理。

问题不会发生在作为函数属性上,它们的拦截工作正常。

我主要是按照这个 Question 的回答来构建这个的。尽管如此,我必须说这个答案看起来很复杂,所以我从中剥离了很多东西,以采用简单的风格,您可以从下面的代码中看到。

class Hook {
    constructor(object) {
        if (object) {
            this.object = object;
        }
    }

    add(object) {
        this.object = object;
    }


    proxy(handler) {
        return new Proxy(this.object,handler);
    }
};


const hook = new Hook();

Object.getownPropertyNames(CanvasRenderingContext2D.prototype).forEach(function (property) {
    let propertyDescription = Object.getownPropertyDescriptor(CanvasRenderingContext2D.prototype,property);

    const proxyPropertyDescription = {
        configurable: propertyDescription.configurable,enumerable: propertyDescription.enumerable
    }

    if (typeof propertyDescription.value === "function") {
        const handle = {
            apply(target,thisArg,args) {
                // forward invocation to underlying function
                console.log("apply",target,args)

                return target.apply(thisArg,args)
            }
        };

        hook.add(propertyDescription.value)

        proxyPropertyDescription.writable = propertyDescription.writable;
        proxyPropertyDescription.value = hook.proxy(handle,true);
    }

    if (propertyDescription.set) {
        const handle = {
            set(target,key,value) {

                // forward access to underlying property
                Reflect.set(target,value);

                console.log("set",value);
            }
        };

        hook.add(propertyDescription.set)

        proxyPropertyDescription.set = hook.proxy(handle,true);
    }

    Object.defineProperty(CanvasRenderingContext2D.prototype,property,proxyPropertyDescription);
})

var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");

// My target but it never get proxified 
ctx.font = "14px 'Arial'";

// But functions like this works....
ctx.fill("red");


// I can see that on Devtools when I log this Property has been set to Proxy
console.log(Object.getownPropertyDescriptor(CanvasRenderingContext2D.prototype,"font"));

解决方法

不要将 getter/setter 与代理混用。使用其中之一,不要同时使用。

这是一个仅使用属性描述符的解决方案:

const hook = {
    wrapMethod(name,method) {
        return function(...args) {
            console.log("applying method "+name,args);
            return method.apply(this,args);
        };
    },wrapSetter(name,setter) {
        return function(value) {
            console.log("setting "+name,value);
            return setter.call(this,value);
        };
    }
};

const proto = CanvasRenderingContext2D.prototype;
for (const name of Object.getOwnPropertyNames(proto)) {
    const descriptor = Object.getOwnPropertyDescriptor(proto,name);

    if (!descriptor.configurable) {
        console.log("Cannot hook onto immutable ."+name);
        continue;
    } else if (typeof descriptor.value == "function") {
        descriptor.value = hook.wrapMethod(name,descriptor.value);
    } else if (descriptor.set) {
        descriptor.set = hook.wrapSetter(name,descriptor.set);
    } else {
        console.log("Did not hook onto ."+name,descriptor);
    }
    Object.defineProperty(proto,name,descriptor);
}


var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
ctx.font = "14px 'Arial'";
ctx.fillStyle = "red";
ctx.fillRect(130,190,40,60);

这是一个仅使用代理的解决方案:

const hook = {
    wrapMethod(name,value);
        };
    }
};

const contextProto = CanvasRenderingContext2D.prototype;
const canvasProto = HTMLCanvasElement.prototype;
const getContext = canvasProto.getContext;
canvasProto.getContext = function(...args) {
    const ctx = getContext.apply(this,args);
    if (ctx && Object.getPrototypeOf(ctx) == contextProto)
        Object.setPrototypeOf(ctx,proxy);
    return ctx;
};

const proxy = new Proxy(contextProto,{
    get(target,receiver) {
         const value = Reflect.get(target,receiver);
         return typeof value == "function" ? hook.wrapMethod(name,value) : value;
    },set(target,value,receiver) {
        hook.wrapSetter(name,function(value) {
            Reflect.set(target,this);
        }).call(receiver,value);
    },});


var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
ctx.font = "14px 'Arial'";
ctx.fillStyle = "red";
ctx.fillRect(130,60);

这是相当低效的,因为方法和设置器在每次访问时都被包装,而不仅仅是一次。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。