In the "JavaScript Patterns" book by Stoyan Stefanov, there's a part about Self-Defining Function.
var scareMe = function(){
console.log("Boo!");
scareMe = function(){
console.log("Double Boo!");
}
}
scareMe();//==>Boo!
scareMe();//==>Double Boo!
It works as I expected. But I modify the scareMe function as following:
function scareMe(){
console.log("Boo!");
function scareMe(){
console.log("Double Boo!");
}
}
scareMe();//==>Boo!
scareMe();//==>Boo!
Problem:
- what's the difference between them?
- In the second case, why the output is not "Double Boo!", but "Boo!"
In the "JavaScript Patterns" book by Stoyan Stefanov, there's a part about Self-Defining Function.
var scareMe = function(){
console.log("Boo!");
scareMe = function(){
console.log("Double Boo!");
}
}
scareMe();//==>Boo!
scareMe();//==>Double Boo!
It works as I expected. But I modify the scareMe function as following:
function scareMe(){
console.log("Boo!");
function scareMe(){
console.log("Double Boo!");
}
}
scareMe();//==>Boo!
scareMe();//==>Boo!
Problem:
- what's the difference between them?
- In the second case, why the output is not "Double Boo!", but "Boo!"
- This pattern is amazing. Besides obfuscation, is there any practical use ?? – GameAlchemist Commented Sep 30, 2013 at 2:03
-
1
@GameAlchemist: Yes. Sometimes I have a function to do something that I only want to do once; initialization, say. I can define that function as so:
function init() { init = function() {}; /* ... */ };
And now as long as I'm only usinginit
by name and not actually capturing a reference to the original function, it will only initialize once. This could be done with a boolean variable, but this way you've got one less variable to keep track of. – icktoofay Commented Sep 30, 2013 at 2:11 - interesting. So for instance the rAF replacement by setInterval could bee : requestAnimationFrame = function(cb) { requestAnimationFrame=function(){}; return setInterval(cb,16); }. A bit cryptic, but elegant. – GameAlchemist Commented Sep 30, 2013 at 2:20
- for the record you mistyped the name : it is Stoyan Stefanov, my friend google said. – GameAlchemist Commented Sep 30, 2013 at 2:26
- @GameAlchemist: in the book, the author also use this pattern to create Singleton pattern. function Singleton(){instance = this; Singleton = function(){return instance};}. Then When you create two instance, they are the same. var s1 = new Singleton(); var s2 = new Singleton(); console.log(s1 === s2); – jason Commented Sep 30, 2013 at 8:52
3 Answers
Reset to default 11The first scareMe
function when invoked overwrite its own behavior by creating another function scareMe
inside it, which overwrites the one in the upper scope, so the definition of original scareMe
changes, i have see this approach been used if you want to do a first time set up in an application and want to change its behavior all over right after setting it up.
If you had defined:
var scareMe = function(){
console.log("Boo!");
var scareMe = function(){ //define it with var
console.log("Double boo!");
}
}
scareMe();//==>Boo!
scareMe();//==>Boo! //you will see the behavior as that of the second one.
Also one practical implementation of a one time setup:
var scareMe = function(){
console.log("Boo!");
//I have done my job now. I am no longer needed.
scareMe = undefined;
}
scareMe();//==>Boo!
scareMe();//==> oops error
Second Case you are creating a new function with the name scareMe
whose scope is only within the function, it doesn't overwrite itself.
Try this one for instance:
function scareMe(){
console.log("Boo!");
function scareMe(){
console.log("Double bool!");
}
scareMe(); //Now this invokes the once defined inside the scope of this function itself.
}
scareMe();//==>Boo! and Double bool!
In your first approch, scareMe is a global variable (in your context). When in the "double boo", you change the value of that global variable, so it works. In the second approch, the inner scareMe is a local variable and it won't change value of the global one. So it's about the variable scope.
Except for hoisting and debuggability, you can consider:
function f(/* ... */) { /* ... */ }
To be equivalent to:
var f = function(/* ... */) { /* ... */ };
If we translate your second code sample to use this second form, we get:
var scareMe = function() {
console.log("Boo!");
var scareMe = function() {
console.log("Double bool!");
};
};
Note that this is not the same as your first snippet; the inner function definition has a var
on it. With the inner var
, it creates a new variable called scareMe
that shadows the outer one.