Can I use proxies with my own browser objects (HTMLElement, Canvas2DRenderingContext, ...)?

What I tried to do. I wanted to split one canvas (because what I'm doing is very hard), and so I thought I was creating a limited resource manager. You ask him about the resource by promise, in this case a Canvas2DRenderingContext. It will transfer the context to the revokable proxy. When you are done, you need to call release, which returns the canvas to the limited resource manager so that it can transfer it to someone else. And it cancels the proxy server so that the user cannot accidentally use the resource again.

Unless I am doing a proxy Canvas2DRenderingContext, it fails.

const ctx = document.createElement('canvas').getContext('2d');
const proxy = new Proxy(ctx, {});

// try to change the width of the canvas via the proxy
test(() => { proxy.canvas.width = 100; });  // ERROR

// try to translate the origin of via the proxy
test(() => { proxy.translate(1, 2); });     // ERROR


function test(fn) {
  try {
    fn();
  } catch (e) {
    console.log("FAILED:", e, fn);
  }
}
Run codeHide result

Uncaught TypeError: Illegal invocation Chrome TypeError: 'get canvas' called on an object that does not implement interface CanvasRenderingContext2D. Firefox

?

: , . - . - JavaScript, , , -. , . Mozilla , , HTMLElement, , , , someElement.appendChild(proxiedElement), , , , DOM .

, Proxies JS. ( ). .

const img = document.createElement('img')
const proxy = new Proxy(img, {});
console.log(proxy.src);
Hide result

. , JavaScript

function testNoOpProxy(obj, msg) {
  log(msg, '------');
  const proxy = new Proxy(obj, {});
  check("get property:", () => proxy.width);
  check("set property:", () => proxy.width = 456);
  check("get property:", () => proxy.width);
  check("call fn on object:", () => proxy.getContext('2d'));
}

function check(msg, fn) {
  let success = true;
  let r;
  try {
    r = fn();
  } catch (e) {
    success = false;
  }
  log('   ', success ? "pass" : "FAIL", msg, r, fn);
}


const test = {
  width: 123,
  getContext: function() {
    return "test";
  },
};

class Test {
  constructor() {
    this.width = 123;
  }
  getContext() {
    return `Test width = ${this.width}`;
  }
}

const testInst = new Test();
const canvas = document.createElement('canvas');

testNoOpProxy(test, 'plain object');
testNoOpProxy(testInst, 'class object');
testNoOpProxy(canvas, 'native object');



function log(...args) {
  const elem = document.createElement('pre');
  elem.textContent = [...args].join(' ');
  document.body.appendChild(elem);
}
pre { margin: 0; }
Hide result

, FWIW, , , , , , . - ( ), - . ,

  • Proxy ?
  • - , , JavaScript?
  • Proxy .
+6
1
const handlers = {
  get: (target, key) => key in target ? target[key] : undefined,
  set: (target, key, value) => {
    if (key in target) {
      target[key] = value;
    }
    return value;
  }
};

const { revoke, proxy } = Proxy.revocable(ctx, handlers);

// elsewhere
try {
  proxy.canvas.width = 500;
} catch (e) { console.log("Access has been revoked", e); }

- , .
- .

, Proxy.revocable() , - , , , try/catch, , .

, , ( , - , ):

const RevocableAccess = (item, revoked = false) => ({
  access: f => revoked ? undefined : f(item),
  revoke: () => { revoked = true; }
});

const { revoke, access: useContext } = RevocableAccess(ctx);

useContext(ctx => ctx.canvas.width = 500);
revoke();
useContext(ctx => ctx.canvas.width = 200); // never fires

, -, , , . , .

-, , proxy.drawImage.apply(ctx, args) . , , .

, , - Canvas, Image, Audio, Video, Promise (, ) .. Proxies, - , , , .

, :

const { proxy, revoke } = Proxy.revocable(ctx, {
  get(object, key) {
    if (!(key in object)) {
      return undefined;
    }
    const value = object[key];
    return typeof value === "function"
      ? (...args) => value.apply(object, args)
      : value;
  }
});

"" , . , , , bind , this . JS.

... ; - , , , drawImage, : const draw = proxy.drawImage;... , , : const ctx = proxy.canvas.getContext("2d"); ... , .

, , , , DOM.

+2

All Articles