I was writing some code with a namespace to reduce clutter when I thought that maybe I could stop polluting the namespace all together by creating an anonymous namespace. I just want to make sure that this is valid and there's no hidden gotcha's that I'm not thinking of.
Basically, the code goes like this:
new function() {
// bunch of private helper functions and variables
// ...
this.loadEventHandler = function()
{
// do load stuff
};
this.resizeEventHandler = function()
{
// do resize stuff
};
window.onload = this.loadEventHandler;
window.onresize = this.resizeEventHandler;
};
Is there anything that I'm not taking in to account? This wouldn't be taken out by a garbage collector or something, right?
What you have will work, but the more idiomatic, clean way would be to use an IIFE —Immediately Invoked Function Expression.
Your code above creates a function on the fly, and invokes it with new, which results in a new object being created. The object has a loadEventHandler and resizeEventHandler added to it, which you then add to the global object. The function then exists, releasing said object for future garbage collection.
An IIFE lets you get in, and add what you want to the global object, without cluttering it up with all your private helpers and such, and without any useless objects being created in the process.
(function() {
// bunch of private helper functions and variables
// ...
function loadEventHandler()
{
// do load stuff
};
function resizeEventHandler()
{
// do resize stuff
};
window.onload = loadEventHandler;
window.onresize = resizeEventHandler;
})();
Related
I have this class in javascript
(function() {
this.testObject = function() {
/*options*/
this.options = arguments[0];
};
/*make object*/
testObject.prototype.make = function(){
this.targetElement = document.getElementById('testDiv');
this.targetElement.addEventListener('mousedown', function(e){
...
});
this.targetElement.addEventListener('mouseup', function(e){
...
});
this.targetElement.addEventListener('mousemove', function(e){
...
});
};
}());
var test; // I need this to be global
function callObject(){
test = new testObject({...});
test.make();
}
This object binds some events. the instantiation is also inside another function. this is because I have situations that adding new elements to DOM, so calling callObject() for every new element to bind events for it.
But I think there is a performance issue here, it's going slow when I call callObject multiple times. I do not know what is the problem in fact.
so how can I delete an object and all it's binded events?
> var test; // I need this to be global
> function callObject(){
> test = new testObject({...});
> test.make();
> }
In the above, test will only reference the last instance of testObject.
The pattern you're using means that every function on the prototype chain has a closure to the execution context of the outer IIFE, and so does every listener added by the make method. That's inefficient if you don't need the closures. If not, then using an IIFE here isn't suitable, consider using a standard approach (it's convention to give constructors a name starting with a capital letter):
function TestObject() {
/*options*/
this.options = arguments[0];
}
TestObject.prototype.make = function (){
this.targetElement = document.getElementById('testDiv');
this.targetElement.addEventListener('mousedown', function (e){
...
};
this.targetElement.addEventListener('mouseup', function (e){
...
};
...
};
As noted elsewhere, adding listeners using function expressions makes it difficult to remove them later. The above pattern also means that each instance has its own copy of the function. An alternative that solves both these issues is to use references. You might add them as properties of the constructor so that they don't create additional global variables and don't need another object, e.g.
TestObject.mousedown = function (e){ ... };
TestObject.mouseup = function (e){ ... };
TestObject.prototype.make = function(){
var TO = TestObject;
this.targetElement = document.getElementById('testDiv');
this.targetElement.addEventListener('mousedown', TO.mousedown, false);
this.targetElement.addEventListener('mouseup', TO.mouseup, false);
...
};
Which avoids a lot of closures and unnecessary copies of functions and means listeners can be removed by name. And you might want to make the test global an object or array so you can keep references to all instances of TestObject, not just the last one.
There are a few things to consider. First, and most importantly, your event listeners have anonymous functions. You CAN'T unbind a listener when you give it an anon function. So go ahead and make actual functions for those. Then you can call removeEventListener the same way you called addEvent... and it will detach those listeners.
What I normally do is make a destroy function that removes all listeners and sets any global vars to null. Then you can call that destroy function whenever you need to.
I am new to JavaScript and have a question to ask. When creating a new programme is it best to create all of my functions within a global variable/object or to create them all separate.
For example this:
var register = {
members: [],
Member: function(){
},
reset: function(){
},
printMembers: function(){
},
process: function(){
}
};
function init(){
register.process();
};
window.onload = init();
Or instead like this:
var members = [];
function Member(){
};
function reset(){
};
function printMembers(){
};
function process(){
};
function init(){
process();
};
window.onload = init();
This may be a very stupid question to ask...
When creating a new programme is it best to create all of my functions within a global variable/object or to create them all separate.
This is kind of opinion based, but I'll give you a good reason for creating all your properties inside a global object: you won't pollute the namespace. That means, you won't be creating a lot of global objects, that would be properties of the global object (window, in browsers).
If you use someone else's library, and if both you and him would create global objects, the script that was included later in the HTML would override properties with the same name declared in the other script.
Finishing, I would suggest you to write your code like:
var app = {};
app.members = [];
app.Member = function(){};
app.reset = function(){};
app.printMembers = function(){};
app.process = function(){};
function init() {
app.process();
};
window.onload = init;
Also, note that you should do window.onload = init, and not window.onload = init().
The difference:
with window.onload = init, you are setting the onload property in window to be the init function. Later, when the page finishes loading, someone will ask the window to execute its onload property, and window will then do something like this.onload().
with window.onload = init(), you are executing init right there and setting the onload property of window to be the return of the execution of the init function. Since every Javascript function returns something (by default, undefined), you'll be setting window.onload = undefined. And this is not what you want.
It's a matter of taste style--kindof like writing an essay. Ultimately, what's important is that anyone (or you, six months from now) can read through your code and understand what's going on. Just focus on functionality and you will naturally start making decisions about what's logical where.
(Eloquent Javascript)[http://eloquentjavascript.net//] is also a great book on the subject.
For a particular listener in my application, I'm using the following code for scope-busting purposes:
// this is all in a prototype of MyClass
var self = this;
myElement.addEventListener("stuff", function(e){self.doStuff(e)});
That will get doStuff to have the desired this binding.
The problem shows up when I try to removeEventListener. I suppose it's because the native function signatures must be different?
// in a different prototype of MyClass
var self = this;
myElement.removeEventListener("stuff", function(e){self.doStuff(e)}); // doesn't work
If I make a separate function that contains all of my scope-busting code, then the this binding in that code will be to the unwanted object of myElement. So the question is: How can I force listener scope and still be able to remove an added event listener?
*note using global / static variables in any way is prohibited due to the nature of the project (otherwise this would be simple!)
This has nothing to do with scope or the way in which you're storing a reference to this. The problem is that removeEventListener expects a reference to a function that's previously been registered as a listener, but you're giving it a brand new function it's never seen before.
You need to do something like this:
var self = this;
var listener = function(e){self.doStuff(e)}
myElement.addEventListener("stuff", listener);
// later
myElement.removeEventListener("stuff", listener);
It doesn't matter that the bodies of your two functions are the same; they're still different functions.
See:
https://developer.mozilla.org/en/DOM/element.removeEventListener
Inline anonymous functions are a very bad practice anyway, so I will suggest the obvious:
function MyClass() {
this.onStuff = this.onStuff.bind(this); //Each instance steals the prototyped function and adds a bound version as their ownProperty
}
MyClass.prototype = {
onStuff: function (e) { //Prototyped, no instance actually uses this very function
this.dostuff()
},
bind: function () {
myElement.addEventListener("stuff", this.onStuff);
},
unbind: function () {
myElement.removeEventListener("stuff", this.onStuff);
}
}
see removeEventListener on anonymous functions in JavaScript
You can't removeEventListener as your using an anonymous function.
I'm using the FB.Event.subscribe() observer model to find out when a user logs in. This method takes two arguments, a string containing the thing to watch, and callback function.
I'm following several events that handle the event the same way, so I've set up the callback function as a pre defined method and passed this to FB.Event.subscribe() like this:
Controller.prototype.go = function() {
FB.Event.subscribe('auth.login', this.fbHandleStatusChange);
FB.Event.subscribe('auth.logout', this.fbHandleStatusChange);
}
Controller.prototype.fbHandleStatusChange = function(response) {
// Doesn't work
this.otherFunction();
}
Controller.prototype.otherFunction = function() {
alert('hello');
}
Unfortunately this means that I loose access to 'this' within the scope of fbHandleStatusChange, obviously I don't want to start coding references to concrete versions of Controller!
I'm guessing I'm passing the function incorrectly?
Thanks.
In JavaScript, this is defined entirely by how a function is called, not where it's defined. This is different than some other languages. (JavaScript doesn't have methods, it just has functions and some syntactic sugar that makes them look like methods sometimes.) So although you're passing in your function correctly, Facebook doesn't know about your object instance and can't set this correctly when calling your function.
Check the FB.Event.subscribe docs to see if it offers a way to say what "context" to use to call the event handler function. It may offer a way to do that. (This will usually be a context or thisArg parameter.)
If not, you can readily solve the problem with a closure:
Controller.prototype.go = function() {
var self = this;
FB.Event.subscribe('auth.login', handleChange);
FB.Event.subscribe('auth.logout', handleChange);
function handleChange() {
return self.fbHandleStatusChange();
}
}
That grabs a copy of this into a variable called self, which is used by the handleChange function (which is a closure over the scope containing the self variable) to call your function with the correct context. More about closures here: Closures are not complicated More about this here: You must remember this
Alternately, though, are you really going to have multiple instances of Controller? People coming to JavaScript from class-based languages tend to use constructor functions (a rough "class" analogue) unnecessarily. They're the right choice if you need to have more than one instance of an object, but if you're only ever going to have a single Controller object on the page, then using a constructor function and fiddling about with this is overkill.
If you don't need multiple, independent Controller instances, then:
var controllerObject = (function() {
var inst = {};
inst.go = go; // Make `go` a publicly-accessible function of the object
function go() {
FB.Event.subscribe('auth.login', fbHandleStatusChange);
FB.Event.subscribe('auth.logout', fbHandleStatusChange);
}
// This is private to us, so we don't expose it as a property on the object
function fbHandleStatusChange(response) {
// Doesn't work
otherFunction();
}
// This is also private to us
function otherFunction() {
alert('hello');
}
return inst;
})();
That creates a private scope via the outer anonymous function, and within that scope creates an instance (inst) which we then return and refer to as controllerObject. controllerObject in the above only has one property, the function go. All of our other functions are truly private. (I've also taken the liberty of ensuring that the functions have names, because that helps your tools help you.)
Note that we don't actually refer to inst anywhere in our function calls, because they're all local to the closure scope. We can even have private data, by having other vars within the outer closure.
If I want to give a JavaScript variable global scope I can easily do this:
var myVar;
function functionA() {
myVar = something;
}
Is there a similarly simple and clean way -- without creating an object -- to separate the "declaring" and the "defining" of a nested function? Something like:
function functionB; // declared with global scope
function functionA() {
functionB() { // nested, would have local scope if declared here
CODE;
}
}
I should clarify that I'm referring to the scope of the function name itself -- so that if it is in an iframe it can be called from a script in the parent document. (Nothing to do with scope of variables inside nested functions.)
You can create global variables and functions by creating instances on the window object:
function _A()
{
// scoped function
function localFunctionInTheScopeOf_A()
{
}
// global function
window.globalFunctionOutsideTheScopeOf_A = function ()
{
};
}
In your case, though, all you need to do is this:
var myFn; // global scope function declaration
function randomFn()
{
myFn = function () // global scope function definition
{
};
}
Note: It is never a good idea to clog up the global scope. If you can; I'd recommend that you re-think how your code works, and try to encapsulate your code.
Perhaps I'm misunderstanding the question, but it sounds like you want something like this:
var innerFunc;
function outerFunc() {
var foo = "bar";
innerFunc = function() {
alert(foo);
};
}
You cannot globalize variables/functions cross windows/iframes that way. Each window/iframe has it's own global scope and to target variables/functions in another window/iframe, you need explicit accessor code and conform to the same origin policy. Only variables/functions inside the windows/iframes global scope are accessible.
code in top window.
var iframe = document.getElementById('iframeId');
var iframeContext = iframe.contentWindow || iframe;
// this will only work if your iframe has completed loading
iframeContext.yourFunction();
You could also possibly define functions/variables in the top window instead and simply work in one scope by binding the stuff you need from the iframe through a closure. Again, assuming you meet the same origin policy. This will not work cross domain.
code in iframe.
var doc = document;
var context = this;
top.myFunction = function(){
// do stuff with doc and context.
}
It is also important to note, that you need to check if your iframe content and it's scripts are fully loaded. Your top page/window will inadvertidly be done and running before your iframe content is done, ergo variables/functions might not be declared yet.
As for exposing a private function, others have awnsered this, but copy/pasting for completeness.
var fnB;
var fnA = function(){
var msg = "hello nurse!";
fnB = function(){
alert(msg);
}
}
I have the habbit of declaring stand alone functions as variables (function expression) and only use function statements to signify constructors/pseudo-classes. It also avoids a few possible embarrasing mistakes.. In any case, fnB resides in the global scope of the iframe and is available to the top window.
Why exactly you want this beats me, seems it makes matters more complicated to debug or update a few months later.
You can kind of do what you want.
You can create a function that acts like a namespace for properties and methods, and then you could essentially call either...
functionB();
or
functionA.functionB();
There is an article on how to do it here:
http://www.stevefenton.co.uk/Content/Blog/Date/201002/Blog/JavaScript-Name-Spacing/
In response to the update...
Is the iframe on the same domain as the parent site? You can't call JavaScript across the domain boundary, which may explain the problem.