I have ignored javascript forever. I started using jQuery a few years back so I could get by. But as I've started doing TDD more I decided yesterday to really dive into javascript (and possibly coffeescript after that).
In my ASP.NET Web Forms application I have many pages and currently most of those pages do not have a ton of javascript. I'm in the process of changing that. I'm using Jasmine with Chutzpah to create my tests.
I was moving along with my tests passing and failing as expected. But then I wanted to create a namespace so I wouldn't be trampling all over global space.
After reading this article:
http://enterprisejquery.com/2010/10/how-good-c-habits-can-encourage-bad-javascript-habits-part-1/
I decided to try and use the pattern from the Self-Executing Anonymous Function: Part 2 (Public & Private) section of the article. It appears to have the most flexibility and appears to encapsulate things very well.
I have a folder called /Scripts. Under that folder are some of the frameworks I'm using like jQuery, jasmine, (twitter) bootstrap and modernizr. I also have a subfolder called /Site where I am putting my code for the site in multiple files based on the page. (product.js, billing.js, etc.)
Under /Scripts/Site I added a subfolder to /Tests (or Specs) that have the files (product_test.js, billing_tests.js, etc.).
Without having namespaces everything is fine. I have a utility.js file I created with a padLeft helper function. I then used that global padLeft in another .js file. My tests all worked and I was happy. I then decided to figure out the namespace and changed my Scripts/Site/utility.js to look like:
(function (myns, $, undefined) {
//private property
var isSomething = true;
//public property
myns.something = "something";
//public method
myns.padLeft = function (str, len, pad) {
str = Array(len + 1 - str.length).join(pad) + str;
return str;
};
//check to see if myns exists in global space
//if not, assign it a new Object Literal
}(window.myns= window.myns|| {}, jQuery ));
Then in my Scripts/Site/Tests/utility_test.js I have
/// <reference path="../utility.js" />
describe("Namespace myns with public property something", function () {
it("Should Equal 'something'", function () {
expect(myns.something).toEqual('something');
});
});
With this extremely simple test I was expecting myns.something to come back with the string value of 'something'.
It doesn't. It comes back undefined.
So, how do I to use javascript namespace across multiple files?
Sorry for the long introduction, but I figured it may help explain the why of me doing it this way. I also put all of this because I'm open to hearing ideas about how this setup is totally wrong or partially wrong or whatever.
Thanks for taking the time to read this question.
UPDATE: SOLVED
Thank you all for your help. The most help came from the commenter #T.J. Crowder. I didn't know the jsbin tool existed and after being convinced that the code I put above was put into the tool and the results were right I knew something had to be off in my environment.
The link in the accepted answer also helped me out a lot. After seeing that the syntax and logic was consistent and working I just had to determine what was off about my setup. I'm embarrassed to say it was me passing in jQuery but in my test harness where I was trying to get this to work I wasn't actually using jQuery. This meant the module wasn't actually being loaded - so myns was never set.
Thanks everyone. Hopefully this may be helpful to someone in the future. If you use the above make sure you include the jQuery object. The other option is to not pass in jQuery and remove the $ from the param list.
Try putting your name space declaration outside of the function call:
myns = window.myns || {};
(function(myns, $, undefined) {
...
}(myns, jQuery));
Your existing syntax appears to be completely valid, but breaking that line out may help figure out what's going wrong with your variable scope.
Javascript does not have namespaces, i guess you have troubles with variable scopes and this is similar to other languages, what you are talking about are objects. You could use
window.myns.something
The first function is already adding your object to the window object, and the window object is accessible from anywhere.
Related
I've always used jquery with a
$(document).ready(function {
....
});
But i've recently just inherited a complete site with the script file opening like the following:
var gc = gc || {};
gc.header = {
mobileNav: function () {
...
},
headerLink: function () {
...
}
};
gc.header.headerLink();
I've never seen it structured in this way - it's actually quite easy to use and would love to learn more about to help improve my coding styles.
If somebody could help me by providing me with what this type of coding style is? And - if you know of any, some learning resources?
Thanks in advance!
Lew.
It is usually referred to as namespacing. It has absolutely nothing to do with jQuery.
This style of JavaScript syntax is a little more object oriented. This makes it easier to edit and read but also helps your JavaScript stay 'clean' by namespacing your JavaScript. This means that you basically try to keep as much of your JavaScript out of the Global Object as possible - thereby reducing collisions in your code.
Line by line:
var gc = gc || {}; // If a 'gc' object exists, use it. Otherwise create an empty object.
gc.header = { // In the gc.header object create 2 methods, mobileNav and headerLink
mobileNav: function () {
...
},
headerLink: function () {
...
}
};
gc.header.headerLink(); // Reference the headerLink() method inside gc.header
This is far preferable to creating a more flat pattern where mobileNav and headerLink are global functions because mobileNav and headerLink are very generic functions that may be used and named identically in other plugins. By namespacing you reduce the risk of breaking your code and running into collisions because gc.header.headerLink() is much more unique.
It`s just ordinary JavaScript, it's a technique called namespacing: How do I declare a namespace in JavaScript?
Context: I have written a mini JS library for myself which is simply a collection of commonly used classes. I have followed the IIFE (http://benalman.com/news/2010/11/immediately-invoked-function-expression/) technique to separate my code into modules/classes and they're all grouped under a common global namespace var. Let's call it, ns. So we have a typical setup, ns.ClassA, ns.ClassB, etc. Now on the other hand, a separate script (main.js) is loaded at runtime and appended to the document, and this main.js contains the actual code that uses these classes.
Goal: I am trying to find an elegant way of accessing the classes inside main.js directly by calling the class name instead of having to access them through ns. . For example, I would want to be able to do var a = new ClassA(); instead of var a = new ns.ClassA();
Solutions researched & considered:
1) the dreaded 'with' keyword (javascript "static imports"). In this case, I would do something like with(ns){ var a = new ClassA()} , except I will have to wrap the entire main.js inside the with(ns) statement. This is undesirable for obvious reasons.
2) using locally declared variables.
var ClassA = ns.ClassA, ClassB = ns.ClassB;
and then, I will be able to instantiate ClassA and ClassB directly. However, this approach would require me to manually maintain the declaration, and it will just get very messy and hard to maintain as the number of classes increase in the package.
3) pollute the global scope by injection
use a for loop to iterate over ns and map all the classes inside nsto global scope. This is clearly undesirable, and also it will create conflicts for cases such as ns.Event, ns.Audio etc.
4) PaperScript-style (http://paperjs.org/tutorials/getting-started/working-with-paper-js/)
Inspired by PaperScript from PaperJS, where PaperScript code is automatically executed in its own scope that without polluting with the global scope still has access to all the global browser objects and functions, such as document or window. Looking at their source code on GitHub (sorry SO won't let me post any more links), they seem to be using some custom script pre-processing and then Acorn.js to parse. The end result is that one can directly refer to any class inside the paper object. For example, var path = new Path() instead of var path = new paper.Path(), which is exactly what I wanted to achieve. However, my fear is that it might seem to be too much work to implement such a simple feature. So I wanted to see if any one has any ideas?
Thank you for taking your time to read this verbose description of the problem. Any inputs are appreciated.
Note: I have done my best in the past two days into researching this topic, please forgive me if I missed any obvious solutions. In addition, if there's no easy solution to this, I will simply stick with the ns.ClassA pattern. Thank you for your help!
I'm not an expert but I believe you could create a prototype of String and set your vars like so
String.prototype.ns = function(){
return new ns[this]();
}
var ca = "ClassA".ns();
I've built a plugin that will use modules. Basically functions that can be added to the code in order to provide additional functionality.
In the plugin is a function to call these modules. Previously, I had called them like this:
processInstance($(doc).find('[data-bcp-crumbs]'), crumbs);
processInstance($(doc).find('[data-bcp-copyright]'), copyright);
processInstance($(doc).find('[data-bcp-activenav]'), activeNav);
The last part of each line is the name of a function that will be called within the processInstance script. So, I have the name of the function as both a string and a first-class object on each line. I would like to simplify that to something like this:
for (var i=0; i>module.length;i++) {
processInstance($(doc).find('[data-bcp-'+module[i].toLowerCase()+']'), window[module[i]]);
}
The module array is added to after each instance of the actual module code. I'm doing that like this:
module.push('crumbs');
This doesn't work because window[module[i]] is returning undefined.
How does my code need to be modified to make this work?
Here is an jsfiddle example of the entire plugin with a simple module inserted: http://jsfiddle.net/michaeljimmy/U8anp/1/
A colleague of mine helped me find the answer to this question. First, though, I noticed an error in the for loop. I had > instead of <. That didn't help. :)
The concept with the code was correct. The issue was scope. If you look at the jsfiddle, you'll see that the whole thing is in an enclosure, including the functions I was trying to call. So, there was no way window had access to the functions. We ended up making a couple simple changes.
First, along with module.push('crumbs'), or whatever the function name is, we added:
functions.crumbs = crumbs;
Then instead of using window[module[i]], we used
functions[module[i]]
Without the enclosure, window[module[i]] would have worked just fine.
Lines 10 - 16 of jquery.effects.core.js:
;jQuery.effects || (function($, undefined) {
var backCompat = $.uiBackCompat !== false; // Irrelevant
$.effects = {
effect: {}
};
})(jQuery); // At end of file
As I understand it, this adds an effects "namespace", but only if it doesn't already exist.
Can someone explain to me:
What is the initial semi-colon for?
What is the purpose of the undefined parameter? Is the meaning of undefined overridden in some way?
What's the difference between adding a function directly to the jQuery object, and adding one to jQuery.fn as recommended in the jQuery documentation?
Finally, if I wanted to create a bunch of jQuery plugins that would only be used by my own team, would it make sense to lump them all under a company namespace using something like the code above?
Edit: I realize now jQuery.effects is probably a bad example. I see jQuery.ui.core.js does it differently:
(function( $, undefined ) {
$.ui = $.ui || {};
// add some stuff to $.ui here
$.fn.extend({
// plugins go here
});
})(jQuery);
But what is the use of the ui object if plugins are added directly to $.fn anyway? Could I define my namespace under $.fn and add all my plugins to $.fn.acme, so that I use them like so: $('something').acme.doStuff()?
Is there a best practice for this sort of thing?
It checks if jQuery.effects exists
If not, it defines a function and calls in the same time
(function() { ... } (jquery), it passes jQuery object for reasons related to scope and conflict and such.
The first line in that function is said to be irrelevant, it seems to be checking a presence of a jQuery plugin property
It defines a placeholder (like namespace or container class) for the effects jQuery plugin property.
So, to your questions:
1 . What is the initial semi-colon for?
I think nothing special. Just ensuring clean statement. This has some edge cases if the last line before this one was a function declaration close.
2 . What is the purpose of the undefined parameter? Is the meaning of undefined overridden in some way?
It just ensures this doesn't happen later. Passes the global object directly. Common pattern I think.
3 . What's the difference between adding a function directly to the jQuery object, and adding one to jQuery.fn as recommended in the jQuery documentation?
It's the way jQuery is structured and general organization issue. The jQuery object is a function and returns an object. The .fn handles registering this one to apply on returned jQuery objects (from jQuery select or so), so, that's better so that jQuery actually knows about your added function.
4 . Finally, if I wanted to create a bunch of jQuery plugins that would only be used by my own team, would it make sense to lump them all under a company namespace using something like the code above?
Most people don't do it. Wouldn't recommend it. Maybe a common "small" prefix is enough.
I have been wondering how I can create functions like jQuery. For example: $(ID).function()
Where ID is the id of an HTML element, $ is a function that return the document.getElementById reference of the element "ID" and function is a custom javascript function.
I'm creating a little library which implements some functions. And I want to use that sintax without using jQuery.
Now, my questions are: how I can implement that? What is the name of the tecnique that allow that?
Edit:
What I want to do is this:
HTMLElement.prototype.alertMe = function() {alert(this.value);}
Then, when I call document.getElementById('html_input_id').alertMe(), it must show an alertbox with the input value. But HTMLElement.prototype doesn't work in IE.
$ = function(id) {
return document.getElementById(id);
}
Okay, look, what you're asking has a lot of details and implications. The code for jQuery is open source, you can read it for the details; you'd do well to find a good Javascript book as well, the the O'Reilly Definitive Guide.
$ is just a character for names in JS, so as some of the other answers have shown, there's no reason you can't just write a function with that name:
var $ = function(args){...}
Since everyone and his brother uses that trick, you want to have a longer name as well, so you can mix things.
var EstebansLibrary = function(args){...}
var $ = EstebansLibrary; // make an alias
Since you end up doing different things with the entry point function, you need to know how JS uses arguments -- look up the arguments object.
You'll want to package this so that your internals don't pollute the namespace; you'll want some variant of the module pattern, which will make it something like
var EstebansLibrary = (function(){
// process the arguments object
// do stuff
return {
opname : implementation,...
}
})();
And you'll eventually want to be prepared for inheritance and that means putting those functions into the prototype object.
You can use prototype to assign a new function to the Element prototype.
Element.prototype.testFunction=function(str){alert(str)};
This would provide the function 'testFunction' to all HTML elements.
You can extend any base Object this way, i.e. Array, String etc.
This will work without any plugin at all - although that said I don't think it will work in IE. I believe libraries such as MooTools and jQquery create their own inheritance with DOM elements to ensure cross-browser compatibility, although don't quote me on that.