I've been trying to make an object method an iife, but i've had no luck at all. Is it possible? We are storing all of our functions as methods in an attempt to keep them organised.
I have chosen ONE object & one method to illustrate what I am trying to do, though we have many more.
I'd like to IIFe-fy them because as soon as they initially fire, they have done their job and aren't needed again, with that in mind along with how many methods we will have, to me it makes to iife-fy them.
My code is:
let trans = {
bind_toggle: function(){
if ($('.tw-o').length != 0) {
// window open, add toggle
$('.tw-h').click(function(e){
$('.tw-b').toggle(599);
});
}
}
}
I'd like trans.bind_toggle to be invoked without having to call it myself... an IIFE, but i've not been able to pull it off. I've tried:
let trans = {
bind_toggle: (function(){
if ($('.tw-o').length != 0) {
// window open, add toggle
$('.tw-h').click(function(e){
$('.tw-b').toggle(599);
})();
}
}
}
The above returns
Uncaught TypeError: trans.bind_toggle is not a function
Is it possible to use an IIFE in an object? if so, how would I do it?
Thank you :)
Instead storing one-timer-functions in an object, I'd suggest you to wrap all the code in an IIFE, and then declaring the one-timers in the scope of that function, or make them IIFEs like in the example below. That way they will be decently garbage collected after the IIFE has run. Something like this:
(function () {
(function () {
// Logig for bind_toggle
}());
// All the rest of your code
}());
Having all the code in an IIFE you can avoid to declare most of global variables too. If a global is really needed, you can define it as a property of window, like window.someGlobal = 1.
Related
JavaScript has all amounts of crazy flexibility. I decided to take advantage of it and have a function change itself on the first call. Is this a bad thing to do? It works like this:
(function(){
var nextAfter = function(){};
Something.prototype.next = function(){
//do pre-start actions.
this.next = nextAfter;
};
})();
This function is called inside of a main loop, so it gets called many times, but the instance is only ever "supposed" to be instantiated once.
It is a perfectly reasonable thing to do.
For example, It can be a useful way of implementing state changes in a state machine, but I'm sure that you could find many other uses.
You may also want to look into how to implement the same functionality with closures -- it may be cleaner depending on the use case.
Edit; example of a closure which doesn't change the prototype
Something = (function(){
var next = function() { next = nextAfter; console.log("A"); }
var nextAfter = function() { console.log("B"); }
return {
next: function(){ next(); }
}
})();
The benefit of the closure is that you don't change the global prototype function for that object type, and you can now have multiple independent object where each closure object can keep their own state.
WARNING!! I AM A NOVICE THROUGH AND THROUGH
Alright, so I know there have been a lot questions about Global variables, and I think that's what I'm looking for, but, not exactly. Lately I've been needing to call upon the same lines of code several times. document.getElementById("example").style or similar to little things like that but I need to continuously repeat.
My question is how do I make it so that I make one variable, outside of the function, to save time writing these lines?
What I've been seeing is to simply write it outside like this var inferno = document.getElementById("inferno"); but this is far from working.
This is my code right now, it's simple because I was just using it as a test, but can anyone help me?
var inferno = document.getElementById("inferno");
function infernoClick () {
inferno.style.backgroundColor="red";
}
You have the right idea. Note, though, that the variable doesn't have to be global. It just has to be where all of the code that wants to use it can use it.
For example, this creates a global:
<script>
var inferno = document.getElementById("inferno");
function infernoClick () {
inferno.style.backgroundColor="red";
}
function somethingElse () {
inferno.style.color="green";
}
</script>
(Note that this needs to be after the markup creating the inferno element.)
The problem with globals is that they can conflict with each other, and in fact the global "namespace" is really, really crowded already.
You can avoid that by wrapping up the code that needs inferno in a scoping function, like this:
<script>
(function() {
var inferno = document.getElementById("inferno");
function infernoClick () {
inferno.style.backgroundColor="red";
}
function somethingElse () {
inferno.style.color="green";
}
})();
</script>
That code creates an anonymous function and then calls it immediately, running the code inside.
Now inferno is "global" to the functions that need it, but isn't actually a global.
Let's take a further example:
<script>
(function() {
var outer = 42;
function doSomethingCool() {
var inner = 67;
document.getElementById("someElement").onclick = function() {
alert("inner = " + inner + ", outer = " + outer);
};
}
// Can't use `inner` here, but can use `outer`
alert("outer = " + outer);
doSomethingCool();
})();
</script>
That code wraps everything in a scoping function, and the outer variable is accessible everywhere within that scoping function. It also has a function, doSomethingCool, which has a variable called inner. inner is only accessible within doSomethingCool. Look at what doSomethingCool does: It hooks up an event handler for when someElement is clicked. It doesn't call the function, it just hooks it up.
The really cool thing is that later, when someone clicks the element, that function has access to that inner variable.
And in fact, that's true for arguments you pass into the function as well. One last example:
<input type="button" id="element1" value="One">
<input type="button" id="element2" value="Two">
<script>
(function() {
function hookItUp(id, msg) {
document.getElementById(id).onclick = function() {
alert(msg);
};
}
hookItUp("element1", "This message is for element1");
hookItUp("element2", "And this one is for element2");
})();
</script>
There, we have this function that accepts a couple of arguments, and we call it twice: Once to hook up click on element1, and again to hook up click on element2.
The really cool thing here is that even though the clicks happen much later, after the calls to hookItUp have long-since returned, the functions created when we called hookItUp still have access to the arguments we passed to it — when we click element1, we get "This message is for element1", and when we click element2, we get "And this one is for element2."
These are called closures. You can read more about them on my blog: Closures are not complicated
That'll work, but only if the declaration appears after the point in the DOM where the element actually appears. Try moving your <script> to the very end of the <body>.
Another thing you can do is use the window "load" event to make sure the whole DOM has been seen before your code runs.
for example
var myGlobalVars = {"inferno":null,"othervar":null}; // globals in their own scope
function clickMe(varName,color) { // generic function
myGlobalVars[varName].style.backgroundColor=color;
}
window.onload=function() {
// initialise after the objects are available
for (var o in myGlobalVars) myGlobalVars[o]=document.getElementById(o);
// execute
clickMe("inferno","red");
}
.
.
T.J. Crowder gave a beautiful answer about scoping; just to add on that you can also use an immediately-invoked function expression to create a module with your UI elements, i.e.
var UI = (function() {
...
return {
inferno: document.getElementById("inferno");
};
})();
...
UI.inferno.style = ...;
I'm trying to create a javascript object that can call other methods within itself. However, I'm running into a weird problem that I just can't seem to figure out.
I have the following code
myObjectDef = function() {
this.init = function() {
//do some stuff
this.doSecondInit();
}
this.doSecondInit = function() {
//do some more stuff
}
}
myObject = new myObjectDef();
myObject.init();
I am getting an error that states "Message: Object doesn't support this property or method". And it ends at this.doSecondInit();. I can't quite figure out why it's doing this. My code runs great up to the call to the second method. How do I make this work?
There's an extra set of parenthesis here:
this.doSecondInit() = function() {
You can't assign to the result of a function call, let alone to the result of a function that doesn't even exist.
After your edit, your thing seems to work fine:
http://jsfiddle.net/nabVN/
You sure you didn't have the same typo in your actual code? Better start getting used to not putting that () after every function call, which is probably a bad habit carried over from languages where functions aren't values.
I'm working with a tabbed interface and have the following jQuery function set up to handle the click events of my tabs.
$(document).ready(function () {
$('a#foo').click(function() {
//content, various calls
return false;
});
});
The above is an example of one of my tabs, the others are also within the same document ready block. What I needed to do was make it so the currently selected tab could not be re-clicked and that in some other cases I could manually disable tabs if needed. I achieved this via the following:
$('a#play').unbind('click');
This works fine, and it certainly disables the tabs but the problem then becomes rebinding the click action that was once there. I achieved this via the bind function:
$('a#foo').bind('click', function() {
//the same content and calls as before
return false;
});
This also works fine, but it has become exceedingly cluttered as I have added tabs to my UI. The immediate solution appears to be to create the function as a variable and then pass it into the initial click creation and into the binding event. Like so:
var Foo = new function() {
//same content and calls as before
return false;
}
$('a#foo').click(Foo());
$('a#foo').bind(Foo());
This, for one reason or another, seems to be causing browser crashing issues. Is it not possible to pass a function as a var in this case or am I just doing it wrong? Alternatively, is there a better way to achieve the results I'm looking for? Thanks.
$('a#foo').click(Foo());
$('a#foo').bind(Foo());
The Foo gives you the function, but adding ()'s after it means you are calling the function instead of passing the function itself. Since you're calling the function, false ends up getting passed to click and bind, obviously not doing anything. Some of your other problems might result from the fact that you simulating switching to that tab twice (calling the event handler twice).
var Foo = function() {
//same content and calls as before
return false;
}
$('a#foo').click(Foo);
$('a#foo').bind(Foo);
^^ should do what you want.
Alternatively, is there a better way to achieve the results I'm looking for?
Currently all we really know about your design is that you are calling using a click event handler to switch tabs. That part is awesome, but we'll need more info to give you the deeper answer you really want. If you post the code inside Foo we should be able to help a bit more. :D
EDIT: credit to SLaks♦ for noticing the new in the function declaration that I missed. I'll add a little more detail to his explanation:
When you write var foo = new
function(...) { ... }, you're making a
function literal, then calling it as a
constructor.
It's equivalent to
var SomeClass = function(...) { ... };
var foo = new SomeClass;
without the SomeClass dummy variable.
The function() {} is an anonymous function as you would expect. new in javascript is a little more confusing. When you call a function and precede it with new, you are using that function to instantiate an instance of a class defined in the function. In JS, unlike most other languages, the entire definition of a class is in one constructor function, from which you set all the instance variables, like so:
Foo = function() {
this.a = "lala";
this.b = 5;
}
To make instance methods of the 'class', you use the prototype attribute. However I just realized I've gotten super off-topic. Read more on that here and here. :D
You need to remove new from the function definition and stop calling the function when using it.
When you write var foo = new function(...) { ... }, you're making a function literal, then calling it as a constructor.
It's equivalent to
var SomeClass = function(...) { ... };
var foo = new SomeClass;
without the SomeClass dummy variable.
You need to simply assign the function literal to the variable.
When you write .click(foo()), you're calling foo, and passing the result to click.
Unless foo returns a function, that's not what you want to do.
You need to pass foo itself by removing the parentheses.
So firstly, click accepts a function, but you call without the () as click runs the function when ready. By adding the () you call it straight up.
Secondly, bind takes a string (what event you are binding to) AND a function (as above)...
Use the following:
function Foo() {
//same content and calls as before
return false;
}
$('a#foo').click(Foo);
$('a#foo').bind('click', Foo);
Hope that helps :)
Try:
var foo = function() // not "new function", as this creates an object!
{
return false;
}
$("a#foo").click(foo); // not "Foo()", as you can't call an object!
As for a better way to achieve the result you're looking for, you could have a class on every tab, such as .tab. That way, you can just do:
$("a.tab").click(function() { return false; });
... without having to fluff around with a lot of ids.
Take a different approach, and do not unbind().
I assume the tabs are all in a common container. If so, just use the delegate()(docs) method to place a handler on the container.
Here's a generic code example:
$('#container').delegate('.tab:not(.selected)', 'click', function() {
$(this).addClass('selected')
.siblings('selected').removeClass('selected');
// rest of the tab code
});
This will only trigger clicks on .tab elements that do not have the .selected class. You'll need to modify for your specific code.
Adding the parenthesis calls the function, but if you wanted to make it cool and stuff, you could make it so that Foo returned the function to be bound.
function Foo(){
return function(){
//your onclick event handler here.
};
}
$('a#bar').bind(Foo())
This makes use of one on javascript's function programming aspects, closures, which is cool, but not as efficient as some of the other answers. You should do some research about closures, as they can be used to make some cool stuff.
http://www.javascriptkit.com/javatutors/closures.shtml
Consider this:
window.onload = function () {
myObj.init();
};
var myObj = {
init: function () {
console.log("init: Let's call the callMe method...");
//callMe is not defined...
callMe();
//Works fine!
this.callMe();
},
callMe: function () {
console.log('callMe');
}
};
Since the init function gets called this way (myObj.init), I expect this to be myObj in the init function. And if that is the case, why the callMe function fails? How am I supposed to call the callMe function without using the this context in the init body? (Actually, it's too annoying to call the object methods using this over and over again through the functions. So what's the point of having a single object?)
I would like to know how can I fix this so that the callMe method gets called using the first invocation in the code above?
this is never implicit in JavaScript as it is in some other languages. Although there are ways to do it, like this using the with statement:
init: function () {
console.log("init: Let's call the callMe method...");
// Make `this` implicit (SEE BELOW, not recommended)
with (this) {
// Works
callMe();
}
},
...it's generally a bad idea. Douglas Crockford probably wrote one of the better descriptions of why it's a bad idea, which you can find here. Basically, using with makes it nearly impossible to tell what the code's going to do (and slows the code down, if you do anything else in that with statement that doesn't come from the this object).
This isn't the only way that JavaScript's this is not the same as it is in some other languages. In JavaScript, this is defined entirely by how a function is called, not where the function is defined. When you do this.callMe() (or the equivalent this["callMe"](), or of course foo.callMe(), etc.), two things happen: The function reference is retrieved from the property, and the function is called in a special way to set this to be the object that property came from. If you don't call a function through a property that way, the call doesn't set any particular this value and you get the default (which is the global object; window on browsers). It's the act of making the call that sets what this is. I've explored this in depth in a couple of articles on my blog, here and here.
This (no pun) can be made even clearer if you look at JavaScript's call and apply functions, which are available on all function objects. If I do this:
callMe.call({});
...it'll call the callMe function with a blank object ({}) as this.
So basically, just get used to typing this. :-) It's still useful to have properties and methods associated with an object, even without the syntactic convenience (and confusion!) of an implicit this.
You can also use the module pattern, which captures all private variables inside a closure, so you are free to use them without this, as they're in the same scope. You then pick and choose which methods/variables you want to make public:
var myObj = (function () {
var init = function () {
callMe(); // This now works
};
var callMe = function () {
...
};
// Now choose your public methods (they can even be renamed):
return {
init: init, // Same name
callMyName: callMe // Different name
};
}) ();
Now:
myObj.init(); // Works
myObj.callMyName(); // Works
myObj.callMe(); // Error