Comments (7)
There shouldn't be any limitations on how you construct your prototype chain. Can you post sample code that reproduces the problem?
from jurassic.
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.
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.
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.
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.
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.
You're right, this is the correct way to do it. Prototypical inheritance is hard! :-)
from jurassic.
Related Issues (20)
- Return value of CompiledScript HOT 5
- 3.2.1 backward compatibility issues HOT 4
- PropertyAccessorValue not visible HOT 1
- How to catch CLR Exception? HOT 7
- System.ArgumentNullException: Value cannot be null. (Parameter 'value') after update from 3.1.0 to 3.2.2 HOT 3
- JSONObject.Parse() fails when number is in scientific notation HOT 4
- ScriptEngine.EnableDebugging HOT 3
- Template of literals is slow
- JSON.stringify([].length) errors HOT 2
- JSONObject.Parse consumes more and more memory. Possible memory leak? HOT 11
- Scoping and CompiledScript.Execute vs ScriptEngine.Evaluate HOT 5
- Catching script errors at compile time HOT 2
- Currently JSONObject.Stringify returns object instead of string after upgrading from 3.1.0 to 3.2.6 HOT 3
- ScriptEngine.GetGlobalValue HOT 1
- Bug in Evaluate: Loop and variable handling HOT 2
- PM - Customer side scripting for Channels HOT 1
- Date.UTC() doesn't interpret two-digit years correctly HOT 1
- Possibility to create 'sessions' HOT 4
- Linux compability ? HOT 4
- Is it possible to go from JS to DLL and then to C# source? HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from jurassic.