-
Notifications
You must be signed in to change notification settings - Fork 35
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Calling JavaScript instance functions from Lua does not respect the JS function's scope #12
Comments
+1 |
This is a tricky one, but I believe that Moonshine is acting as it should in this situation. In JavaScript, functions are passed around by value and the context is only applied at the point at which it is called. So, the function value is passed into the Lua environment and passed around the Lua environment by value too. The function is then called in Lua, but Lua does not have the same concept of context. In Lua, context is applied using the colon operator, So, there are two solutions to this situation. Neither are perfect, but you can choose which you think is best.
var employee = function (name) {
this.name = name;
this.say_name = this.constructor.prototype.say_name.bind(this);
} and in Lua: fred.say_name()
employee.prototype.say_name = function (self) {
console.log(self.name);
} and in Lua: fred:say_name() Personally, i've chosen the second option for the projects that I've built because that the results in Lua code that uses context correctly. I haven't needed to reuse the same objects in JavaScript, but I'm aware that could be pretty ugly. A third option could be to create a layer between the JavaScript and the VM, that handles this for you while keeping your JS objects clean. Some other points that I've noticed along the way:
If you need any more help, please shout. |
I thought I'd follow up on this a little. I understand your perspective even though I think it's unfortunate that it precludes seamless interop with most JS libraries. The solution that I've come to is to use a form of method binding but doing it implicitly via the "fat arrow" notation in CoffeeScript. rectangle: (mode, x, y, width, height) =>
switch mode
when "fill" then @ctx.fillRect(x, y, width, height)
when "line" then @ctx.strokeRect(x, y, width, height) becomes https://github.com/TannerRogalsky/moonshine-love2d/blob/master/js/love.js#L55 var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
function Graphics() {
this.rectangle = __bind(this.rectangle, this);
// ...
}
Graphics.prototype.rectangle = function(mode, x, y, width, height) {
switch (mode) {
case "fill":
return this.ctx.fillRect(x, y, width, height);
case "line":
return this.ctx.strokeRect(x, y, width, height);
}
}; I've found this to be an easy and reasonable way to bridge the existing gap for my purposes. |
Yep, the CoffeeScript __bind function is the same as Function.prototype.bind in modern browsers. Keep me posted with your progress and shout if you need help. I look forward to seeing the result. |
It's also worth looking at the implementation of the DOMAPI extension for interop between JS and Lua. There is a drawback that you are not able to iterate over JS object properties in Lua using pairs(), but context is taken care of quite nicely. See DOMAPI demo. |
When calling an instance of a JS prototype's function from Lua,
this
always refers to Window when it should instead refer to the instance.Consider this code:
When calling
fred.say_name();
from JS,this
refers to the fred object.When calling
fred.say_name()
from Lua,this
refers toWindow
.I believe this to be because of this line setting the first argument to
apply
to benull
. It's not obvious to me how to fix this because the instance does not seem to be in scope when executing the instruction.Perhaps the caller needs to be closed over when creating the instruction? I'm a bit out of my depth here.
I've created a small branch to showcase this issue in action if it helps: https://github.com/TannerRogalsky/moonshine-love2d/tree/prototype_scope_bug
The text was updated successfully, but these errors were encountered: