I generally write code that looks like this (but with many more handlers).
$(document).ready(function() {
$("#next").click(function() {
doStuff();
});
$("#prev").click(function() {
doSomeOtherStuff();
});
$("#link").hover(function() {
doSomeTotallyOtherStuff();
});
});
Is this the best way of doing this? Should I do it differently? Paul Irish's presentation suggests it's a bad idea. Is that true?
We like to use the object literal pattern and named functions. I'd rewrite your example like this:
$(function() {
Example.somegrouping.init();
});
var Example.somegrouping = {
init: function() {
// set up listeners
$("#next").click(this.onNextClick);
$("#prev").click(this.onPrevClick);
$("#link").hover(this.onLinkHover);
},
onNextClick: function() {
// do some stuff
},
onPrevClick: function() {
// do some stuff
},
onLinkHover: function() {
// do some stuff
}
};
Why? Well, it makes it easier to reuse event handlers in other places without resorting to triggers. The naming of the function can help self-document your code. Testing/debugging is easier. The object literal only adds one entry to the global namespace, so there is little chance for collisions with other scripts your page might be using.
One reason that it's useful to define your functions the old boring way is that you get names to look at in stack traces.
$(function() {
function nextClickHandler() { ... };
$('#next-button').click(nextClickHandler);
});
It's not safe to use a function name in a function expression:
$('#next-button').click(function nextClickHandler() { // DO NOT DO THIS
});
which is sort-of unfortunate but there you go.
This should be fine as long you don't do it in a loop or some other code that runs more than once (i.e. a recurring function call). Otherwise there's not much difference between one anon function and one named function. it's when you have 100 identical anonymous functions that's a problem.
ex:
$("#someID").click(function(){})
is okay,
for (var i in somelistofIDs) {
$(i).click(function(){})
}
is not, because you've created 100 anonymous functions instead of one named function.
EDIT
of course, if you're wrapping a single function call in a closure, you're doing it wrong, you can just pass the function itself.
Related
I have the following code in JS:
if (Subs.Lsr!==null) {
Subs.Measure.Do("markLSRPosts",function() {
Subs.Lsr.Dom($ce);
});
}
Because this kind of code is inside my file multiple times I wanted to create a function for that:
function SingleMeasure(measureTitle, functionName) {
if (setting!==null) {
Subs.Measure.Do(measureTitle,function() {
functionName();
});
}
}
SingleMeasure(Subs.Lsr, "markLSRPosts", Subs.Lsr.Dom($ce));
Now my problem is that the function Subs.Lsr.Dom($ce) is called BEFORE my SingleMeasure()-Function is called (and so always, no matter what the condition of setting is).
I have to admit: This makes sense to me from coding logic. Nonetheless I wonder if there is another way to achieve what I want. (the method ...Dom() only being called when the setting-condition is met)
Now my problem is that the function Subs.Lsr.Dom($ce) is called BEFORE my SingleMeasure()
That's because you are calling it right here: Subs.Lsr.Dom($ce)
SingleMeasure(Subs.Lsr, "markLSRPosts", Subs.Lsr.Dom($ce));
You might pass it as an anonymous function:
SingleMeasure(Subs.Lsr, "markLSRPosts", function() {
Subs.Lsr.Dom($ce);
});
The callback will be invoked by your SingleMeasure function at a later stage.
I'm frequently using this structure:
var example = (function () {
function privateFn2 () {
...
}
function privateFn1 () {
...
}
return {
publicMethod1: function () {...
},
publicMethod2: function () {...
}
};
}());
What I want to know is this: If privateFn1 is the only function/method that calls privateFn2, is it regarded as better practice to set it up as follows?
EDITED for clarity
var example = (function () {
function privateFn1() {
function privateFn2() {
}
...
privateFn2();
}
return {
publicMethod1: function () {...
},
publicMethod2: function () {...
}
};
}());
This is a wildly simplified example, of course. The issue is that I have lots of private functions, and I'm wondering whether nesting is well- or poorly-regarded. I recognise it is quite possibly a matter of preference, but any advice will be gratefully received.
Thanks.
It depends on the situation. If you have several private functions that are only relavent to yet another private function, then perhaps this is a situation where the object or class is packaging more functionality then it should be.
Here are some questions to ask:
Do these private functions have side effects? That is, are they
manipulating any closed over properties? If not, is this some generalized
logic that could be implemented statically and included
separately? Or, are these private functions manipulating a subset of
properties that could be moved to another class or object?
Are the private functions simple task specific helper functions to be used within
a larger algorithm or control function; such as a filter function for sorting an
array, or some other kind of iteration callback? If so, then it may be in-fact
cleaner to nest those functions inside and keep them out of scope of the main
object. Will any other code need these functions?
How many times will the main private function be called? If it will be called very
frequently (inside a loop or on a timer interval) then nesting the
private functions inside could incur a measurable overhead --
which would have otherwise been negligible had the private function
been called only occasionally.
There are always trade-offs to consider, thinking about these questions will help you decide what is best for your particular situation.
I would avoid your second example. Every time privateFn1 gets called, it redefines privateFn2. Why not just do it once? You might even need to use it somewhere else later on.
However, if you really want to hide privateFn2, a better solution is this:
var privateFn1 = (function () {
function privateFn2() {
// ...
}
return function () {
privateFn2();
// ...
};
})();
This is common task coming from object oriented programming, I would like to change behavior of JavaScript program by overriding existing function with possible calling it as well. I remember Windows introduced that as writing hooks and chaining them. So what I want, I have a web page which calls some onload hook which finally calls function initFields. I want to redefine this function however keep previous implementation. If I simply define my JS function as
function initFields() {
// do some stuff ...
// I do not know how to call super.initFields() here
}
I read something like you can write
initFields.prototype = function() {
// do some stuff ...
// but still have no idea how to call the original one
};
Can somebody help?
One option is
var initFieldsInitial = initFields;
function initFields() {
// your stuff
initFieldsInitial.apply(this, arguments);
}
You could try the wrap() function from the underscore.js library.
http://documentcloud.github.com/underscore/#wrap
var initFields = function() {
// do something
console.log('initFields');
}
initFields = _.wrap(initFields, function(initial) {
// do some stuff
console.log('wrapper');
initial();
});
$(document).ready(function() {
initFields();
});
Trying to use an svg onClick to call a prototype function.
Usually to call a prototype function I would just do this.(functionName) but when I put it into the .setAttribute(onclick, "this.(functionName)") it does not recognise the prototype function. Has anyone had any experience in this?
In case the above wasn't clear heres the basic jist of it...
function myobject(svgShape) {
this.svgshape.setAttribute(onclick, 'this.doSomething()');
}
myobject.prototype.doSomething = function() {
alert("works");
}
Three things that may help:
1) First off, I think you're missing this line from the top of your myobject function:
this.svgshape = svgshape;
I'm assuming that was just an error posting the question and have inserted that below.
2) Normally when you're using Prototype (or any modern library), you don't use strings for callbacks, you use functions. Also, you normally assign handlers using the library's wrapper for addEventListener / attachEvent (observe, in Prototype's case) rather than the old DOM0 attribute thing. So:
function myobject(svgShape) {
this.svgshape = svgshape;
$(this.svgshape).observe('click', this.doSomething); // STILL WRONG, see below
}
myobject.prototype.doSomething = function() {
alert("works");
}
3) But JavaScript doesn't have methods (it doesn't really need them), it just has functions, so the above won't ensure that this (the context of the call) is set correctly. With Prototype you'd use bind to set the context:
function myobject(svgShape) {
this.svgshape = svgshape;
$(this.svgshape).observe('click', this.doSomething.bind(this));
}
myobject.prototype.doSomething = function() {
alert("works");
}
(Or you can use your own closure to do it. The advantage of bind is that the closure is in a very well-controlled environment and so doesn't close over things you don't want kept around.)
Now, I've never done any SVG programming with Prototype, so if observe doesn't work for some reason, you might try directly assigning to the onclick reflected property:
function myobject(svgShape) {
this.svgshape = svgshape;
this.svgshape.onclick = this.doSomething.bind(this);
}
myobject.prototype.doSomething = function() {
alert("works");
}
I'm still using bind there so that this has the correct value.
These posts from my anemic little blog offer more discussion of the above:
Mythical methods
You must remember this
Closures are not complicated
I like to organize my javascript in namespace style like below. What I want to know : is there another (shorter?) way to call myFirstFunction() from mySecondFunction()? I tried this.myFirstFunction() and it's not working so maybe there's some kind of mysterious trick here that I don't know.
var myNameSpace = {
myFirstFunction: function(){
alert("Hello World!");
},
mySecondFunction: function(){
myNameSpace.myFirstFunction();
}
}
Thanks for your help as usual, people of SO! :)
As written in your example code, this.myFirstFunction() would work. Your code is likely simplified to illustrate your problem, so it would probably help to see the actual code to tell why it doesn't work with this.
One possible reason that it fails would be if the code where you call this.myFirstFunction() is inside a closure. If so, this would be a reference to the closing function, not your namespace and would therefore fail. See here for a contrived example based on your code to see what I mean. Again, having a look at the actual code would probably be helpful to diagnose what's going on.
Your suggestion to use 'this' should work. i.e.:
var myNameSpace = {
myFirstFunction: function(){
alert("Hello World!");
},
mySecondFunction: function(){
this.myFirstFunction();
}
}
Result:
myNameSpace.mySecondFunction() // "Hello World!".
If you want it to be shorter maybe you should consider the following pattern:
Javascript Design Pattern Suggestion
basically for your example:
var myNameSpace = (function()
{
function _myFirstFunction(){
alert("Hello World!");
}
function _mySecondFunction(){
_myFirstFunction();
}
return {
MyFirstFunction : _myFirstFunction,
MySecondFunction : _mySecondFunction
};
})();
I find this to be the cleanest pattern, also providing "private/public" variables in javascript that's otherwise pretty much impossible
In some cases the this keyword should work fine. If you explicitly call myNameSpace.mySecondFunction() then this.myFirstFunction() will execute as intended.
If you are using myNameSpace.mySecondFunction as an event handler it likely will not. In the case of an event handler you would need some way to refer to the namespace you want to use. A lot of JavaScript frameworks provide a way to define what the this keyword refers to. For example, in MooTools you can do myNameSpace.mySecondFunction.bind(myNameSpace) which will cause this to refer to myNameSpace inside mySecondFunction. If you are not using a framework you could make your event handler an anonymous function like:
document.getElementById('myId').addEventListener('click', function(e) {
myNameSpace.mySecondFunction.call(myNameSpace);
});
For more information on the call method I would refer to the MDC page for the call function or you could use apply which behaves similarly to call but passing an array of arguments for the second paramter rather than having a varargs like approach for additional parameters.
All of these suggestions are predicated on defining your namespace as #Harnish suggested:
var myNameSpace = {
myFirstFunction: function(){
alert("Hello World!");
},
mySecondFunction: function(){
this.myFirstFunction();
}
}
For more information about JavaScript function binding I'd highly suggest reading Justin's article on Function scope and binding in JavaScript
If you are attaching to event:
possible issue could be if you are attaching Namespace's function to event, like:
$(el).on("click", nameSpace.myFunc);
....
nameSpace = {
myFunc: function(){
this.anotherFunc();
}
}
that will throw error.
Solution 1
You may change this.anotherFunc() with nameSpace.anotherFunc()
Solution 2
You might change
$(el).on("click", nameSpace.myFunc);
// to ----->
$(el).on("click", function(){ nameSpace.myFunc(); } );