I am trying to localize everything to a namespace in javascript. So I have objects that follow a naming convention like:
myapp.utilities.file.spinner
etc...
My question is, is there a way to avoid repeating that big string everytime I want to augment the object with a property or a method. Currently my code looks like this...
myapp.utilities.file.spinner.method1 = function() { };
myapp.utilities.file.spinner.method2 = function() { };
etc.
Something like this...
spinnerPath.method1 = function()
...where spinnerPath stands for myapp.utilities.file.spinner, would be nicer. But from my understanding I cannot just say
spinnerPath = myapp.utilities.file.spinner
as that will create another object in the global space.
Thanks
The code you're using won't actually create a new object, merely a new global variable referring to the existing object. It will pollute the global namespace however, so if you're looking to avoid that, you have several options:
You can use with, but don't because it will probably cause you more heartache than it's worth.
You can make a shorthand pointer variable inside each function outside of the global namespace: var s = myapp.utilities.file.spinner;, but this is annoying.
(Probably the best option) create a "private namespace" using an immediate-call function:
(function (S)
{
S.method1 = function(){/*whatever*/};
S.method2 = function(){/*whatever*/};
})(myapp.utilities.file.spinner)
Try this:
(function(){
var spinner = myapp.utilities.file.spinner;
spinner.method1 = function(){};
})();
myapp.utilities.file.spinner.method1 = function() { };
myapp.utilities.file.spinner.method2 = function() { };
...
// Somewhere else in your code, create a temp local called "spinner"
// that references your longer path object.
var spinner = myapp.utilities.file.spinner;
spinner.method1();
You can just make a temporary local variable wrapped in an anonymous function:
(function(){
var spinnerPath = myapp.utilities.file.spinner;
spinnerPath.method1 = function() { };
spinnerPath.method2 = function() { };
spinnerPath.method1();
})();
Here, spinnerPath is in fact a local reference to the global myapp.utilities.file.spinner object, not a copy. Objects in JavaScript are references, so if you create a local variable that points to it, you will not create a copy or pollute the global namespace.
Related
I've got 3 codes :
var control = new Control();
function Control() {
this.doSomethingElse = function() {...}
this.doSomething = function () {
control.doSomethingElse();
}
}
Or
var control = new Control();
function Control() {
var self = this;
this.doSomethingElse = function() {...}
this.doSomething = function () {
self.doSomethingElse();
}
}
Or
var control = Control();
function Control() {
var self = this;
this.doSomethingElse = function() {...}
this.doSomething = function () {
self.doSomethingElse();
}
return self;
}
Important : The function is a controller, and just declared once. Then I'm using "control" everywhere in my code...
I was wondering if the control.doSomethingElse() was slow ?
In the end, what is the right thing to do and/or the fastest code in those exemple ?
Thanks !
The first is wrong - an object should never internally use the variable name by which it is known outside. Other code could change that variable to point to something else, breaking this code.
The third is also wrong - when calling Control() without new the assignments to this.foo inside will end up getting attached to the global object (except in strict mode, where there's no implicit this on bare function calls, so the assignment to this.doSomethingElse tries to attach to undefined, causing a runtime error).
That only leaves the second as appropriate, but ultimately it's a question of correctness, not performance.
Do not define methods in constructor - that means defining them every time an instance is created. Use Control.prototype.foo = function() {} instead. Also you do not need to return this if you're using new operator - that's the whole point of new operator.
The recommended approach is this:
function MyClass(param1) {
// Here we're changing the specific instance of an object
this.property1 = param1;
}
// Prototype will be shared with all instances of the object
// any modifications to prototype WILL be shared by all instances
MyClass.prototype.printProperty1 = function() {
console.log(this.property1);
}
var instance = new MyClass("Hello world!");
instance.printProperty1(); // Prints hello world
To understand this code, you need to understand javascript's prototype-based inheritance model. When you create instance of MyClass, you get a new object that inherits any properties present in MyClass.prototype. Read more about it.
Also I wonder:
The function is a controller, and just declared once.
If you're not using this multiple times, you don't need to create something like class. You can do this instead:
var control = {doSomething:function() { ... }};
I assume you are used to Java, where everything must be a class, whether it makes sense or not. Javascript is different, you can also make single objects or functions as you need.
I have a function on a prototype like this:
Car.prototype.drive = function() {
this.currentSpeed = this.speed;
}
I want to call this function very often in another function which is also part of the prototype Car. And because I'm lazy I don't want to rewrite the this all the time. So I would like to copy a reference to the function to a local variable:
Car.prototype.doSomeThing = function() {
var driveReference = this.drive;
driveReference();
}
But when I call driveReference(), the this-pointer of driveReference() points to Window instead to my instance of Car.
Is there any possibility to prevent that?
(apply() would work, but that is even more verbose then using this)
You can use Function.prototype.bind to bind the context of a function to whatever you like:
Car.prototype.doSomeThing = function() {
var driveReference = this.drive.bind(this);
driveReference();
}
You could write
var driveRef = this.drive.bind(this);
but that can have some possibly unwanted performance impact. Or you could just copy this to a shorter variable name:
var me = this;
me.drive();
Explicitly using a reference to the context object is a pretty basic design feature of JavaScript, so it's hard to get around it.
So I'm a fairly decent javascript programmer and I've just recently finished working on a fairly big web application that involved writing quite a bit of javascript. One of the things I can across when I was debugging my script was that there were some namespace conflicts with my various global variables I used throughout my script. Essentially, my javascript file was structured as such:
global var a
global var b
global var c
function1(){}
function2(){}
function3(){}
with a jQuery document on-ready function to bind various events to buttons in my html and call my functions as event handler callbacks.
Some people recommended encapsulating my entire script in one gigantic function to prevent any scope-related errors. I couldn't quite figure out exactly what that would entail. Any tips are appreciated as I am about to create another web app that will involve quite a bit of AJAX page loads to avoid browser refreshes and DOM manipulation bound to various events. Thanks!
I recommend reading the jQuery plugin authoring guide (I also recommend you consider using jQuery if you are not)
http://docs.jquery.com/Plugins/Authoring
BTW this been asked many times (not a criticism for re-asking)
jQuery: Global Variable Namespace Problem
Avoiding polluting the global namespace with javascript dependencies
JavaScript Namespace
I also highly recommend you read about jQuery live plugin for register DOM events(I guess its built-in now):
http://api.jquery.com/live/
(this will minimize the nasty need for state management of unbinding and rebinding your DOM nodes).
A similar alternative to Michael's and nnnnnn's version is to do
var YourApp = {
a: 1234,
b: 5678,
function1: function () {
},
etc
};
YourApp is the only global var and its properties can be accessed like
YourApp.function1();
or
YourApp.a;
I like wrapping the contents of each file inside an anonymous function. You can then pass window to this as a parameter and selectively choose what to export from each file.
(function(exports) {
var MyClass = function() {
};
MyClass.prototype.method = function() {
};
// this won't be visible outside this file
var helperFunction = function() {
};
exports.module = exports.module || {};
exports.module.MyClass = MyClass;
})(window);
Also, you can structure it in the following way to use this as the global object instead, if that appeals more to your coding style:
(function() {
this.Thing = function() { };
}).call(window);
I expect to get downvoted from OO purists, but...
A very simple solution to the namespace collisions is to place your variables and functions into a class, even if it doesn't have a working constructor or perform any internal processing of its own.
function YourApp() {} // empty constructor...
YourApp.a = 1234;
YourApp.b = 5678;
YourApp.function1 = function() {};
YourApp.function2 = function() {};
function YourOtherApp() {} // empty constructor...
YourOtherApp.a = 1234;
YourOtherApp.b = 5678;
YourOtherApp.function1 = function() {};
YourOtherApp.function2 = function() {};
// Then you call it like:
YourApp.function1();
// And you have no more namespace collisions with other globals
The quickest first step based on what you have done in the past with lots of global variables and functions is to simply take all of those and make them properties of a single object. That single object is declared as a global variable, but it is your only global variable and is effectively your new namespace and thus you only have to worry about one name potentially clashing with other libraries.
So relating that directly to the example you gave with a, b, etc:
var SNS = {}; // create some namespace object
SNS.a = "something";
SNS.b = "something else";
SNS.c = 17;
SNS.method1 = function(x) {
alert(SNS.a + x);
};
SNS.method2 = function() {
SSN.method1(12); // call another function
};
SNS.SUB = {};
SNS.SUB.property1 = "sub namespace prop 1";
SNS.SUB.method1 = function() {};
// etc.
My example uses 'SNS' for 'some namespace'; I'm sure you can immediately see how that would be pretty easy to apply to the project you just finished. You can probably also see the disadvantage that for your methods to refer to each other and to your variables you have to prefix them all with the name of your object. If you have sub namespaces that gets worse. Fortunately there are ways around that, but I'm declaring them outside the scope of this answer.
Having said all that, something for you to read up on (Google) is the "revealing module pattern" - will help you go a bit more OO (if that's what you want).
A really in-depth answer to your question can be found here: http://enterprisejquery.com/2010/10/how-good-c-habits-can-encourage-bad-javascript-habits-part-1/
Further reading:
http://www.adequatelygood.com/2010/3/JavaScript-Module-Pattern-In-Depth
I have two objects defined something like this (simplified for sake of the question):
var firstObject = function(){ };
firstObject.prototype.doSomethingFirstObjectsDo();
var secondObject = function(){ };
secondObject.prototype.doSomethingSecondObjectsDo();
Next I have an Object Manager which works as a sort of interface for my main application to create objects:
var ObjectManager = function()
{
this.create = {
FIRST:firstObject,
SECOND:secondObject
};
};
ObjectManager.prototype.createObject = function(type)
{
return new this.create[type]();
};
Finally an example of the main application using the Object Manager to Dynamically Create Either firstObjects or secondObjects:
var MainApplication = function(options)
{
this.objectTypes = options.objectTypes;
this.objManager = new ObjectManager();
};
MainApplication.prototype.createObjects = function()
{
//Iterate through all the types this application needs to create
for (var type in this.objectTypes)
{
var dynamicallyCreatedObject = this.objManager.createObject(type);
//Do Something Else
}
};
This approach works great, but has one disadvantage that I can see - being that you need to formally define the name of the Constructor Function for each Object "Type" that could be created.
In the event that I wanted to create a "thirdObject" - which would be already formally defined - I would also need to go back and add a reference to the "thirdObject"'s constructor function in the ObjectManager.
Ideally, I would like to remove the need for an "ObjectManager" and simply be able to dynamically call the constructor method with the "new" keyword like this:
//Inside MainApplication
for (var type in this.objectTypes)
{
var dynamicallyCreateObject = new [type](); //Invalid Syntax
};
Anybody have any thoughts on a better way to handle dynamically creating different objects in JavaScript using the "new" keyword?
Responding to Some Initial Comments:
I should have mentioned that the entire application is enclosed within an anonymous function.
(function(){
//All of My Mentioned Code is Found Here
$(document).ready(function(){
mainApp = window.mainApp = new MainApplication(options);
});
});
#casablanca: From what you are saying I believe I'll need to actually define a NameSpace inside the entire anonymous function, since once it finishes I have no real way to directly refer to that scope again. I think I know what I need to do now, I was kind of hoping there was another way to work with that "new" keyword - but it doesn't seem like that is the case.
This:
var dynamicallyCreateObject = new [type]();
is almost correct, except you need an outer object to access properties. In the case that your constructors are global, you can use window:
var dynamicallyCreateObject = new window[type]();
Ideally, they should be within your own namespace, in which case you can do something similar:
var dynamicallyCreateObject = new MyNamespace[type]();
Assuming I understand your desire (and I'm not sure that I do) you can use the global window object in DOM 0 browsers, or create your own reference to the global scope, and use that to look up variables defined locally.
var $global = this;
var firstObject = function(){};
var objName = "firstObject";
var instance = new $global[objName];
I'm currently using JavaScript and jQuery.
I have an function which executes once the document is ready, and inside that I am creating objects which contain various attributes.
Within the same function, I can access these new object's attributes no problem, however once I'm inside a different function I can't seem to reference them properly and therefore cannot access the objects or the information inside them.
What's the correct way to reference the attributes of an object which was created in a different function to the one looking for the information?
In general you can't reference an object that was created in a different function. The scoping rules do not allow that.
However, if you create your sub functions inside of the main JQuery ready function, you can create your objects as local variables to the ready function and use them in your other functions.
This would create a closure allowing the variables to still exist yet not be in the global scope.
Something like this:
$(function () {
var MyObj = {"CT":0};
function Inc(){
MyObj.Ct++;
}
$("INPUT[type=button]").click(Inc);
})
Create the a global refernence to the object outside of the function, e.g.:
var obj;
function func1()
{
obj = 1;
}
function func2()
{
alert(obj);
}
The best way would be to create a global object which holds all that data.
var myGlobalData = function(){
// private data
var some = 'data',
foo = 'bar';
// public
return {
publicfunction: function(){
alert(foo);
return(some);
}
}
};
within your ready code you can create an instance of that
$(document).ready(function(){
var myData = new myGlobalData();
myData.publicfunction();
});
Note that in this example, you can't access 'some' and 'foo' with
myData.foo = 'bar'; // not possible
because those are 'private' to the object. If you write an object in that manner, you
can simulate a more OOP style.
I have an function which executes once
the document is ready, and inside that
I am creating objects which contain
various attributes.
Define these objects as properties of the global object (window) and you'll be fine.
function onready() {
window.myObject = {};
}
function otherFunction() {
window.myObject.fu = "bar";
}