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.
Related
I'm a javascript newbie and trying to understand how functions work. I found a similar question here but it doesn't really answer my question.
Taking the following piece of javascript as an example
var test = function(){
console.log("kick off");
var insideTest = "variable inside test";
var init = function(){
var insideInit ="variable inside init";
console.log("inside init");
}
return{
init:init
}
}
test().init();
The above code prints the following:
kick off
inside init
But if I remove
return{
init:init
}
it gives me an error saying
Uncaught TypeError: Cannot read property 'init' of undefined
Also, even though I'm calling init method using test().init() it doesn't print inside Init if the return statement is removed.
My question is why is it necessary to return init:init to execute init method.
EDIT:
To answer why my init function is inside the test() function here is the larger picture of what i'm trying to do.
var test = function() {
var init = function() {
var a = 0;
function1();
function2();
}
var function1() = function() {
//some code
}
var function1() = function() {
//some code
}
return {
init: init
}
}
Have added inline comments. Also the init inside test will have access to variable defined outside it(init) scope which is closure. test is returning an object to access it's inner function.This specific pattern is revealing module pattern
var test = function(){
console.log("kick off");
var insideTest = "variable inside test";
// Here init is a function which is private to test function
// Any function calling test will not have access to init unless it is 'exposed'
var init = function(){
var insideInit ="variable inside init";
console.log("inside init");
}
return{
init:init // exposing the private function
}
}
When you return, you're returning an Object with a single key of init, which you've assigned the "init" function that you defined within the test function. This allows you to return multiple functions if you'd like, so you can chain calls.
If you'd prefer a different way, you could just return the function without the curly braces, ie. return init;, and then assign the return of test() to a variable. var externalInitFnc = test();
Edit: Looking back it seems that you are fuzzy on the idea of scope in Javascript. When you defined init within the test function, it is only accessible within that function. Similar to how a private variable in a Java class is only available within that same class.
Let's say I have a function myFunction which in it's body uses results of other function otherFunction and those results are constant per every invocation.
function myFunction() {
doSomething(otherFunction());
}
In the past I used IIFE-s to invoke otherFunction only once and so optimize the code:
var myFunction = (function() {
let otherFunctionResult = otherFunction();
return function() {
doSomething(otherFunctionResult);
};
}());
I wonder if ES6 const keyword would achieve the same result without using IIFE:
function myFunction() {
const OTHER_FUNCTION_RESULT = otherFunction();
doSomething(OTHER_FUNCTION_RESULT);
}
Can I expect const to be optimized so that otherFunction is invoked only once? That would greatly simplify the code.
The fact that OTHER_FUNCTION_RESULT is declared as const doesn't mean it's only ever called once:
function callOften(){
const test = init(); // This is a `const`, but does it get called more often?
}
function init(){
console.log('Init Called');
return 1;
}
callOften();
callOften();
callOften();
Basically, your IIFE is one way to do what you want.
Yes, you don't need IIFEs any more in ES6. However, you seem to confuse const with what static does in other languages. You still need to initialise it outside of the function if you want to invoke otherFunction only once. It would look like this:
var myFunction;
{ // block scope!
let otherFunctionResult = otherFunction(); // `const` works the same here
myFunction = function() {
doSomething(otherFunctionResult);
};
}
Admittedly, an IIFE is still nicer to read if your module has a result (here: the function) that is supposed to be stored in a non-local variable.
Recently while I was trying to learn more about IIFE and modules in JavaScript
a question came to my mind that how is IIFE making a Module while not Immediately
Invoking the function doesn't make it a module..
can anyone share with me the Difference between this code
var MODULE = (function () {
var my = {},
privateVariable = 1;
function privateMethod() {
// ...
}
my.moduleProperty = 1;
my.moduleMethod = function () {
// ...
};
return my;
}());
and this code where the function is not Immediately Invoked..
var MODULE = function () {
var my = {},
privateVariable = 1;
function privateMethod() {
// ...
}
my.moduleProperty = 1;
my.moduleMethod = function () {
// ...
};
return my;
};
Does the second block of code means that Module is just a function that itself returns an object?
IF I use the second variable like this
var ModuleObj = Module();
Will this work the same as the first Code block that I shared like IIFE.. Kind of confused...
Yeah you pretty much got the idea of the difference between the two, let's look at why you might want one over the other.
An IIFE is useful to isolate the scope. It lets you keep the variables you define private inside the IIFE without polluting the global space around it. It's a nice way to compose a function that has some variables you don't need lurking around. Let's minimize this example a bit.
var Counter = (function () {
var count = 0;
var counter = {
add: function () {
count++;
},
subtract: function () {
count--;
},
getCount: function () {
return count;
}
}
return counter;
})();
Counter.add();
Counter.add();
Counter.getCount(); // 2
Counter.subtract();
Counter.getCount(); // 1
What happens above is that we're able to compose this "counter" functionality without leaking the private information, like count. It'd be bad if other things could override it by accident. Also what happens is that right away we can assign Counter to the result of the IFFE -- the counter set of functions. Counter is now equal to that, and counter is able to retain access to count since it was defined in the same scope.
The benefit here is that we're able to assign a variable to this composition of functionality. The IIFE basically allows us to immediately return what we return inside of it. Since we assign Counter to the IIFE, and the IIFE returns the functionality inside of it, Counter is now a fully functional component.
We don't always have to use IIFE. It's really handy when you want to "tuck away" the implementation details and return an API.
So, what if we had the same thing, but it wasn't an IIFE -- just a function?
Just like your example, we'd have to call it in order to get the "instance".
var CounterFactory = function () {
var count = 0;
var counter = {
add: //...
subtract: //...
getCount: //...
};
return counter;
};
var CounterA = CounterFactory();
var CounterB = CounterFactory();
CounterA.add();
CounterA.add();
CounterA.getCount(); // 2
CounterB.add();
CounterB.getCount(); // 1
See the difference? It's all about what the function is returning. In the first example we only get a single Counter instance, which may be perfectly fine. In the second example, it's more of a "factory" -- it generates an instance of counter and we can call that multiple times and get multiple instances of it.
Ok an IIFE runs the functions within it and defines the variable MODULE to the return of that function. The other declares the MODULE variable to the function itself.
Think of it this way (also try it in your console to see the results).
This code does not run the console.log method.
(function(){
console.log('ran')
});
This code does
(function(){
console.log('ran')
})();
So the whole point of the IIFE is to run the function before doing anything and the (); at the end does this.
If we take the code that did not run and assign it to a value what happens?
var foo = (function(){
console.log('ran')
});
foo();
We have a function foo that we can execute.
So what is the point of an IIFE if we can just assign it and run it later? The answer to that is local variables which you can use for closure later.
console.log(num); //get undefined
(function(){
var num = 'ran';
console.log(num) //get 'ran'
})();
console.log(num); //get undefined
We get undefined ran then undefined so the values we declare in the function stay in the function and nothing else can get to them. This is the lexical scoping that JavaScript runs off of.
Just for fun lets do a closure with it.
var add = (function(){
var num = 0;
return function(){
console.log(num++);
}
})();
console.log(num) //get undefined
add() //get 1
add() //get 2
console.log(num) //still undefined
I have a file called bbUI.js which contains this bit of JavaScript. Outside of that file, I'd like to be able to call "var x = new iScroll(...)", but I currently get the error "ReferenceError: Can't find variable: iScroll".
(function(){
var iScroll = function (el, options) {
var that = this,
doc = document,
i;
// More code
};
})();
From what I can tell, iScroll is defined within an anonymous function, and is itself anonymous but assigned to the identifier iScroll. If that's accurate, should I be able to call "var x = new iScroll(...)" in other places in my code?
The iScroll function only exists within the scope of the anonymous function it's wrapped in. To use it elsewhere, you need to make it global.
You can make it global by removing the function it's wrapped in, or by setting window.iScroll = iScroll inside the anonymous function.
Remove the anonymous function that wraps the code:
(function(){ // <- this
...
})(); // <- this
The anonymous function prevents that code from polluting the global variables, so iScroll is defined only within that anonymous function.
Answer to this question depends on what do you really want to achieve and what the purpose of a self executing function. Anyway.. here iScroll is inside the self executing function and you have to make it available in the scope outside. I have given below two methods to do this.
Either just make it global by removing var keyword
(function(){
iScroll = function (el, options) {
var that = this,
doc = document,
i;
this.show = function(){
alert("hello world");
}
};
})();
or by adding a variable iScroll externally and returning the function.
var iScroll = (function(){
return function (el, options) {
var that = this,
doc = document,
i;
this.show = function(){
alert("hello world");
}
};
})();
to test
var iscroll = new iScroll();
iscroll.show();
Another option could be the following:
var iScroll = (function(){
var iScroll = function (el, options) {
};
return iScroll; // <-- add this line
})();
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();