JavaScript is partly an object-oriented language.
To learn JavaScript, we got to learn the object-oriented parts of JavaScript.
In this article, we’ll look at some basic coding and design patterns.
Async JavaScript Loading
By default, script tags are loading synchronously.
This means they’re loading in the order that they’re added in the code.
Therefore, we can move our script tags to the bottom of the page so that we see the HTML content first.
HTML5 has the defer
attribute to let us load scripts asynchronously but in the order that they’re added in.
For instance, we can write:
<script defer src="script.js"></script>
We just add the defer
attribute to the bottom of the script.
This isn’t supported by older browsers, but we can do the same thing by loading our scripts with JavaScript.
For instance, we can write:
<script>
(function() {
const s = document.createElement('script');
s.src = 'script.js';
document.getElementsByTagName('head')[0].appendChild(s);
}());
</script>
We create a script element, set the src
value to the script path and attach it as a child of the head
tag.
Namespaces
We can add an object to namespace our app.
This way, we won’t have so many global variables in our app.
For instance, we can write:
let APP = APP || {};
to create a MYAPP
namespace.
Then we can add properties to it by writing:
APP.event = {};
And then we can add properties to the event
property:
APP.event = {
addListener(el, type, fn) {
// ..
},
removeListener(el, type, fn) {
// ...
},
getEvent(e) {
// ...
}
// ...
};
Namespaced Constructors
We can put constructors inside our namespace object.
For instance, we can write:
APP.dom = {};
APP.dom.Element = function(type, properties) {
const tmp = document.createElement(type);
//...
return tmp;
};
We have the Element
constructor to create some object with.
A namespace() Method
We can create a namespace
method to let us create our namespace dynamically.
For instance, we can write:
APP.namespace = (name) => {
const parts = name.split('.');
let current = APP;
for (const part of parts) {
if (!current[part]) {
current[part] = {};
}
current = current[part];
}
};
We create a namespace
method that takes a string with words separated by dots.
Then we add the properties to the current
object by looping through them.
Once we defined it, we can use it by writing:
APP.namespace('foo.bar.baz');
console.log(APP);
We see the foo.bar.baz
property in the console log.
Init-Time Branching
Different browsers may have different functions that implement the same functionality.
We can check for them and add the functionality we want by checking and which one exists and then add the one that exists to our namespace object.
For instance, we can write:
let APP = {};
if (window.addEventListener) {
APP.event.addListener = function(el, type, fn) {
el.addEventListener(type, fn, false);
};
APP.event.removeListener = function(el, type, fn) {
el.removeEventListener(type, fn, false);
};
} else if (document.attachEvent) {
APP.event.addListener = function(el, type, fn) {
el.attachEvent(`on${type}`, fn);
};
APP.event.removeListener = function(el, type, fn) {
el.detachEvent(`on${type}`, fn);
};
} else {
APP.event.addListener = function(el, type, fn) {
el[`on${type}`] = fn;
};
APP.event.removeListener = function(el, type) {
el[`on${type}`] = null;
};
}
We check for different versions of the addEventListener
and removeEventListener
and their equivalents and put them one that exists in our namespace.
This puts all the feature detection code in one place so we don’t have to that multiple times.
Conclusion
We can create namespace objects to keep our code away from the global namespace.
JavaScript scripts can be loaded asynchronously.