Page 1 of 1
JS "this" question
Posted: Fri Mar 04, 2011 2:37 pm
by Commander McLane
I am experimenting with SystemInfo, and want to understand something.
I had the following code, which is supposed to randomly select a system and calculate the route to that system from the player's current system:
Code: Select all
var amnestySystem = Math.floor(Math.random() * 256);
var amnestyRoute = system.info.routeToSystem(System.infoForSystem(galaxyNumber, amnestySystem), "OPTIMIZED_BY_TIME");
(Some people may guess what I'm working on.) It didn't work.
amnestyRoute
was always
null
.
I replaced it with a this.variable:
Code: Select all
this.amnestySystem = Math.floor(Math.random() * 256);
this.amnestyRoute = system.info.routeToSystem(System.infoForSystem(galaxyNumber, this.amnestySystem), "OPTIMIZED_BY_TIME");
The result was the same.
this.amnestyRoute
was always
null
.
Then I thought: perhaps inside the SystemInfo object (not sure whether the outer or the inner, or both)
this
doesn't refer to my script anymore, so I tried:
Code: Select all
this.amnestySystem = Math.floor(Math.random() * 256);
this.amnestyRoute = system.info.routeToSystem(System.infoForSystem(galaxyNumber, worldScripts.Anarchies.amnestySystem), "OPTIMIZED_BY_TIME");
And now it worked.
I haven't quite understood yet, however, why this is so. Can I get some more enlightenment about
this
? Where is it supposed to refer to what?
Re: JS "this" question
Posted: Fri Mar 04, 2011 3:28 pm
by Eric Walch
Commander McLane wrote:I replaced it with a this.variable:
Code: Select all
this.amnestySystem = Math.floor(Math.random() * 256);
this.amnestyRoute = system.info.routeToSystem(System.infoForSystem(galaxyNumber, this.amnestySystem), "OPTIMIZED_BY_TIME");
The result was the same.
this.amnestyRoute
was always
null
.
Are you sure there was no other change? When I use this code, copied from above, in the console, it works. I also used it in a worldScript. On logging
this.amnestyRoute
I get
[object Object]
(not null) and when logging
this.amnestyRoute.distance
I get a correct value.
Re: JS "this" question
Posted: Fri Mar 04, 2011 3:32 pm
by JensAyton
In all the code you presented, this
is in the same scope and should work in the same way. This suggests you’re in a context where this
is not worldScripts.Anarchies
. This could happen, for instance, if your code is in a function that’s not being called as a method. If you need more help, post the code with more context.
Re: JS "this" question
Posted: Fri Mar 04, 2011 5:22 pm
by Commander McLane
I'm seeing it now. The very next line checks whether the randomly chosen system is disconnected, and stops the function if so.
Code: Select all
if(this.amnestyRoute == null) return;
I got the error message after that line, and just understood why. At first I had accidentally only typed one '=', so I was actually
setting my variable to
null
. No wonder that it was
null
afterwards.
Obviously I corrected the typo together with starting to use
worldScripts.Anarchies
, so I didn't see the correlation right away.
Re: JS "this" question
Posted: Fri Mar 04, 2011 7:29 pm
by Micha
Commander McLane wrote:
I got the error message after that line, and just understood why. At first I had accidentally only typed one '=', so I was actually setting my variable to null
. No wonder that it was null
afterwards.
This is actually quite a common programming error and, depending on circumstances, quite difficult to find.
That's why some people write comparisons with the const value first, ie:
Code: Select all
if(null == this.amnestyRoute) return;
That way you immediately get an error if you use '=' instead of '==', although it makes reading the code seem ass-backwards.
Re: JS "this" question
Posted: Fri Mar 04, 2011 9:38 pm
by JensAyton
Tools like
JSLint and
JSHint can also be useful, but they’re geared towards web scripting so they don’t know about Oolite’s objects and reject SpiderMonkey extensions.
Re: JS "this" question
Posted: Fri Mar 11, 2011 9:59 pm
by stevesims
Sorry for the slight lateness of this reply...
In JavaScript, it's generally good practice to use === for equality comparisons, rather than ==. That's because the == does type coercion... Similarly it's considered to be good practice to always wrap commands after an "if" statement in a block, to help avoid shooting yourself in the foot.
JavaScript does a few things that many would consider odd. For example, it has a feature called 'automatic semicolon insertion' whereby if you've forgotten to end a line with a semicolon it'll most likely work just fine and you'll not see an error. Another oddity is that variables are defined in function scope, rather than block scope - which means for example that code like the following will not work as one may expect:
Code: Select all
function test() {
for (var i = 0; i < 5; i++) {
for (var i = 0; i < 2; i++) {
console.log("here is a message");
}
}
}
Coming from block-scope languages you'd expect the above code to produce 10 messages in the console, but in fact it's an infinite loop. Function-scope, combined with variable definition hoisting means that there's only actually one 'i' variable inside the test function, so the inner loop overwrites the value of the single 'i' variable when it executes. (You won't see an error or warning about 'i' getting redefined.) The easy way around this is to just remember to not re-use variables and replace the inner 'i' with a 'j' - a slightly more perverse solution would be this:
Code: Select all
function test() {
for (var i = 0; i < 5; i++) {
(function() {
for (var i = 0; i < 2; i++) {
console.log("here is a message");
}
}());
}
}
This technique of wrapping up statements inside an anonymous function block is useful for creating local variables that don't pollute the global object, and is a pattern you'll often see adopted by JS libraries.
The best resource I've found to learn about this stuff is Douglas Crockford's writings.
This page of his, for example, explains why it's often bad to use ==
Crockford works for Yahoo, and there's a series of 6 "Crockford on JavaScript" videos in the YUI Theatre that are well worth watching.
JSLint (written by Crockford) and the more recent friendlier variant JSHint are your friends - they'll point out bad practices and help keep you out of trouble.
Re: JS "this" question
Posted: Sat Mar 12, 2011 10:58 am
by Eric Walch
stevesims wrote:Similarly it's considered to be good practice to always wrap commands after an "if" statement in a block, to help avoid shooting yourself in the foot.
The easiest way to shoot yourself in the foot is using:
Code: Select all
if (condition) i = true;
else i = false
The semicolon terminates here the if statement and sees the 'else' as a new one, resulting in 'i' to be always false. Using {} for wrapping largely avoids such errors, as you write. (edit: It seems the semicolon is allowed after all. I thought I had problems with it in the past but i probably had a different problem)
With the double 'i' declaration in your example it helps when using the 'let' statement. It is not an official JS statement. In the ecma 5 specifications it is listed as "future reserved word". But the JS variant used by Oolite knows it.
Re: JS "this" question
Posted: Mon Mar 14, 2011 5:00 pm
by stevesims
Yeah - 'let' is a proposal for ES6, adding in block-scope variables to JavaScript. Mozilla frequently prototype future extensions to JavaScript in their interpreters, releasing them to the public. There's an inevitable risk that if you try using such bleeding-edge language features you'll get bitten at some unspecified point in the future since they might get removed (or change in an incompatible way).
Whilst ES6 is quite a long way away from getting finalised, I suspect that 'let' is a fairly safe bet to make it in there.
Re: JS "this" question
Posted: Mon Mar 14, 2011 11:04 pm
by JensAyton
stevesims wrote:Yeah - 'let' is a proposal for ES6, adding in block-scope variables to JavaScript.
Technically, it’s a leftover from ES4 prototyping. :-)