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!"
The 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.
Related
I have a question about whether or not what I'm attempting is possible in Javascript in regards to closures and parent scopes. Here's my code:
var func1 = function() {
// console.log(this.source1); // wont work, makes sense
// console.log(source1); // wont work, wish it would
console.log(this.source2); // works fine
console.log(source2); // works fine
};
var func2 = function() {
var source1 = "source1";
this.source2 = "source2";
func1.call(this);
}();
var func3 = function() {
var source3 = "source3";
var func4 = function() {
console.log(source3); // also works fine, makes sense
}();
}();
Is there any way I can get access to variables declared with var on func2 inside func1, or am I out of luck?
As others have said -- no.
But if you put this whole thing in a wrapper and use the Revealing Module Pattern, then you can.
var module = (function() {
var source1;
var source2;
var func1 = function() {
console.log(source2); // works fine
};
var func2 = function() {
source1 = "source1";
}();
var func3 = function() {
var func4 = function() {
console.log(source1); // also works fine, makes sense
}();
}();
return {
func1: func1,
func2: func2,
func3: func3
};
}());
// Then invoke them.
module.func2();
module.func1();
EDIT
Then you can re-assign them back to the original names.
var func1 = module.func1;
var func2 = module.func2;
var func3 = module.func3;
Well, no, it won't work, and it shouldn't. Thankfully :) That's exactly how local variables are supposed to behave.
One thing you can do is pass the desired variable to the function func1 (and the function should expect it. Which is no problem — if you wish to somewhere call this function without passing a variable, Javascript would be totally okay with it)
Another thing is to declare the source1 variable without "var" keyword. Then it would work, but you really shouldn't do that unless you're keeping a very good track of your global variables.
The answer to your question is no.
The scope of the inner function is limited to its own scope and the scope of the outer function in which the inner function is declared (not called but declared).
console.log(this.source2); works fine cause both function have window as their context (window plays role of the outer function). this = window inside those functions.
I have tried folllowing two ways of referring a function:
First
let a = function() {
somefunction();
}
Second
let a = somefunction;
Where somefunction is the following in both cases:
function somefunction() {
alert("hello");
}
Is there any difference between these two ways?
Yes, there is a difference between your two examples.
In the first case, you are defining a new anonymous (unnamed) function which calls somefunction. You are then assigning your new function definition to the variable a. a holds a reference to your new function.
In the second case, you are simply assigning your original function of somefunction to the variable a. The variable a then holds a reference to somefunction. You are not creating a new function as you are in the first case.
I think this example may make the difference clear. arguments is an array like object that contains each of the arguments passed to a function.
Try running each of these lines on your favorite browser console.
var somefunction = function() { console.log(arguments); };
Your first example demonstrates defining a named function a that closes around the named function somefunction.
var a = function() { somefunction(); };
Your second example makes a reference, b, directly to somefunction. This makes invoking b the same as invoking somefunction.
var b = somefunction;
Now if you call each of these a and b with some arguments you will see the difference.
=> a('a', 1);
[]
=> b('a', 1);
['a', 1]
In the first case the arguments object is empty. That's because the arguments that were passed to a were not forwarded onto somefunction.
In the second case the arguments are available to somefunction, because some function is being called directly.
Here is how you could redefine a so that it were functionally equivalent using apply
var a = function() { somefunction.apply(this, arguments); }
Running this at your console prints the argument array.
=> a('a', 1);
['a', 1]
var a = function(){
somefunction();
}
Is an Anonymous Function attributed to a variable.
somefunction :function() {
alert("hello");
}
Is an declaration of a function throungh the Object Literal notation.
The diference are shown when you are creating an object. The anonymous function are not acessible as a "public" method, instead in the Object Literal notation, that are acessible from outside.
As Douglas Crockford said, in JS the Good Parts, the first declaration are just a function and the second one could be a method.
In the first case, you are creating a function which calls someFunction(), then you assign that function to a, so now calling a() calls an anonymous function which in turn calls someFunction().
In the second case, a and someFunction become the exact same thing, calling a() is the same as calling someFunction().
The way you're setting var a by accessing the function is clearly out of scope.
So I suspect you have a typo : instead of = :
var somefunction = function() {
alert("hello");
};
somefunction(); // hello
...Now that your first and second makes sense with the code above:
Anonymous Function stored in variable:
var a = function(){
alert('Hey');
somefunction();
};
a(); // Hey // hello
Variable as Function Reference
var a = somefunction;
a(); // hello
In the other case than:
var objLiteral = {
somefunction : function() {
alert("hello");
}
};
var a = objLiteral.somefunction;
a(); // hello
When I run the following code, the alert messages shows up but this.test() does not run.
function SomeFunction() {
document.onclick = function(e) {
alert("Hi");
this.test();
};
}
SomeFunction.prototype.test = function (){
...
}
However when I try this:
function SomeFunction() {
document.onclick = this.test();
}
SomeFunction.prototype.test = function (){
...
}
this.test() will run.
EDIT: I have added more details to my code.
Your issue is related to scope. Try:
document.onclick = function(e) {
alert("Hi");
test();
};
this will vary depending upon where it is called from. In your first example, you are calling it from within an anonymous function and therefore the scope of this shifts to inside that function. In the second example, the scope is a level higher and refers to your SomeFunction() function.
Since your function is nested inside another function, you may want to reassign this to a another variable that is accessible up the scope chain. E.g.
function SomeFunction() {
that = this;
document.onclick = function(e) {
alert("Hi");
that.test();
};
}
In the first example, the word "this" refers to function itself. So the function test would be outside its scope.
But on the second one you are telling the code that this.test() is the actual onclick function, that is why it's working there and not on the first one.
What I want to do is to sending data between two handlers.
element.onmousedown = function() {
data = precalculate();
}
element.onmouseup = function() {
dosomething(data);
}
if the data is a global variable it works. People says global variable is evil. But I don't know how to do without it.
or I misunderstood "global variable"?
Just scope the variable if you don't want/need it to be global:
(function() {
var data;
element.onmousedown = function() {
data = precalculate();
}
element.onmouseup = function() {
dosomething(data);
}
})();
EDIT: To clarify, the only way to create a new variable scope in javascript is in a function.
Any variable declared with var inside a function is inaccessible to the outer scope.
In the code above, I created an IIFE (immediately invoked function expression), which is simply a function that is invoked as soon as it is created, and I placed your data variable (along with the handler assignments) inside of it.
Because the handlers were created in a scope that has access to the data variable, they retain their access to that variable.
To give another example:
var a = "a"; // global variable
(function() {
var b = "b"; // new variable in this scope
(function() {
var c = "c"; // new variable in this scope
// in this function, you have access to 'a', 'b' and 'c'
})();
// in this function you have access to 'a' and 'b' variables, but not 'c'
})();
// globally, you have access to the 'a' variable, but not 'b' or 'c'
In this case a global variable would make sense. Another possibility is to attach the value to the DOM element:
element.onmousedown = function() {
// 'this' should point to the element being mouse downed
this.data = precalculate();
};
element.onmouseup = function() {
// 'this' should point to the element being mouse upped
var data = this.data;
dosomething(data);
};
You misunderstood "global variable is evil".
In fact, what really happened is that someone wanted to be "part of the crowd", and so told you a sweeping generalisation, when in fact they should have said "only use global variables where appropriate".
Well, they are appropriate here, my friend.
JQuery makes this possible by using the .data() function:
http://api.jquery.com/jQuery.data/
You can get away with using global variables as long as you keep them to a minimum, for example you can place stuff in a single global namespace:
App = {};
element.onmousedown = function() {
App.data = "hello";
}
element.onmouseup = function() {
console.log(App.data);
}
Another more general solution is to create functions that cache their results using a technique that is called memoization. There's plenty of stuff if you search.
The following code does exactly that :
Function.prototype.memoize = function() {
var fn = this;
this.memory = {};
return function() {
var args = Array.prototype.slice.call(arguments);
return fn.memory[args] ? fn.memory[args] : fn.memory[args] = fn.apply(this, arguments);
};
};
e.g. You have an expensive function called exfunc..
newFunc = exfunc.memoize();
The above statement creates a new function called newFunc that caches the result of the original function so that the first time the actual code is being executed and all subsequent calls are retrieved from a local cache.
This mostly works with functions whose return value does not depend on global state.
More info :
http://osteele.com/archives/2006/04/javascript-memoization
How can i call a YUI function that is wrapped inside a YUI().use from javascript?
example
Below is a YUI function "runShowAnim" which executes animShow.run(); for an animation effect...
var runShowAnim = function(e) {
animShow.run();
};
I want this effect to happen when i validate something in a javascript function. I tried to call it as below. But it doesn't seem to work.
function notifyUser(message) {
document.getElementById("msgArea").innerHTML = message;
runShowAnim();
}
I achieved this by sandwiching the YUI function completely inside a function and calling that function..
var runShowAnim = function() {
YUI().use('anim', 'node', function(Y) {
var animShow = new Y.Anim({
node: '#msgArea',
to: { height: 50,opacity:1 }
});
animShow.run();
});
};
now i can call runShowAnim without any problem like in the below sample function..
function notifyUser(message) {
document.getElementById("msgArea").innerHTML = message;
runShowAnim();
}
If you want to call a function, you have to suffix the function name with () and include 0 or more comma separated arguments between them.
runShowAnim();
If the function doesn't have global scope (as yours will have if it is declared inside a function passed to use()) and not passed outside in some way then you can only do this from the same scope.
I think you're missing the parentheses.
function notifyUser(message) {
document.getElementById("msgArea").innerHTML = message;
runShowAnim(); // right here
}
YUI.thefunction()?
I think you need to call it with namespace too
something similar to
var X = function(){};
X.Y = function(){};
X.Y.Z = function(){};
X.Y.Z.foo = function(e){alert(e);}
//foo("me");<-error
X.Y.Z.foo("me");
If you want to call a function that has been defined inside the closure (the function passed as the last parameter to YUI.use) from outside it, you need to expose the function globally.
Either define a global variable outside the closure and assign your function to it, or assign your function to the window object
i.e.
var runShowAnim;
YUI().use(function(e){
runShowAnim = function(){alert('called');}
});
runShowAnim();