I have been writing a lot of javascript functions and event listeners and I want to move them into their own namespaced, isolated place that doesn't conflict when I concatenate and minify it with my other javascript files.
I am still new to javascript, so there may be simple solution to this answer. I started by creating a javascript object:
var MySpecialStuff = {
init: function(){
//do everything here
}
};
Then in my html, on the page I want to use it on I can initialize this code:
<script>
MySpecialStuff.init();
</script>
But then the init method started growing and I need to start breaking that code into smaller chunks, but I am stuck on the syntax and how to set private methods/variables and call them from within the init method and other private methods. How can I do this?
Am I headed in the right direction? What other ways can I / should I go about doing this sort of thing?
You are headed in the right direction. You can use the module pattern to create an object with private members and methods like this:
var foo = function(){
var bar = ""; //private
function funk(){
//private function
return "stuff";
}
return{
getBar: function(){
return bar;
},
init: function(){
alert(funk());
},
randomMember: 0 //public member
}
}();
Only what's in the return block is public, but you can call any private methods/access any private methods from within the return block.
Thanks to Joseph for linking to another SO question which explained this approach:
Another way to do it, which I consider to be a little bit less restrictive than the object literal form:
var MySpecialStuff = new function() {
var privateFunction = function() {
};
this.init = function() {
};
};
I like to further segment my code into a more modular approach. So, let's say we have a page that has a list of blog posts, a page menu, and a sidebar. I'd end up with this:
var blog_controller = {
init: function(){
document.getElementById('some_element').addEvent('click', this.handleSomeClick);
this.applySomePlugin();
},
applySomePlugin: function () {
/* do whatever needs to happen */
},
handleSomeClick: function () {
// scope will be element
this.style.color = 'red';
}
};
var page_menu_controller = {
init: function(){
document.getElementById('some_other_element').addEvent('click', this.handleSomeClick);
},
handleSomeClick: function () {
// scope will be element
this.style.color = 'blue';
}
};
... and so on. This keeps code organized by purpose, helps keep scope clear, and allows you to reuse elements of code that might occur frequently (for instance, if you AJAX'd in a new blog post, you could call this.applySomePlugin again).
This of course is a quick-and-dirty example, but I hope you get the idea
Divide responsibility of code you have put inside the init function into subobjects of the main object.
MySpecialStuff = {
// Variables/constants and everything else that needs to be accessed inside the whole MySpecialStuff object
init: function(){
//Do only whats required to be globally initiated.
this.MainMenu.init(); // Main menu stuff usually is.
}
};
MySpecialStuff.MainMenu = {
// Variables / constants that are only important to the MainMenu subobject.
init: function(){
//Just the initiation stuff thats related to the MainMenu subobject.
}
};
MySpecialStuff.SlideShow = {
// Variables / constants that are only important to the SlideShow subobject.
init: function(){
// Same as for the MainMenu with the difference that nonessential objects can be "fired" when required
}
};
Related
i was wondering how to use functions within the Backbone.View code. Can someone advise/show me how this is done properly. I understand that extending is only put into the var. I've looked at extend, prototype, super, parent, baseview and other fancy stuff. But that only confused me even more ;).
var jsHelpers = {
intVar: 300,
sum: function(a, b, callback) {
// do something interesting:
c = a + b;
d = c + intVar;
callback(c);
} //end sum function
} //end jsHelpers
/* somewhere else */
ViewThingy = Backbone.View.extend({
initialize: function() {
this.render();
},
render: function() {
var result = jsHelpers.sum(1, 1, function(callbackData) {
//let's do something with the return stuff:
this.$el.html(callbackData);
}); //end jsHelpers
} // end render
}); //end extend
The error is of course that the function jsHelpers.sum(); is not available in the extend.
TIA ! Vince
var View = Backbone.View.extend({
hello: function() {
console.log('hello');
},
// You can also override Backbone methods here.
initialize: function() {
// Do init code shared by all your views
}
});
// All the views in your app should extend off this view instead of Backbone.View.
var RandomView = View.extend({
initialize: function() {
// Call the parent to do init code.
View.prototype.initialize.apply(this, arguments);
this.hello();
},
// You can override methods too..
hello: function() {
// You can call the parent.
View.prototype.hello.apply(this, arguments);
}
});
Actually it is a good idea to always extend View, Model, Collection and Router when you make an app as there will always be shared functionality you want to do to not repeat the same code everywhere in your app. Typically for a view this would be stuff like the render routine such as rendering the template and rendering sub views - you wouldn't want to do that logic again in every view in your app.
Typically to share other code you would use a dependency manager like RequireJS or Browserify. But you can also have a single global object:
window.app = {};
and attach things to it:
window.app.utils = ....;
That is accessible from anywhere. Having an app object is pretty common - e.g. you would often have a Model that maintained the state of the app at app.state.
You can hook your helpers to some global namespace or make them global.
window.jsHelpers = {...}
Second way :
jsHelpers = {..} //Remove the var, it'll make jsHelpers a global variable.
I use the first one, for similar purposes.
I am trying to create an api that extends some functionality of Tizen.
Tizen has a way of creating objects such as: 'new tizen.ContactName(...)' and 'addressbook = tizen.contact.getDefaultAddressBook();'.
This seems to be a nice way to group together methods and objects when there are a lot of them.
So, for example I want to extend the contact handling:
(An external js-file)
function ContactManager(){ //edited by comment
var self = this;
this.add = function(details, posCallback, negCallback){
//do stuff to add contact
this.otherMethod(){...}
}
etc.
I can call this by using: var contactManager = new ContactManager(); and it works fine.
Now I want to access by include it in another object(?) so that it looks like: var contactManager = new myTizen.ContactManager().
I tried:
function myTizen(){
this.ContactManager = function(){
//methods and stuff
}
}
This doesn't work. Why? How should I build my "API"?
I see it like this
define some object myTizen
then set myTizen.ContactManager = somefunction();
Here's what you want:
function myTizen() {
function whatevername() {
// methods and stuff
}
// you can even extend whatevername's prototype down here
this.ContactManager = whatevername; // please note the lack of parentheses
}
// here's another way you could do it:
function alternateMyTizen() {
}
function alternatewhatever() {
// methods and stuff
}
// extend the prototype if you choose
alternateMyTizen.prototype.ContactManager = alternatewhatever;
The main difference between option 1 and option 2 is that in the second method, your "subclass" remains in scope and can be used independently of your myTizen class, in the first method once the constructor goes out of scope, you can only access it through myTizen.ContactManager.
I am trying create a hashmap in View function having instances of subviews , which I do in init method of View. But it is giving an error that init() of view doesnt exist. Am I doing anything wrong here? Thanks in advance.
http://jsfiddle.net/3fR4R/1/
view = function() {
var subview;
init = function() {
subview['search'] = new searchSubView();
}
}
check = function() {
console.log("clicked");
var view1= new view();
view1.init();
}
searchSubView = function() {
}
You've created a function and assigned it to an implicit global, which has nothing to do with the view function or instances created by it.
You can assign the function either by assigning to this.init within the constructor, or by putting it on view.prototype.
So either:
view = function() {
var subview;
// Note: You need code here to initialize `subview`
this.init = function() {
subview['search'] = new searchSubView();
};
};
or (note that I've made subview a property):
view = function() {
this.subview = /* ...initialize subview... */;
};
view.prototype.init = function() {
this.subview['search'] = new searchSubView();
};
Side notes:
You're falling prey to The Horror of Implicit Globals a lot in that code. You need to declare variables.
The overwhelming convention in JavaScript code is to use initial capitals for constructor functions, e.g., View rather than view.
You're also relying on automatic semicolon insertion, which I wouldn't recommend. Learn the rules and apply them, not least so you can minify/compress/compact your code safely.
Say I have the following modules, split across multiple files both capable of extending skillet:
File1.js:
(function(){
var privateVar1 = 0;
var privateFunction1 = function() {
//function definiton
};
skillet.fry() = function() {
//fry it
//matchbox.light();
};
})(window.skillet = window.skillet || {});
File2.js:
(function(){
var privateVar2 = 0;
var privateFunction2 = function() {
//some private function
};
skillet.grillIt = function() {
//grill It
//matchbox.strike(); <-- Shared with File1.js
};
})(window.skillet = window.skillet || {});
Is it possible to have a shared variable/object like matchbox be sharable by the two modules without being bound to window.matchbox or window.skillet.matchbox? I.e. the visibility of matchbox should only be to File1.js and File2.js and must not be accessible elsewhere. I doubt if it's possible, but is there a way to achieve such a behavior in JavaScript? If not, what's the best practice to use in this regard?
(It's more like having a shared event-bus among a set of related modules without exposing that bus globally)
Nope.
"private" variables work in JS only because of the scope that the function was declared in. There is no way to share that scope with a function declared in an entirely different scope. Scope is an unchangeable property of functions, once they are created.
This is why this sort of thing is usually done with _foo style properties.
skillet._matchbox = { strike: function() { ... } };
The underscore prefix is convention for "internal" and serves as a hint not to mess with it.
You could also get creative with how you pass matchbox around though, though in all cases it will mean providing a way to get matchbox out it's original scope. Like perhaps, makes a skillet.extend method that passes the matchbox to it's argument?
(function() {
var matchbox = { strike: function() { ... } }
window.skillet = {
extend: function(fn) {
fn(matchbox);
}
};
})();
skillet.extend(function(matchbox) {
var privateVar2 = 0;
var privateFunction2 = function() {};
skillet.grillIt = function() {
//grill It
matchbox.strike();
};
}
Which allows you to use matchbox outside it's original scope in a controlled way. But it also allows anyone to get matchbox that maybe shouldn't.
var stolenMatchbox;
skillet.extend(function(matchbox) {
stolenMatchbox = matchbox;
});
while (stolenMatchbox.count > 0) { stolenMatchbox.strike(); }
alert("Now you are outta matches, sucker!");
Since you are already dividing your code into multiple files, you may look into using a module loader like require.js. You could define a third module for the matchbox and then pass it in as an argument to the two skillets in your example above. With this approach, you won't have to globally expose the matchbox via the window.
File1.js with require.js would look like this:
define(['matchbox'], function(matchbox){
(function(){
var privateVar1 = 0;
var privateFunction1 = function() {
//function definiton
};
skillet.fry() = function() {
//fry it
matchbox.light();
};
})(window.skillet = window.skillet || {});
});
matchbox.js would look something like this:
define([], function() {
function light() {
//light implementation
}
function strike() {
//strike implementation
}
return {
light: light,
strike: strike
}
}
I am wrapping common javascript functions that will work on elements on a page.
My page has 2 of these elements (textareas), so I will need to create 2 instances and then I want to do this:
var textArea1 = new SomeClass();
var textArea2 = new SomeClass();
textArea1.init("ta1");
textArea2.init("ta2");
I tried doing this the module pattern way, but I'm confused how I can create 2 seperate instances of it?
var MYMODULE = function() {
var _init = function(ta) {
// ..
}
return {
init: function(ta) {
_init(ta);
}
};
}();
Use a constructor function:
function SomeClass(id) {
this.id = id;
// ...
}
Usage:
var textArea1 = new SomeClass("ta1");
var textArea2 = new SomeClass("ta2");
You can put methods for the class in the prototype for the function. Example:
SomeClass.prototype = {
getValue: function() { return document.getElementById(this.id).value; }
};
Usage:
var text = testArea1.getValue();
Using your specific example, you could just MYModule twice, but it's a weird pattern that doesn't seem to do a whole lot.
Simple example how instantiation works:
function SomeClass() {
// constructor
}
SomeClass.prototype.init = function(ta) {
// ..
}
var textArea1 = new SomeClass();
var textArea2 = new SomeClass();
textArea1.init('ta1');
textArea2.init('ta2');
But regardless, you may like Backbone.js
Your MYMODULE idea will work fine. As above and then
MYMODULE.init("ta1");
MYMODULE.init("ta2");
This line here will not care it is called with two different parameters
var _init = function(ta) {
// ..
}
It is just a place to hold a function. The real question is what is inside that function.
For example if it works with ta in some standard way (attaches event handlers, does some styling.. ) then it will not be a problem. The issue will be if you use MYMODULE local variables and expect to have more than one of them. You only have one MYMODULE so local variables will be shared with this design. This might be what you want. I'm not sure.
This pattern can work fine for a control passed in having special data all itself. The best way to do this -- since you are using jQuery is with the data function... thus the code could look like:
var _init = function(ta) {
jQuery.data(ta,"foo", 10);
// etc
}