For information and discussion about Oolite.
https://bb.oolite.space/
Code: Select all
return $this.myfunction() ? true : false;
Code: Select all
return $this.myfunction();
Code: Select all
return $this.myfunction() === true;
Code: Select all
return $this.myfunction();
Code: Select all
return $this.myfunction() === false;
Code: Select all
return !$this.myfunction();
Code: Select all
var myvar = {"someValue"};
Code: Select all
var myfunc = function() {
var myinnerfunc = function() {};
return myinnerfunc; // this returns the function itself
}
// Here I get the func, and use it
var myresult = myfunc(); // The parenthesis executes the function. myresult contains now the inner function.
myresult(); // The parenthesis executes the inner function :-)
Code: Select all
var r = Math.random();
var z = 1000;
while (z--) {
r();
}
Code: Select all
var myfunc = (function() {}).bind(this);
Should that be:Day wrote: ↑Thu Jun 29, 2017 1:06 pmFor example:Code: Select all
var r = Math.random(); var z = 1000; while (z--) { r(); }
Code: Select all
var r = Math.random;
To test your assertion, I profiled the following:First, some micro-optimizations:
Code: Select all
(function (){
function truth() {
var x = true;
return x ? true : false;
}
function plain() {
var x = true;
return x;
}
log(console.profile( function() {var i=10000; var result = false; while(i--) { result = result && truth() } } ) );
log(console.profile( function() {var i=10000; var result = false; while(i--) { result = result && plain() } } ) );
})()
Code: Select all
truth: Total time: 0.467 ms
JavaScript: 0.458 ms, native: 0 ms
Counted towards limit: 0.467 ms, excluded: 0 ms
Profiler overhead: 0.018 ms
NAME T COUNT TOTAL SELF TOTAL% SELF% SELFMAX
(<console input>) <anonymous> J 1 0.46 0.46 98.1 98.1 0.46
plain: Total time: 0.283 ms
JavaScript: 0.275 ms, native: 0 ms
Counted towards limit: 0.283 ms, excluded: 0 ms
Profiler overhead: 0.014 ms
NAME T COUNT TOTAL SELF TOTAL% SELF% SELFMAX
(<console input>) <anonymous> J 1 0.27 0.27 97.2 97.2 0.27
Code: Select all
truth: Total time: 0.28 ms
JavaScript: 0.271 ms, native: 0 ms
Counted towards limit: 0.28 ms, excluded: 0 ms
Profiler overhead: 0.016 ms
NAME T COUNT TOTAL SELF TOTAL% SELF% SELFMAX
(<console input>) <anonymous> J 1 0.27 0.27 96.8 96.8 0.27
plain: Total time: 0.311 ms
JavaScript: 0.298 ms, native: 0 ms
Counted towards limit: 0.311 ms, excluded: 0 ms
Profiler overhead: 0.019 ms
NAME T COUNT TOTAL SELF TOTAL% SELF% SELFMAX
(<console input>) <anonymous> J 1 0.30 0.30 95.8 95.8 0.30
By returning myinnerfunc, you're telling JS that you'll be back (ie. will call the function later) and so it preserves its environment or scope. This is a 'closure': any variables you define will be preserved over each call (but the code itself won't be duplicated if you create many copies). Consider the following:A better one:
variables can be used for data structures, like this:but they can be used for functions too:Code: Select all
var myvar = {"someValue"};
So we can store functions into variables.Code: Select all
var myfunc = function() { var myinnerfunc = function() {}; return myinnerfunc; // this returns the function itself } // Here I get the func, and use it var myresult = myfunc(); // The parenthesis executes the function. myresult contains now the inner function. myresult(); // The parenthesis executes the inner function :-)
For example:Code: Select all
var r = Math.random(); var z = 1000; while (z--) { r(); }
Code: Select all
(function (){
function sum( start ) {
var total = 0;
if( start !== undefined ) total = start;
function add( x ){
total += x;
return total;
}
return add;
}
var i = 10;
var func1 = sum();
while( i-- ) func1( i );
log( 'result1: ' + func1( 0 ) );
var func2 = sum( 100 );
log( 'result2: ' + func2( 1 ) );
log( 'result1: ' + func1( 0 ) );
log( 'result2: ' + func2( 1 ) );
})()
Code: Select all
result1: 45
result2: 101
result1: 45
result2: 102
Code: Select all
this._VModelAndRing_closure = function() {
// 'constant' variables
var ws = worldScripts.telescope;
var w_shiplib = worldScripts.shiplib;
var dataKeys77 = ws.$DataKeys77;
var autoLock = ws.$AutoLock;
[editted for brevity]
// function references
var system_addVisualEffect = system.addVisualEffect;
var oolite_compareVersion = oolite.compareVersion;
var _Scan = ws._Scan;
[editted for brevity]
// local variables
var vRing = null; //a ring around the visual effect target
var vShip = null; //visual effect to show the selected target
var vDataKey = null; //key of the visual effect
var ps;
[editted for brevity]
function getVShip() { return vShip; }
function setVShip( ship, up, delay, length ) {
vShip = ship;
[editted for brevity]
return ship;
}
function clearVShip() { //Clear Visual Effect Ship Model and large visual ring
if( vShip ) {
vShip.remove();
vShip = null;
}
if( vRing ) {//remove large visual ring
vRing.remove();
vRing = null;
}
}
function showVShip() { //Show Visual Effect
ps = player.ship && player.ship;
if( !ws.$ShowVisualTarget ) return;
if( !autoLock && !ps.target ) return;
[editted for brevity]
_Scan();
[editted for brevity]
vShip = system_addVisualEffect( dk, p );
[editted for brevity]
}
return { clear: clearVShip,
get: getVShip,
show: showVShip }
}
Code: Select all
var vc = ws._VModelAndRing_closure();
ws._Clear_VModel_Ring = vc.clear;
ws._GetVModel = vc.get;
ws._ShowVModel = vc.show;
Yup, the first 'r' has a random number, the second a function reference.hoqllnq wrote:Should that be:without the parentheses,Code: Select all
var r = Math.random;
Ok, so this part is useless.At best we gain a fraction of a millisecond (averaged over 10k iterations, 100s of nano-seconds), so not much at all, at the expense of readability & explicit typing.
Exactly.hoqllnq wrote: ↑Thu Jun 29, 2017 5:36 pmShould that be:Day wrote: ↑Thu Jun 29, 2017 1:06 pmFor example:Code: Select all
var r = Math.random(); var z = 1000; while (z--) { r(); }
without the parentheses,Code: Select all
var r = Math.random;
so that the value of r becomes (a reference to) the function itself, not the result of calling the function?
I've carefully read all of that, yup, I'm totally ok with everything you wrotecag wrote: ↑Thu Jun 29, 2017 7:59 pmBy returning myinnerfunc, you're telling JS that you'll be back (ie. will call the function later) and so it preserves its environment or scope. This is a 'closure': any variables you define will be preserved over each call (but the code itself won't be duplicated if you create many copies).
[...]
The value of 'total' is preserved from one call to the next (call of add via func1), or we'd have result: 0 (or rather, a ReferenceError); And func2 has its own scope, independent of func1. From everything I've read, there is only one copy of the function 'add'.
[...]
So, the pointers to the VisualEffects are stored inside _VModelAndRing_closure and the speed gain comes from having to only evaluate all the var statements (with their expensive worldscript property gets) before the line 'function getVShip() ...' once per game instead of once per frame!
You don't necessarily have to put a closure in an IIFE (immediately invoked fucntion expression - who comes up with this stuff?!) as you see in most online discussion of closures. In oolite (vs. a web page) it makes more sense to delay initiation of the closure, as some of your references won't exist right away, esp. if you reference another oxp; you may have to wait until startUpComplete.
Code: Select all
this.startUpComplete = function () {
var s = this._subscribers.sort();
var z = s.length, y = z - 1;
while (z--) {
var startDate = new Date();
worldScripts[s[y - z]]._startUp();
log(s[y - z], "startUp in ms: " + (new Date().getTime() - startDate.getTime()));
}
delete this.startUpComplete; // No need to startup twice
};
this._subscribers = []; // [ scriptName ]
this.$subscribe = function (aScriptName) {
this._subscribers.push(aScriptName);
};
Code: Select all
this.startUp = function() {
worldScripts.DayDiplomacy_000_Engine.$subscribe(this.name);
delete this.startUp; // No need to startup twice
};
And the subscribers will put their startUp code in:Day wrote:In the other scripts:
Code: Select all
this._startUp = function() {
// move your startUp code here
};
Code: Select all
this.$myArrayThatEveryoneLovesToReference = [];
Code: Select all
while( i-- ) this.$myArrayThatEveryoneLovesToReference.pop();
I'll try and get you started. Feel free to butcher what follows, it only sounds good in my head.Day wrote:I need a way to include this into the first post...
Code: Select all
this._myFirst_closure = function() {
function _my_orig_fn() {
...
}
return _my_orig_fn;
}
// and in startUpComplete() add:
this._my_orig_fn = this._myFirst_closure();
Code: Select all
this._myFirst_closure = function() {
var wm = worldScripts.my_fabulous_oxp;
var _do_something = wm._do_something;
function _my_orig_fn() {
...
// wm._do_something(); gets changed to:
_do_something();
...
}
return _my_orig_fn;
}
Code: Select all
Total time: 6.936 ms
JavaScript: 2.821 ms, native: 4.106 ms
Counted towards limit: 5.63274 ms, excluded: 1.30326 ms
Profiler overhead: 2.472 ms
NAME T COUNT TOTAL SELF TOTAL% SELF% SELFMAX
WorldScriptsGetProperty N 24 1.50 1.38 21.6 19.9 0.12
...
this.$RADIANS_to_ANGLE = 180 / Math.PI;
. The procedure is the same as with function references above. And test it.Code: Select all
this._myFirst_closure = function() {
// 'constant' variables
var wm = worldScripts.my_fabulous_oxp;
var gameWindow = oolite.gameSettings.gameWindow;
var $RADIANS_to_ANGLE = wm.$RADIANS_to_ANGLE;
var ps, scannerRange;
// function references
var _do_something = wm._do_something;
var addShips = system.addShips;
// local variables
var weaps = true; //the previous state of the player weapons
var vShip = null; //visual effect to show the selected target
function _reset_vars() { // <============== call in shipLaunchedFromStation()
ps = player.ship;
scannerRange = ps.scannerRange;
gameWindow = oolite.gameSettings.gameWindow;
}
function _my_orig_fn() {
...
if( weaps && !ps.weaponsOnline ) {
weaps = false;
vShip = _addShips( ... );
}
...
}
return { _my_orig_fn: _my_orig_fn,
_reset_vars: _reset_vars };
}
// and startUpComplete() is now changed to this:
var mfc = this._myFirst_closure();
this._my_orig_fn = mfc._my_orig_fn;
this._reset_vars = mfc._reset_vars;
...
// and in shipLaunchedFromStation() add:
this._reset_vars();
return { my_fn: _my_orig_fn, ...
and this._my_orig_fn = mfc.my_fn;
vShip.remove(); vShip = null;
to clean up; you cannot do so directly from shipWillDockWithStation()wm.$myvar = myvar = ....
when you alter its value. I've avoided that so far, instead moving the other function into the closure!I've decided to addDay wrote: ↑Mon Jun 05, 2017 11:29 am- the following is faster than indexOf when dealing with arrays:Code: Select all
this._index_in_list = function( item, list ) { // for arrays only var k = list.length; while( k-- ) { if( list[ k ] === item ) return k; } return -1; }
if( !list ) return -1;
as the 1st line, so that calling it is cleaner. I'll get an occasional extra function call but the calling statement goes from
Code: Select all
if( my_list && _index_in_list( x, my_list ) )
Code: Select all
if( _index_in_list( x, my_list ) )
Code: Select all
this._index_in_list = function( item, list ) { // for arrays only
if( !list ) return -1;
var k = list.length;
while( k-- ) {
if( list[ k ] === item ) return k;
}
return -1;
}
Code: Select all
if( targets && targets.indexOf( ship ) ...
Code: Select all
if( ws._index_in_list( ship, targets ) ...
Sorry and thank you, I forgot to include this part.cag wrote: ↑Fri Jun 30, 2017 8:39 pmAnd the subscribers will put their startUp code in:Day wrote:In the other scripts:Submit to you, compel everyone, how will you, 'Master', hmm?Code: Select all
this._startUp = function() { // move your startUp code here };
Granted, your solution is easier to grasp than
http://wiki.alioth.net/index.php/Handli ... JavaScript
but herding cats would be less daunting.
Exactly! I use Intellij Idea.cag wrote: ↑Fri Jun 30, 2017 8:39 pmAnd if you're anyway near as skilled a typist as I am, never make the code changes manually! Use the editor's replace utility! That way, if you make a typo, it'll be glaring obvious (nothing works) vs subtle (single line typo only shows up a week later in some outlying circumstance).
Code: Select all
this.$rand = function(max) {
return Math.floor((Math.random() * max) + 1);
}
Code: Select all
this.$rand = function $rand(max) {
var that = $rand;
var floor = (that.floor = that.floor || Math.floor);
var random = (that.random = that.random || Math.random);
return floor((random() * max) + 1);
}
Code: Select all
var floor = (that.floor = that.floor || Math.floor);
Code: Select all
if( that.floor === undefined ) that.floor = Math.floor;
var floor = that.floor;
Code: Select all
Total time: 6.541 ms
JavaScript: 3.824 ms, native: 2.711 ms
Counted towards limit: 5.06183 ms, excluded: 1.47917 ms
Profiler overhead: 1.719 ms
NAME T COUNT TOTAL SELF TOTAL% SELF% SELFMAX
(cagsdebug.js:28) _dump_map J 1 5.85 2.55 89.5 39.0 2.55
GlobalLog N 2 0.81 0.74 12.4 11.4 0.39
(cagsdebug.js:36) number_str J 51 0.44 0.44 6.7 6.7 0.20
(telescope.js:796) _RelativeDirection J 17 0.96 0.30 14.6 4.6 0.06
EntityGetProperty N 166 0.54 0.29 8.2 4.4 0.01
(cagsdebug.js:29) dist J 17 0.90 0.23 13.7 3.5 0.06
WorldScriptsGetProperty N 3 0.25 0.23 3.8 3.5 0.12
-[NSObject(OOJavaScriptConversion) oo:jsDescriptionWithClassName:] N 17 0.22 0.22 3.4 3.4 0.02
(telescope.js:1269) _detect_distanceTo J 17 0.56 0.21 8.5 3.3 0.04
...
Code: Select all
Total time: 4.622 ms
JavaScript: 1.981 ms, native: 2.634 ms
Counted towards limit: 3.11484 ms, excluded: 1.50716 ms
Profiler overhead: 1.411 ms
NAME T COUNT TOTAL SELF TOTAL% SELF% SELFMAX
(cagsdebug.js:28) _dump_map J 1 4.08 0.99 88.2 21.5 0.99
GlobalLog N 2 0.86 0.79 18.6 17.2 0.48
(telescope.js:796) _RelativeDirection J 17 0.94 0.31 20.3 6.7 0.08
EntityGetProperty N 166 0.52 0.27 11.3 5.9 0.01
-[NSObject(OOJavaScriptConversion) oo:jsDescriptionWithClassName:] N 17 0.24 0.24 5.1 5.1 0.03
(telescope.js:1269) _detect_distanceTo J 17 0.55 0.21 11.8 4.6 0.04
WorldScriptsGetProperty N 2 0.20 0.19 4.4 4.2 0.11
(cagsdebug.js:29) dist J 17 0.83 0.19 18.0 4.1 0.05
(cagsdebug.js:36) number_str J 51 0.19 0.19 4.0 4.0 0.03
...