Kino

EIS Game Dev
Veteran
Joined
Nov 27, 2015
Messages
562
Reaction score
805
First Language
English
Primarily Uses
RMMV
Closures - How To Create Reusable Functions
Closures are a concept mentioned a lot in JavaScript, and you may not have heard of them. They provide you with a power that's very similar to bind and apply in Javascript. The advantage is, unlike bind, you can create more robust functions without explicitly defining the parameters. Today, we're going to show you how they work and how you can make reusable functions in JavaScript with them. But first, let's talk bind and apply.



Bind And Apply
Bind and apply are functions that allow you to create a new function with explicitly defined arguments, or you could wrap the call in another function to create one that takes varying arguments. Here's an example of a bound function:

ClosureOut1-1.png


As you can see in the screenshot, sayTim is now bounded up and uses sayMyName to create a new version for Tim. Now, we could do this anytime we want to create a new function based on an old function. But the problem is bind and apply are not well suited for partial application. What is partial application you might ask?

Partial application is the process giving a function only part of its arguments. This allows us to create functions that can be passed arguments for later, and this also means we can't create complex functions using bind and apply. Now, if we want to create complex functions, we need to use closures!



Closures
Because JavaScript functions are objects, we can pass them around to other functions and objects like any other variable in our program. Closures, leverage this power to create a similar, but more robust effect of bind and apply. Here's the above example using a closure and the previous sayMyName function:

ClosureOut2.png


As you can see we simply returned a function from a function. When we do this, we can close a scope around variable we pass in allowing us to save those variables for the inner function. This allows us to create new functions often in a single line.

Now this example might seem weird to you. Why would I ever want to use this you say? Normally, you wouldn't need to bind up a name to a single variable like that. This code isn't doing anything I couldn't do manually! In fact, I can't think of any reason why you wouldn't just pass 'Tim' as the argument. But, let's look at a less trivial example: creating different kinds of people.


The Problem

The problem in this program is that we need to create programmers and consultants all with different names for our company. Our company wants a list of these people sorted by job type. Here's the class for creating people that you must use creating by another programmer:

ClosureOut3.png


The people also may have extra parameters defined in their options other than their job. I will show you how to do this in the normal way, and the closure way.


Normal Way

ClosureOut4.png


In this example, we create two new functions for creating programmers and consultants. They both do exactly the same thing, with one difference of changing the job property (We could have optimized this with a for loop or using two lists). After that, we declare a list of workers and push our programmers and consultants into them. Now, this is alright, but it sucks because we basically had to write the same thing twice. Here's one that makes it a single function, but adds more clutter:

Optimized
ClosureOut5.png




Now, Let's optimize this with closures and a little functional magic.

Closure Way
ClosureOut6.png


Console Output Of Every Way

ConsoleOut7.png


As you can see each way does the exact same thing, but the closure and optimized normal way allow us to create people of multiple different jobs. However, the closure way wins out in one way: We can create functions that look more readable in a single line.

This approach offers a lot of flexibility because that's not the only thing we can do with them. We can save functions for later execution, combine multiple functions together, and more. And to be honest, this is something you'll see a lot in JavaScript with the popularity of functional programming. For fun here's the ES6 version of the createByJob and the JS file:

Closure7.png





Anyway, I hope this tutorial was helpful and added a new skill to your toolbelt.
 

Jonforum

Veteran
Veteran
Joined
Mar 28, 2016
Messages
1,634
Reaction score
1,452
First Language
French
Primarily Uses
RMMV
It would be cool if you could make a all_in_one big PDF of all your tutorial.
So that I can read on my tablet when I travel :rock-left::D
It's just a small suggestion.
thank for you tutorial
 

Jonforum

Veteran
Veteran
Joined
Mar 28, 2016
Messages
1,634
Reaction score
1,452
First Language
French
Primarily Uses
RMMV
I am looking for a simple way to be able to store closure in array. !
I do not know if I got it right but I wonder if these a good idea.
I used an object system before for call all function in this._OnMoveContexts = {};
but loose too mutch performance MS.
So now i try with closure and store as array this._OnMoveContexts = [];
However, I am concerned about the memory leak and garbage collection, that a i read about closure !
PHP:
animationPlayersControler = function () {
    PIXI.Container.call(this);
    this._OnMoveContexts = [];
    this._motionsLists = {};
    this._motionContexts = {};
    this._currentMotionRendered = undefined;
};
animationPlayersControler.prototype = Object.create(PIXI.Container.prototype);
animationPlayersControler.prototype.constructor = animationPlayersControler;

animationPlayersControler.prototype.addMotionContext_onMove = function(motion,motionCall,luck) { // special motionContext Not Update in onFrameChange, but called Once
    var len = 1, mot = motion, direction = false, luck = luck || 100; // allow check if is array loop or only string arg
    if(motion instanceof Array){ len = motion.length, mot = motion[0]; };
    // closure will retaine all var injected (experimental) check:memory leak && garbage collection
    var motionClosure = function(){
        var _motion = this.getChildAt(this._motionsLists[mot]), _motionCall = {};
        if(motionCall!==String){ // if not string, is motionCall MultiDirection context {}
            for (motionDirection in motionCall){
                _motionCall[motionDirection] = this.getChildAt(this._motionsLists[motionCall[motionDirection]]);
            };
            direction = true;
        }else{ _motionCall = this.getChildAt(this._motionsLists[motionCall]); }
    
        return function(ran,dir){ // return the motionContext as closure
            if(ran<luck){
                if(direction){ // if is a motionCall By direction
                    _motion.renderable = false;
                    _motionCall[dir].renderable = true;
                    _motionCall[dir].gotoAndPlay(0);
                }else{
                    _motion.renderable = false;
                    _motionCall.renderable = true;
                    _motionCall.gotoAndPlay(0);
                }
                return true;
            };
            return false;
        }.bind(this);
    }.bind(this);
    for(var m=0;m<len;m++,mot = motion[m]){
        var MotionContext = motionClosure(); // check if need inject direction,luck,.. in closure ?
        this._OnMoveContexts.push(MotionContext);
    };
};
 

Kino

EIS Game Dev
Veteran
Joined
Nov 27, 2015
Messages
562
Reaction score
805
First Language
English
Primarily Uses
RMMV
In JavaScript arrays are just objects so I think you'd still get a performance drop.

Only difference is that an array is indexed by numbers.

First, why do you need the closure? Closures can be useful if you want to save specific function parameters or combine functions.
 

Jonforum

Veteran
Veteran
Joined
Mar 28, 2016
Messages
1,634
Reaction score
1,452
First Language
French
Primarily Uses
RMMV
In JavaScript arrays are just objects so I think you'd still get a performance drop.

Only difference is that an array is indexed by numbers.

First, why do you need the closure? Closures can be useful if you want to save specific function parameters or combine functions.
in fact the idea was simply to replace this function registers this._motionContexts = {};
PHP:
            aniPlayerSprite.onFrameChange = function(frame,motionContexts,ran){
                for (context in motionContexts){ // execute all registered motionContexts
                    if ( motionContexts[context](frame,ran) ){ break }; // if return true break or false: continue
                };
            };
by array this._motionContexts = [];
PHP:
            aniPlayerSprite.onFrameChange = function(frame,motionContexts,ran){
                for(var i=0,len=motionContexts.length;i<len;i++){
                    if ( motionContexts[i](frame,ran) ){ break }; // if return true break or false: continue
                }
       
            };

by doing some testing Performance.now(), the js speed performance results are much better.
but good everything seems good it works well.
 

Kino

EIS Game Dev
Veteran
Joined
Nov 27, 2015
Messages
562
Reaction score
805
First Language
English
Primarily Uses
RMMV
Okay glad to hear. That's interesting; I guess the interpreter does less work with arrays.
 

Jonforum

Veteran
Veteran
Joined
Mar 28, 2016
Messages
1,634
Reaction score
1,452
First Language
French
Primarily Uses
RMMV
the difference is minimal for a single call.
put inside a update (loop register at each frame update) !
the difference is remarkable.

the result here say a lot .
https://jsperf.com/fastest-array-loops-in-javascript/24
for 1000ms, js execute 1,000,288 X for loop
vs
21,761 X for in
in same radius time !!
 

Kino

EIS Game Dev
Veteran
Joined
Nov 27, 2015
Messages
562
Reaction score
805
First Language
English
Primarily Uses
RMMV
Yep for loop is more optimized than other loop structures if speed is what you're looking for.
 

Jonforum

Veteran
Veteran
Joined
Mar 28, 2016
Messages
1,634
Reaction score
1,452
First Language
French
Primarily Uses
RMMV
I'm going to better optimize some of my codes.

I just discovered this super method that I knew absolutely not!
PHP:
Object.setPrototypeOf


PHP:
var motion= {
  myMotionID: function() {}; // __proto__ object
};
Object.setPrototypeOf(motion['myMotionID'], {
  add: function() {
    alert('test 123');
  }
})
motion.['myMotionID'].add(); // alert 123 ! now is prototype motion.myMotionID
this method are isane !!!
I regret not having discovered her before, she would have solved some borings contexts.
strangely I never saw them in any plugin on rmmv.!

I think this is a method add in pixi.js from ES2015,

edit: Ho, but if we check the polyfill.
we have obj.__proto__ ?
wherever it is said that we must never proceed in this way!
because this will cause internal issues in the js ?!
PHP:
// Cette prothèse ne fonctionne pas pour IE
Object.setPrototypeOf = Object.setPrototypeOf || function (obj, proto) {
  obj.__proto__ = proto;
  return obj;
}
it maybe better to use
Object.create(proto[, objetProp])
 
Last edited:

Kino

EIS Game Dev
Veteran
Joined
Nov 27, 2015
Messages
562
Reaction score
805
First Language
English
Primarily Uses
RMMV
Object.setPrototype sets the prototype of the object. The protype are the methods that are attached to the object when you create a new one of its kind. Works for classes, functions, etc.

Just be careful what prototype you're extending. It could be the base object prototype.
 

Latest Threads

Latest Posts

Latest Profile Posts

I am SO HYPED to share this trailer with everyone when its done. I almost want to tear up i had no idea how far i came along with Fallen Feather in these last 3 years.
how it looks from the top
Hike was successful guys! I made it to the top!
Add Film-like Visual Effects During Test-Play, Deploy Games with Electron | RPG Maker News #69

brain broken as soon as i figure out whats wrong with my game im taking a long nap

Forum statistics

Threads
112,184
Messages
1,066,440
Members
145,784
Latest member
lafa
Top