不能从上下文对象继承?
我试图创建一个从上下文对象继承的对象.但是在从我继承的对象调用函数时,浏览器 (Chrome) 会显示 Uncaught TypeError: Illegal invocation.这是基本代码:
I was trying to create an object that inherits from the context object. But upon calling a function from the object that I'm inheriting from, the browser (Chrome) states Uncaught TypeError: Illegal invocation. Here's the basic code:
http://jsfiddle.net/adrianh/BKYfv/1/
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var ctx2 = Object.create(ctx);
ctx.setTransform(1, 0, 0, 1, 0, 0); // identity -- works
alert("ctx works");
ctx2.setTransform(.5, 0, 0, .5, 0, 0); // scale by half -- fails
alert("ctx2 works");
为什么会失败?
我写了一个 makeForwardingObject()
函数来做我想做的事.可以在这里找到.
I wrote a makeForwardingObject()
function that does what I want. It can be found here.
// makeForwardingObject(obj, funcs, attribs)
//
// obj - the object that is being forwarded to
// funcs - array of non enumerable function member names to forward to
// attribs - array of non enumerable attributes to forward to
//
// Makes a forwarding object that forwards all functions calls and attribute
// requests to the forwarded object. In this way, the original object is
// acted upon directly, while you can delete or modify members to your
// object without interfering with the original.
//
// Access to the object being forwarded to is always available using member
// functions applyParent(), callParent(), setParentAttrib() and
// getParentAttrib().
//
// If funcs or attribs are enumerable in the object, they are not added
// a second time.
function makeForwardingObject(obj, funcs, attribs)
{
var _ = { };
Object.defineProperties(_, {
_: { value: obj },
// like obj.apply() except it applys to object being forwarded to.
applyParent : { value: function applyParent(func, args)
{
return this._[func].apply(this._, args);
}},
// like obj.call() except it applys to object being forwarded to.
callParent: { value: function callParent(func)
{
// FF at least doesn't understand arguments.slice(),
// arguments.splice() or arguments.shift(). WTF?!?!
var args=[];
for (i=1; i<arguments.length; ++i)
args[i-1]=arguments[i];
return this._[func].apply(this._, args);
}},
// this is for setting member of object being forwarded to.
setParentAttrib: { value: function setParentAttrib(attrib, val)
{
return this._[attrib]=val;
}},
// this is for getting member of object being forwarded to.
getParentAttrib: { value: function getParentAttrib(attrib, val)
{
return this._[attrib];
}},
});
for (var key in obj)
{
switch (typeof obj[key])
{
case 'function':
_[key] = eval("(function "+key+"() { return this._."+key+".apply(this._, arguments); })");
break;
default:
eval("Object.defineProperty(_, '"+key+"', {"+
"get: function "+key+"() { return this._."+key+"; },"+
"set: function "+key+"(v) { return this._."+key+"=v; },"+
"enumerable: true,"+
"})");
break;
}
}
for (var index in funcs)
{
var key = funcs[index];
if (!_.hasOwnProperty(key))
{
_[key] = eval("(function "+key+"() { return this._."+key+".apply(this._, arguments); })");
}
}
for (var index in attribs)
{
var key = funcs[index];
if (!_.hasOwnProperty(key))
{
eval("Object.defineProperty(_, '"+key+"', {"+
"get: function "+key+"() { return this._."+key+"; },"+
"set: function "+key+"(v) { return this._."+key+"=v; },"+
"enumerable: false,"+
"})");
}
}
return _;
}
// Return a string of all the members in an object. Used for debugging.
function getMembers(obj)
{
var _ = "";
for (key in obj)
{
_ += key + ":" + typeof obj[key] + " = " + obj[key] +"
";
}
return _;
}
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var ctx2 = makeForwardingObject(ctx);
var x = { a: "" };
alert(getMembers(ctx));
alert(getMembers(ctx2));
ctx.setTransform(1, 0, 0, 1, 0, 0); // identity -- works
alert("ctx works");
ctx2.setTransform(.5, 0, 0, .5, 0, 0); // scale by half -- works!
//These are alternate ways to call the forwarded object's member functions:
// ctx2.applyParent('setTransform', [.5, 0, 0, .5, 0, 0]); // scale by half -- works!
// ctx2.callParent('setTransform', .5, 0, 0, .5, 0, 0); // scale by half -- works!
alert("ctx2 works");
ctx2.moveTo(0,0);
ctx2.lineTo(100, 100);
ctx2.stroke();
推荐答案
一个肤浅的答案是因为无法构造画布渲染上下文.使用 CanvasRenderingContext2d()
函数(与 DOM 中的许多其他构造函数一样)将引发 Type 错误:Illegal constructor"
,因为它们应该只在一个中创建来自工厂函数的特定方式.在这种情况下,画布的 .getContext()
方法.
One shallow answer would be because a canvas rendering context can't be constructed. Using the CanvasRenderingContext2d()
function (like many other constructors in the DOM) will throw an Type error: "Illegal constructor"
, because they are supposed to only be created in one specific way from a factory function. In this case the .getContext()
method of the canvas.
尽管使用 RenderingContext2d 作为原型创建了一个新对象,但您可以使用
Despite creating a new Object with a RenderingContext2d as a prototype you can falsely create a rendering context by using
ctx2=Object.create(CanvasRenderingContext2D.prototype);
或
ctx2=Object.create(ctx.constructor.prototype);
为您提供一个完全空白的无状态(无用)渲染上下文对象,它实际上会引发与克隆上下文相同的异常.它甚至没有分配画布.
Giving you a completely blank stateless (and useless) rendering context object, which actually throws the same exceptions as your cloned context. It doesn't even have a canvas assigned.
这不起作用的原因是因为您只继承了对 RenderingContext 原型的公共方法的引用,并且在您的情况下,克隆具有对您通过原型创建的上下文的所有状态的引用链条,但除此之外,它是一个空心体.在 CanvasRenderingContext
构造函数中声明的私有 var
和私有声明的 function
都不会通过原型继承.
The reason this doesn't work is because you only inherit a reference to the the public methods of the RenderingContext prototype, and in your case of the clone has a reference to all the states of the context you created it from via prototype chain, but other than that it's a hollow body. No private var
and no privately declared function
declared inside the CanvasRenderingContext
constructor gets inherited via the prototype.
如果你好奇,可以自己写这种对象
In case you are curious, you can write this kind of object yourself
function nonConstructable(factoryVar){
if(arguments.callee.caller !== Factory){
throw TypeError("Invalid constructor");
}
var privateVar = privateMethod();
privateVar+=factoryVar;
this.publicVar= privateVar;
function privateMethod(){
return 123;
}
}
function Factory(){
var privateFactoryVar = 321;
return new nonConstructable(privateFactoryVar );
}
您会看到这两个对象以这种方式链接,并且您在 nonConstructable
构造函数中执行操作的唯一方法是通过特定的 Factory
构造它.
You see this way these 2 objects are linked and the only way for you to execute the operations inside the nonConstructable
constructor is by constructing it via the specific Factory
.
做更多的研究,似乎 CanvasRenderingContext2D
和 WebGLRenderingContext
计划成为上下文的有效构造函数,它们可以渲染到任何画布并且都应该在工作线程.我不知道当前的实现状态如何,似乎人们在 2 年前出于某种原因完全停止写它.
Doing a bit of more research it seems CanvasRenderingContext2D
and WebGLRenderingContext
are planned to be valid constructors for contexts, who can just render to any canvas and should both work inside a worker thread. I couldn't find out what the current state of the implementation is through, seems like people completely stopped writing about it 2 years ago for some reason.
相关文章