无法继承上下文对象? [英] Can't inherit from context object?

查看:136
本文介绍了无法继承上下文对象?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图创建一个继承自上下文对象的对象。但是在从我继承的对象调用函数时,浏览器(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] +"\n";
    }
    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中的许多其他构造函数一样)将抛出类型错误:非法构造函数,因为它们只能以一种特定的方式从工厂功能创建。在这种情况下,画布的 .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创建了一个新的Object你可以使用

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 且没有私下声明的函数 >构造函数通过原型继承。

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.

这篇关于无法继承上下文对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆