Somehow imperceptibly for myself, I decided to move away from the fussing with classes and patterns, and deal with the most common Js functions. I thought it would be quite boring, but I was mistaken - it turned out to be very interesting.
In this article I will talk about the features of the function declaration, and some useful patterns (hehe, yes, they are here too)
1. Function declaration
If you do not pay attention to any perversions, then you can declare a function in only two ways:
')
// function a() { console.log(1); } // , b = function() { console.log(2); }
The difference between them seems small, but it only seems.
What happens if you execute this code?
console.log( "sample b" ); b(); function b() { console.log(1); }
that's right - first the text with the name of the example appears in the console, and then the unit, because all the named functions are transferred to the beginning of the scope, and you can call them whenever you want.
Complicate the task
console.log( "sample c" ); function c() { console.log(1); } c(); function c() { console.log(2); }
But we are not so easily confused. Functions are transferred to the beginning exactly in the order in which they were created, that is, the console will be 2.
Okay, is it so?
console.log( "sample d" ); d(); var d = function() { console.log(2); }
Answer: and here the rules are different - it will not work at all, because the value of the variable at the time of the call is undefined.
And if so?
console.log( "sample e" ); var e = function(){ console.log(1); } e(); e = function() { console.log(2); }
Well, everything is already clear here, yes - at the time of the call,
e contains the first function, so there will be one in the console.
And now the time has come for the most difficult question * drum roll *
And what will happen here?
console.log( "sample f" ); var f = function() { console.log(1); } f(); function f(){ console.log(2); } f();
correct answer: two times one. After its declaration, the variables overlap the function.
Is always.
Instant functions
Behind this name are hidden “one-time” functions - they do not have a name, and are executed immediately after their announcement.
Why do you need it? Mostly in order not to clutter up the global namespace. For example, if you need to initialize something, and during initialization, we need a couple of variables that are then completely unnecessary.
You can declare such a function in two, in fact, equivalent ways.
console.log("//immidiate function"); //sample a (function(){ console.log( "a" ); })(); //sample b (function(){ console.log( "b" ); }());
There are no differences between them.
Init time branching
Sometimes it happens that the value of a function depends on some value that does not change after initialization. Well, something like this:
// function saySomethingClever(){ var appleTest = /Apple/i; var googleTest = /Google/i; if( appleTest.test(navigator.vendor) ) console.log("I love apples <3") else if( googleTest.test(navigator.vendor) ) console.log("android is everything for me <3") else console.log("i love this unpopular corporation too") } saySomethingClever();
Everything is great, except that every time we make a call, we make the check that has already become absolutely unnecessary. You can rewrite the function like this:
// var saySomethingClever = (function(){ var appleTest = /Apple/i; var googleTest = /Google/i; if( appleTest.test(navigator.vendor) ) return function(){ console.log("I love apples <3"); } if( googleTest.test(navigator.vendor) ) return function(){ console.log("android is everything for me <3"); } return function(){ console.log("i love this unpopular corporation too"); } })(); saySomethingClever();
As the attentive reader could notice, the
immidiate function is also used here. Now the check is performed exactly once.
Self defining function
Sometimes it happens that when you first call a function, you need to perform some additional actions. You can implement this as follows:
var selfDefining = function() { console.log("some really heavy initialization occured"); console.log("f*ck yeah!"); selfDefining = function(){ console.log("job done!"); } } selfDefining(); selfDefining();
Such a technique helps to save one variable outside the function, according to which we would check whether the function was called or not.
Currying
Using this technique, you can create a private version of some fairly general function. The implementation looks like this:
function curry( fn ){ var slice = Array.prototype.slice, storedArgs = slice.call( arguments, 1 ); return function() { var args = storedArgs.concat( slice.call( arguments ) ); return fn.apply( this, args ); } }
While of course it's not clear, but this is normal. Now I will explain everything.
Suppose we have a function that prints a message.
function printMessage( author, message ){ console.log( author + " say: " + message ) }
But in this module of the project, author always use
me as the value of the author, so a more private version of it accepts only the line with the message, and the author inserts itself.
var printMyMessage = curry( printMessage, "me" )
And now we have it.
UPD. Addition to the article from one very good person with the nickname
jamaykaDeclaring recursive functions
For example, we have a stupid factorial, which is used in a heap of code:
var factorial = function (n) { return n === 1 ? 1 : factorial(n - 1) * n; };
so or
function factorial(n) { return n === 1 ? 1 : factorial(n - 1) * n; };
Then for some reason we need
var realFactorial = factorial; factorial = function (n) { throw 'please use realFactorial instead'; };
The result is that both the factorial (5) and realFactorial (5) calls throw an error. It turns out, because the recursive call factorial uses a variable from the parent scope, and there it is redefined. In this case, it is necessary to define a function like this:
var factorial = function factorial (n) { return n === 1 ? 1 : factorial(n - 1) * n; };
Well, or not to be confused:
var factorial = function f (n) { return n === 1 ? 1 : f(n - 1) * n; };
It seems to be all - as always, you can download the source code of the examples
here.