Function express CANNOT be used before being declared correct? But is also passed in a keypress function as well. How is this magic happening?
I am doing a code along and noticed it while I was looking over it.
var controller = (function(budgetCtrl, UICtrl){
var setUpEventListeners = function(){
//CTRLADDITEM is called below
document.querySelector(DOM.inputBtn).addEventListener('click', **ctrlAddItem**);
document.addEventListener('keypress', function(e){
if(event.keyCode === 13 || event.which === 13){
**ctrlAddItem();**
}
});
}
var **ctrlAddItem** = function(){
updateBudget();
}
};
}
})(budgetController, UIController );
This is called IIFE (Immediately Invoked Function Expression) and it's fairly common in Javascript as well as other languages like Go. Here is a simple version...
(function() {
alert('invoked immediately');
})();
This code will run immediately. Note: it is not invoked before it is defined. It is invoked by the trailing parenthesis, which come directly after it is defined. It is the exact same thing as doing this...
function doSomething() {
}
doSomething();
We have just inlined the function by wrapping it in parenthesis and then called it (), instead of calling it by name.
If you assign the result to a variable, it works just as expected, you are assigning the result of the function.
// these two are equivalent
var result = (function () {
return 5;
})();
var result = 5;
Now, the value in result will equal 5.
Why do we use them?
Most commonly, we use them to isolate the scope of your code to prevent it from polluting the global scope. For example, if this is your application code...
function MyApp() {
}
You have now polluted the global scope by creating window.MyApp. If you use a third party library that also provides a function on the global scope called MyApp, it will override yours. To prevent that, we can do...
(function(window) {
function MyApp() {}
MyApp();
})(window);
Now, MyApp is not attached to the window and we still have access to the window.
Further down the rabbit hole
You must convert your function from a declaration to an expression before you can invoke it immediately. To do this, you wrap it in parenthesis.
This does NOT work
function (){
// do something
}()
This DOES work (thanks to the parenthesis)
(function () {
})()
You can also use any unary operator instead of parenthesis. All of these work
~function () {
}()
+function () {
}()
-function () {
}()
void function () {
}()
What you have is:
// Pass ctrlAddItem
document.querySelector(DOM.inputBtn).addEventListener('click', ctrlAddItem);
// Include closure to ctrlAddItem in function expression
document.addEventListener('keypress', function(e){
if(event.keyCode === 13 || event.which === 13){
ctrlAddItem();
}
});
// later...
var ctrlAddItem = function(){ /* whatever */ }
So ctrlAddItem is declared, and therefore exists with a value of undefined before any code is executed. At the point it's used in addEventListener, its value is still undefined.
However, before the outer function completes (and before the listeners are called), ctrlAddItem is assigned a value. So by the time the listeners are called, it's a (reference to a) function.
Related
I'm getting the following error in the console:
Uncaught ReferenceError: jason is not defined
Here is my javascript:
$(document).ready(function jason() {
console.log("test");
});
var addEvent = function(object, type, callback) {
if (object == null || typeof(object) == 'undefined') return;
if (object.addEventListener) {
object.addEventListener(type, callback, false);
}
};
addEvent(window, "resize", jason());
Thanks so much for the help!
When you put it inside $(document).ready() it is not available outside that function. Make it global and define it without $(document).ready(), but this will run even before the page loads.
function jason() {
console.log("test");
};
var addEvent = function(object, type, callback) {
if (object == null || typeof(object) == 'undefined') return;
if (object.addEventListener) {
object.addEventListener(type, callback, false);
}
};
addEvent(window, "resize", jason());
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
In Javascript, there are many ways to define functions. They generally fall into two categories though: function declarations and function expressions.
Function declarations look like this:
function jason() {
console.log("test");
}
A function declaration has global scope regardless of where it is in the code, and it can be called earlier in the program than where it is defined. (Edit: actually, if a function declaration is nested within another function, then it can only be called within that outer function)
Function expressions look like this:
// Anonymous function expression set to a variable
let jason = function() {
console.log("test");
};
// Named function expression set to a variable
let jason = function freddy() {
console.log("test");
};
They are scoped just like any other variable. They must be set before they can be called.
In your case, you created jason() as a function expression but didn't assign it to a variable. You did name it jason, but that name only works inside of the function (like for recursion), not elsewhere. If you were only going to use the jason() function as an argument to $(document).ready(), then defining as you did would be ok. But since you seem to want to use jason() in more than one place, you either need to 1) change it a function declaration or 2) make it a function expression that is assigned to a variable prior to being passed to $(document).ready() and addEvent().
By the way, when passing a function as an argument, you need to leave off the parentheses, like this:
$(document).ready(jason);
addEvent(window, "resize", jason);
If you do the following instead, it will execute jason() and pass the value it returns to each of those funtions. In this case, that value would be undefined.
// Not what you want
$(document).ready(jason());
addEvent(window, "resize", jason());
Function expressions can be given a name, but as noted in the MDN link, the name is only available within the function's body.
Function declarations which cause a named function to be hoisted to the top of the function or script element in which they reside are also function statements.
Naming and providing the code for jsan as an argument value for a call to ready prevents it being treated as a statement and creates a function expression.
Obviously if you want to call or reference the jsan function in multiple places it needs to be declared in the scope of everywhere it's referenced - which could require creating a global function or declaring it in a common outer function, depending on the structure of the code.
Jason is wrongly define. Just check the below code, first you define a function and then call it.
$(document).ready(function() {
addEvent(window, "resize", jason());
console.log("test");
});
var addEvent = function(object, type, callback) {
if (object == null || typeof(object) == 'undefined') return;
if (object.addEventListener) {
object.addEventListener(type, callback, false);
}
};
function jason(){
console.log("call jason function");
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
You can write your code just in document .ready function().
$(document).ready(function(){
console.log("test");
})
var addEvent = function(object, type, callback) {
if (object == null || typeof(object) == 'undefined') return;
if (object.addEventListener) {
object.addEventListener(type, callback, false);
}
};
addEvent(window, "resize");
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
I have an IIFE that I am trying to make into a bookmarklet. I would like to have the modal the bookmarklet will pop up have some buttons that will call a function. However, when I have a structure like this:
(function(){
var __myFn = function(str){ //also have tried function __myFn(){..}
alert(str);
}
//some AJAX that builds HTML with the `result`s
document.getElementById("resultDiv").innerHTML = "<span onclick='__myFn(" + result.someData+ ")'>test</span>
}());
I get Uncaught ReferenceError: __myFn is not defined
How can I make this function recognized? Is there another way?
When you use the var keyword, you're creating a variable that is local to the scope of the enclosing context. Since the enclosing context is a function, __myFn is local to the function itself and not known outside (i.e., not known in the global context).
If you want to use something that is inside, you would have to return a reference to it. You can use something like the module pattern for this:
var myModule = (function(){
var __myFn = function(str) {
alert(str);
}
return {
myFn: __myFn
};
})();
Then you can do:
//some AJAX that builds HTML with the `result`s
document.getElementById("resultDiv").innerHTML = "<span onclick='myModule.myFn(" + result.someData+ ")'>test</span>
However, I recommend not binding your event handler this way. Use jQuery or use DOM methods (addEventListener) to bind event handlers. This way you could even do it inside the IIFE itself, which means you don't even have to return something from the IIFE. This means your global context is not polluted. Right now, the only reason you have to return something from the IIFE is because you are binding an event-handler inline via HTML.
Here are two examples. The first one assumes that the IIFE returns a reference to __myFn:
var resultDiv = document.getElementById("resultDiv");
resultDiv.addEventListener("click", myModule.myFn, false);
Here is the second example that does it within the IIFE itself:
(function(){
var __myFn = function(str) {
alert(str);
}
var resultDiv = document.getElementById("resultDiv");
resultDiv.addEventListener("click", __myFn, false);
})();
For reasons I don't understand, I have found that taking the "var" off the declaration makes it work.
Example 1 (fails):
(function() {
var counter = 0;
var init = function() { console.log("init called"); }
})();
init(); // regardless of whether I named the function or not, this fails.
HOWEVER:
Example 2 (works):
(function() {
var counter = 0;
init = function() { console.log("init called"); }
})();
init(); // this calls the function just fine.
Note you can add any number of functions inside the iife this way just fine.
I was playing around with javascript objects to understand "this" and function context better. I stumbled across this problem. I get the error "obj2 is not defined" unless I run window.obj2() after assigning it, but I don't know why. Shouldn't it be enough to assign the function to window.obj2 without also executing it immediately afterwards? I know that you're not supposed to pollute the window object, this is just a test.
Thanks!
window.obj2 = function(){
console.log('obj2 in window.object',this);
}
window.obj2(); // problem when this line is commented out
(function () {
var parent = {
obj : function(){
//console.log(this);
obj2();
this.obj2();
window.obj2();
},
obj2 :function(){
console.log('obj2 in parent',this);
}
}
parent.obj();
}());
EXPLANATION
OP asks why does he have to execute the function after defining it in order for it to become defined later in the code... See what happens when you comment out the problem line.
Solution to the mystery:
You forgot a semicolon:
window.obj2 = function(){
console.log('obj2 in window.object',this);
}; // <--
Without it, the code will be interpreted as
// I'm naming the functions to refer to them later
window.obj2 = function a(){
...
}(function b() { ... }());
I.e. the parenthesis around b are interpreted as call operation of a (just like you did with b itself: (function b() {...}()).
The engine first executes b in order to pass the return value as argument to a, and only after that the return value is assigned to window.obj2.
So, at the moment b is called, window.obj2 does indeed not exist yet.
So, the reason why adding window.obj2() makes it work is not because you are accessing window.obj2, but because it makes the code un-ambigious. The following parenthesis cannot be interpreted as call operation anymore. You could use any statement there, e.g.
window.obj2 = function(){
console.log('obj2 in window.object',this);
}
"foo";
(function () {
obj2();
}());
If you defined function window.obj2 inside the anonymous function which does call itself then it works fine, have a look code.
<script>
//window.obj2(); // problem
(function (window) {
window.obj2 = function(){
console.log('obj2 in window.object',this);
}
var parent = {
obj : function(){
//console.log(this);
obj2();
this.obj2();
window.obj2();
},
obj2 :function(){
console.log('obj2 in parent',this);
}
}
parent.obj();
}(window));
</script>
<body>
</body>
I have read a lot about closures in Javascript
What are those braces for??
I read on mozilla.org which says closure should be defined as
(function(){...})();
but on http://www.adequatelygood.com/JavaScript-Module-Pattern-In-Depth.html, it says the closure function is
(function(){...}());
What's the difference or the latter one is wrong?
what's the purpose of the last ()? Would you put some parameters inside?
I am looking for a good reference.
Edit:
Moreover, there is an example on Mozilla.org
var makeCounter = function() {
var privateCounter = 0;
function changeBy(val) {
privateCounter += val;
}
return {
increment: function() {
changeBy(1);
},
decrement: function() {
changeBy(-1);
},
value: function() {
return privateCounter;
}
}
};
why the semicolon is needed for this 'function'? If it needs to be invoked immediately after its declaration, a () should be put before the ending semicolon. But there is not.
The syntax
(function(){...})()
is simply an immediately invoked anonymous function. It does not matter how you use your brackets, as the underlying code is a function being declared, and invoked.
Closures are instead used to describe a situation where a function has access to variables declared outside of its scope, accessible via closures
For clarity :
If we have the following function
function hello() {
alert("Hello");
}
We can call the function with the following
hello()
Which invokes the function 'hello'. But if we do not wish to give it a name, but still invoke it, then we can do
(function hello() {
alert("Hello");
})()
Which will do the exact same as the previous example of calling hello
However, in this scenario there is no point in giving the function the name 'hello', so we can simply remove it:
(function() {
alert("Hello");
})()
Which is the notation used in your original question.
Your example shows an Immediately Invoked Function Expression, or IIFE. It says to the interpreter:
here is a function
it has no name
keep it away from the global scope ie 'window'
call it now
Yes, you can put parameters inside the last (). For example:
(
function(username){
alert("Hello " + username);
}
)("John Smith")
Closures are a feature of javascript that allows us to implement data hiding which is roughly equivalent to private variables in languages like C++ or Java.
function getBmiCalculator(height, weight) {
// These are private vars
var height = height;
var weight = weight;
function calculateBmi(){
return weight / (height * height);
}
return calculateBmi;
}
var calc = getBmiCalculator(1.85, 90);
// calc still has access to the scope where height and weight live.
var bmi = calc();
alert(bmi);
In this example, height & weight cannot be garbage-collected until calc is destroyed. The section of memory or "scope" where height & weight exist are "Closed Over"
There is no difference. You can also do so:
true && function(){ /* code */ }();
0,function(){ /* code */ }();
!function(){ /* code */ }(); // Facebook style
~function(){ /* code */ }();
-function(){ /* code */ }();
+function(){ /* code */ }();
// with new
new function(){ /* code */ }
new function(){ /* code */ }() // if you need arguments then use brackets
the grouping operator can surround the function description as without call parentheses, and also including call parentheses. I.e. both expressions below are correct FE:
(function () {})();
(function () {}());
Function Expression
How do I explain the role of the arguments passed in the beginning vs end of a wrapped Javascript closure as the one shown below?
(function($, window) {
return $(function() {
return alert("js!");
});
})($, window);
The first appearance are function parameters, the second is passing values for those parameters when executing the function.
Keep in mind, the parameters to the function don't need to match the names being passed (this could, in fact, cause confusion later on):
(function(jQuery, w) {
return jQuery(function(){
return alert("js!");
});
})($, window);
Would work the same way.
In that way you are passing arguments that will be safe in the scope from an overwrite in the future. For example:
var a = 1
(function(a){
setTimeout(function(){
console.log('This variable is still safe', a);
},2000)
})(a)
a = 0
console.log('has changed', a)
So in your example you can be sure that $ and window will be that what you are expecting.
It may be easier to explain if you change the argument names and name the function
(function init($, win) {
return $(function() {
return alert("js!");
});
})(jQuery, window);
The init function is being passed the parameters jQuery and window immediately as it's defined, they are available as arguments to the init function as $ and win
If you break it out into the equivalent code, that may also be easier to understand
function init($, win) {
return $(function() {
return alert("js!");
});
}
init(jQuery, window);
The set of parameters on top is where the parameters are recieved, and the set of parameters on the bottom is where they are passed.
They're ensuring that their closed copies of the global variables don't get reassigned outside of the closure. It is a way of protecting your code against other (potentially poorly written) code. Consider this example:
var $ = 'foo';
var blah = (function($) {
return function () {
alert($);
};
})($);
var shizzam = (function() {
return function () {
alert($);
};
})();
// someone evil overwrites my $ var
$ = 'bar';
// blah still works
blah();
// but shizzam is now borked
shizzam();
http://jsfiddle.net/xfTcq/
When a function returns another function it can be enclosed on parentesis to be immediate executed.
The last parenthesis are the passed arguments to it.
You can do some tests do understand:
var a (function(){});
typeof a;
a.toSource()
typeof (function(){});
(function(){}).toSource()