Code Monkey home page Code Monkey logo

Comments (7)

paulbartrum avatar paulbartrum commented on June 25, 2024

There shouldn't be any limitations on how you construct your prototype chain. Can you post sample code that reproduces the problem?

from jurassic.

DAVco4 avatar DAVco4 commented on June 25, 2024

Hi Paul,

If it should be achievable, I'm almost certain that I'm doing something wrong here. If I want instances of OuterObject to have the prototype InnerObject, where should I 'set' that prototype?

I have attempted the following:

  • Setting the third parameter of OuterObjectConstructor's base constructor to new OuterObjectInstance(new InnerObjectInstance(engine.Object.InstancePrototype)
  • returning new OuterObjectInstance(new InnerObjectInstance(this.InstancePrototype, i), i) from the Construct function
  • Making OuterObjectInstance inherit (on the .NET side) from the InnerObjectInstance class.
  • All combinations of the above.

I fully expect this to be a hugely obvious thing that I've overlooked :)

public class OuterObjectConstructor : ClrFunction
{
    public OuterObjectConstructor(ScriptEngine engine)
        : base(engine.Function.InstancePrototype, "OuterObject", new OuterObjectInstance(engine.Object.InstancePrototype))
    {
    }

    [JSConstructorFunction]
    public OuterObjectInstance Construct(int i)
    {
        return new OuterObjectInstance(this.InstancePrototype, i);
    }

public class OuterObjectInstance : ObjectInstance
{

    public OuterObjectInstance(ObjectInstance prototype)
        : base(prototype)
    {
        this.PopulateFunctions();
    }

    public OuterObjectInstance(ObjectInstance prototype, int i)
        : base(prototype)
    {
    }

    [JSFunction(Name = "OuterFunction")]
    public string OuterFunction()
    {
        return "Outer function";
    }
}

from jurassic.

paulbartrum avatar paulbartrum commented on June 25, 2024

Your code looks correct. What makes you think it's wrong?

var engine2 = new ScriptEngine();
engine2.SetGlobalValue("O", new OuterObjectConstructor(engine2));
Console.WriteLine("instance: {0}", engine2.Evaluate<string>("var x = new O(); Object.getOwnPropertyNames(x)"));
Console.WriteLine("prototype: {0}", engine2.Evaluate<string>("x = Object.getPrototypeOf(x); Object.getOwnPropertyNames(x)"));
Console.WriteLine("prototype.prototype: {0}", engine2.Evaluate<string>("x = Object.getPrototypeOf(x); Object.getOwnPropertyNames(x)"));
Console.WriteLine("Anything else? {0}", engine2.Evaluate<string>("x = Object.getPrototypeOf(x); x"));

Gives this output:

instance:
prototype: OuterFunction,constructor
prototype.prototype: hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,valueOf,toString,constructor
Anything else? null

This is exactly what I would expect. The instance itself has no properties defined, the instance prototype has two (including the OuterFunction function), then you have the base object prototype which is shared by all objects. It has toString, etc.

from jurassic.

DAVco4 avatar DAVco4 commented on June 25, 2024

I think I've discovered the problem; my outerObject contains all the expected properties inherited from innerObject, but outerObject instanceof InnerObject evaluates to false. I think this is because I'm not using the same constructor instance to create the innerObject instance within the OuterObjectConstructor class, so although they are the same class on the .NET side, on the Javascript side they are entirely different objects, just with identical signatures.

So my question now is; what is the correct way to provide the OuterObjectConstructor with the correct constructor (or a correct instance) of InnerObject such that instanceof will behave as I expect?

Here is the Javascript of what I would like to achieve. Basically; outerObject inherits (prototypes?) from innerObject.

var JSInnerObject = function() {
    this.innerValue = "my inner value";
};

var JSOuterObject = function() {    
    this.outerValue = "my outer value";
};

// OuterObject instances prototype InnerObject
JSOuterObject.prototype = new JSInnerObject();

var jsInnerObject = new JSInnerObject();
var jsOuterObject = new JSOuterObject();

console.log(jsInnerObject instanceof JSInnerObject);
console.log(jsOuterObject instanceof JSOuterObject);
console.log(jsOuterObject instanceof JSInnerObject);

console.log(jsOuterObject.innerValue + " - " + jsOuterObject.outerValue);

And the output;

true
true
true
my inner value - my outer value

And here are the C# classes I'm using to achieve the same thing on the .NET side

InnerObject

using Jurassic;
using Jurassic.Library;

public class InnerObjectConstructor : ClrFunction
{
    public InnerObjectConstructor(ScriptEngine engine)
        : base(engine.Function.InstancePrototype, "InnerObject", new InnerObjectInstance(engine.Object.InstancePrototype))
    {
    }

    [JSConstructorFunction]
    public InnerObjectInstance Construct(int i)
    {
        return new InnerObjectInstance(this.InstancePrototype, i);
    }
}

public class InnerObjectInstance : ObjectInstance
{

    public InnerObjectInstance(ObjectInstance prototype)
        : base(prototype)
    {
        this.PopulateFunctions();
    }

    public InnerObjectInstance(ObjectInstance prototype, int i)
        : base(prototype)
    {
        this["innerValue"] = "my inner value";
    }

    [JSFunction(Name = "innerFunction")]
    public string InnerFunction()
    {
        return "Inner function";
    }
}

OuterObject

using Jurassic;
using Jurassic.Library;

public class OuterObjectConstructor : ClrFunction
{
    public OuterObjectConstructor(ScriptEngine engine)
        : base(engine.Function.InstancePrototype, "OuterObject", new OuterObjectInstance(engine.Object.InstancePrototype))
    {
    }

    [JSConstructorFunction]
    public OuterObjectInstance Construct(int i)
    {
        return new OuterObjectInstance(new InnerObjectInstance(this.InstancePrototype, i), i);
    }
}

public class OuterObjectInstance : ObjectInstance
{

    public OuterObjectInstance(ObjectInstance prototype)
        : base(prototype)
    {
        this.PopulateFunctions();
    }

    public OuterObjectInstance(ObjectInstance prototype, int i)
        : base(prototype)
    {
        this["outerValue"] = "my outer value";
    }

    [JSFunction(Name = "OuterFunction")]
    public string OuterFunction()
    {
        return "Outer function";
    }
}

Setup in Jurassic

js = new ScriptEngine();

js.SetGlobalValue("InnerObject", new InnerObjectConstructor(js));
js.SetGlobalValue("OuterObject", new OuterObjectConstructor(js));

js.Evaluate("var innerObject = new InnerObject(0)");
js.Evaluate("var outerObject = new OuterObject(0)");

js.Evaluate("console.log(innerObject instanceof InnerObject)");
js.Evaluate("console.log(outerObject instanceof OuterObject)");
js.Evaluate("console.log(outerObject instanceof InnerObject)");

js.Evaluate("console.log(outerObject.innerValue + \" - \" + outerObject.outerValue)");

And the output

true
true
false
my inner value - my outer value

After that enormous post, I feel as though I should express my thanks for your excellent library, by the way :)

from jurassic.

paulbartrum avatar paulbartrum commented on June 25, 2024

The instanceof operator works by traversing the LHS operand's prototype chain, looking for an object which is equal to the RHS operand's prototype property (this is called InstancePrototype in Jurassic).

Pseudocode for lhs instanceof rhs:

Object.getPrototypeOf(lhs) === rhs.prototype ||
   Object.getPrototypeOf(Object.getPrototypeOf(lhs)) === rhs.prototype ||
   Object.getPrototypeOf(Object.getPrototypeOf(Object.getPrototypeOf(lhs))) === rhs.prototype || ...

So the reason outerObject instanceof InnerObject == false is because InnerObject.prototype never appears in the outerObject prototype chain. Try referencing InnerObject.InstancePrototype in your OuterObjectConstructor.Construct method instead of using new InnerObjectInstance.

from jurassic.

DAVco4 avatar DAVco4 commented on June 25, 2024

Thanks for that Paul.

I managed to get the results I expected, but not quite in the way you suggested. I think this is correct though; when instantiating the OuterObjectConstructor object, I pass in the InnerObjectConstructor and use new OuterObjectInstance(innerObjectConstructor.InstancePrototype) to set the instance prototype object for OuterObjectConstructor. When instantiating an OuterObjectInstance with Construct(), I then use this.InstancePrototype as currently. See the C# code below.

OuterObject

using Jurassic;
using Jurassic.Library;

public class OuterObjectConstructor : ClrFunction
{

    public OuterObjectConstructor(ScriptEngine engine, InnerObjectConstructor innerObjectConstructor)
        : base(engine.Function.InstancePrototype, "OuterObject", new OuterObjectInstance(innerObjectConstructor.InstancePrototype))
    {
    }

    [JSConstructorFunction]
    public OuterObjectInstance Construct(ObjectInstance prot, int i)
    {
        return new OuterObjectInstance(this.InstancePrototype, i);
    }
}

public class OuterObjectInstance : ObjectInstance
{ 

    public OuterObjectInstance(ObjectInstance prototype)
        : base(prototype)
    {
        this.PopulateFunctions();
        this["outerValue"] = "my outer value";
    }

    public OuterObjectInstance(ObjectInstance prototype, int i)
        : base(prototype)
    {   
    }

    [JSFunction(Name = "OuterFunction")]
    public string OuterFunction()
    {
        return "Outer function";
    }
}

InnerObject

using Jurassic;
using Jurassic.Library;

public class InnerObjectConstructor : ClrFunction
{
    public InnerObjectConstructor(ScriptEngine engine)
        : base(engine.Function.InstancePrototype, "InnerObject", new InnerObjectInstance(engine.Object.InstancePrototype))
    {
    }

    [JSConstructorFunction]
    public InnerObjectInstance Construct(int i)
    {
        return new InnerObjectInstance(this.InstancePrototype, i);
    }
}

public class InnerObjectInstance : ObjectInstance
{

    public InnerObjectInstance(ObjectInstance prototype)
        : base(prototype)
    {
        this.PopulateFunctions();
        this["innerValue"] = "my inner value";
    }

    public InnerObjectInstance(ObjectInstance prototype, int i)
        : base(prototype)
    {
    }

    [JSFunction(Name = "innerFunction")]
    public string InnerFunction()
    {
        return "Inner function";
    }
}

Setup code

js = new ScriptEngine();

InnerObjectConstructor innerObjectConstructor = new InnerObjectConstructor(js);
OuterObjectConstructor outerObjectConstructor = new OuterObjectConstructor(js, innerObjectConstructor);

js.SetGlobalValue("InnerObject", innerObjectConstructor);
js.SetGlobalValue("OuterObject", outerObjectConstructor);

js.Evaluate("var innerObject = new InnerObject(0)");
js.Evaluate("var outerObject = new OuterObject(0)");

js.Evaluate("console.log(innerObject instanceof InnerObject)");
js.Evaluate("console.log(outerObject instanceof OuterObject)");
js.Evaluate("console.log(outerObject instanceof InnerObject)");

js.Evaluate("console.log(outerObject.innerValue + \" - \" + outerObject.outerValue)");

And the output

true
true
true
my inner value - my outer value

from jurassic.

paulbartrum avatar paulbartrum commented on June 25, 2024

You're right, this is the correct way to do it. Prototypical inheritance is hard! :-)

from jurassic.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.