JS Proxying HTML5画布上下文

the*_*dow 4 javascript html5 canvas ecmascript-6 es6-proxy

我希望代理画布API,以便我可以测试抽象方法实际绘制到画布,但是我遇到问题,在代理后我得到一个错误:

'strokeStyle' setter called on an object that does not implement interface CanvasRenderingContext2D

此代码已简化但引发相同的错误:

/** !NB: This snippet will probably only run in Firefox */
var canvas = document.createElement("canvas");
canvas.width = 100;
canvas.height = 100;
canvas.style.backgroundColor = '#FF0000';

var ctx = canvas.getContext("2d");                          
var calls = [];

var handler = {
    get( target, property, receiver ) {

        if ( typeof ctx[property] === 'function' ){
            return function( ...args ){
                calls.push( { call: property, args: args } )
                return ctx[property]( ...args );
            };
        }

        return ctx[property];
    }
};

try {
    document.body.appendChild(canvas);
    var proxy = new Proxy( ctx, handler );
    
    proxy.scale( 1, 1 );
    proxy.strokeStyle = '#000000';
    
    canvas.getContext = function(){
        return proxy;  
    };
}
catch( e ) {
    document.getElementById('message').innerHTML = 'Error: ' + e.message;   
}
Run Code Online (Sandbox Code Playgroud)
<div id="message"></div>
Run Code Online (Sandbox Code Playgroud)

有什么想法吗?

aps*_*ers 5

您可以通过set在处理程序上定义方法来修复此错误:

set(target, property, value, receiver) {
    target[property] = value;
}
Run Code Online (Sandbox Code Playgroud)

这个错误的原因可能看起来有点奇怪.CanvasRenderingContext2D实例没有自己的strokeStyle属性.相反,CanvasRenderingContext2DPrototype(每个CanvasRenderingContext2D实例的原型)都有一个访问器属性,其set/ getcomponents将设置并获取实例的笔触样式值:

> ctx.hasOwnProperty("strokeStyle")
false

> Object.getOwnPropertyDescriptor(ctx.__proto__, "strokeStyle")
Object { get: strokeStyle(), set: strokeStyle(), enumerable: true, configurable: true }
Run Code Online (Sandbox Code Playgroud)

(如果您有兴趣了解有关此模式的更多信息,请查看我对JSON.parse的回答,而不是在循环对象出错.)

这里的问题是this提供给CanvasRenderingContext2DPrototype.strokeStylesetter的是proxy对象,而不是实际的ctx对象.也就是说,当我们仅在代理上设置属性时:

proxy.isAFake = true;
Run Code Online (Sandbox Code Playgroud)

并在重新定义的setter中测试它:

Object.defineProperty(ctx.__proto__, "strokeStyle", {
    set: function() {
        console.log("strokeStyle setter called for proxy?", this.isAFake);
    }
});
Run Code Online (Sandbox Code Playgroud)

我们看到setter记录了仅代理属性:strokeStyle setter called for proxy? true.

无论出于何种原因,setter on CanvasRenderingContext2DPrototype.strokeStyle只接受一个真实的CanvasRenderingContext2D实例,而不是代理实例.