JavaScript lets us do a lot of things. It’s sometimes too forgiving in its syntax.
In this article, we’ll look at how to create objects, including namespacing members with global objects, destructuring dependencies, and private members.
Object Creation
We can create JavaScript objects in a few ways.
We can either use object literals or constructor functions/classes.
However, there’re some patterns that are useful for creating objects in an orderly manner.
Namespace Pattern
We can separate JavaScript code into namespaces so that we don’t have so many global variables.
Namespaces let us avoid name collisions and reduce the need to prefix names.
For instance, we can write:
const APP = {};
APP.Foo = class {}
APP.Bar = class {}
APP.c = 1;
//...
The code above creates a global APP
object which has multiple classes inside it.
They’re in their own object so that name collision is minimized compared to having them all at the top level.
Now that we have JavaScript modules, we can just use that instead.
But if we’re using old-fashioned scripts, we can use this pattern.
It’s a bit more to type. Also, the state of the object is propagated everywhere since it’s all under one global object.
Nested names also mean longer property resolutions lookups.
General Purpose Namespace Function
We can’t assume the object that we have been in the script.
Therefore, we may want to check if it’s there first before we put things in there.
For instance, we can write:
APP = APP || {};
In case APP
isn’t defined yet, we should check with the ||
operator to see if it’s defined.
We can also create a namespacing function.
For instance, we can write:
const namespace = (path) => {
const parts = path.split('.');
const obj = {};
let nestedObj = obj;
for (const p of parts) {
nestedObj[p] = {};
nestedObj = nestedObj[p];
}
return obj;
}
const obj = namespace('foo.bar');
The code above created a namespace
function that takes an object path separated by dots.
It separates the path and creates the empty nested objects that we need.
Then we can put that in the function.
Declaring Dependencies
Most dependencies are namespaces, which we can use to reference global variables from dependencies easily.
For instance, we can write:
const {
foo,
bar
} = APP;
to destructure the APP
namespace into its properties.
This way, we can access them individually in functions or any other piece of code.
Private Properties and Methods
JavaScript has no private properties and methods in objects and constructors.
To keep the private, we either keep them in modules or functions.
If we keep them in objects, anything can access them.
For instance, we can write:
const obj = {
foo: 1,
getFoo() {
return this.foo;
}
};
Then we can access the properties as follows:
obj.foo
obj.getFoo()
Nothing can stop us from accessing them.
Likewise, with constructors and classes, everything is public.
For instance, if we have:
class Foo {
constructor() {
this.foo = 1;
}
getFoo() {
return this.foo;
}
};
Then we can write:
(new Foo()).foo;
or:
(new Foo()).getFoo();
Private Members
If we want private members, then they can’t be a property of this
.
So we can write:
class Foo {
constructor() {
const foo = 1;
}
getFoo() {
return this.foo;
}
};
We can only put them into functions and declare them with let
and const
to avoid exposing them to the public.
With the constructor function syntax, we can write:
function Foo() {
const foo = 1;
};
Privileged Methods
Public members that have access to private members are called privileged methods.
We can define one as follows:
function Foo() {
const foo = 1;
this.getFoo = function() {
return foo;
}
};
getFoo
is a privileged method as it can access foo
.
This way, we can keep things private while being able to use them when needed.
Conclusion
We can use the namespace pattern to put members in a global object to avoid name collisions.
Also, there’re no private members in classes or constructors. So we’ve to define ordinary data with let
or const
to make them private.
Privileged methods are ones that can access private data.