By Azat Mardanov
If it’s not fun, it’s not JavaScript. And you can’t spell fundamentals without fun.
Programming languages like BASIC, Python and C have a boring, machine-like nature that requires developers to write extra code that’s not directly related to the solution itself. Think about line numbers in BASIC or interfaces, classes and patterns in Java.
On the other hand JavaScript inherits the best traits of pure mathematics, LISP and C# which leads to a great deal of expressiveness (and, arguably, fun). You can read more about expressive power in languages via this post.
The quintessential “Hello World” example in Java looks like (remember, Java is to JavaScript is what ham to a hamster):
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World");
}
}
The same example in JavaScript:
console.log('Hello World')
…or from within an HTML page:
<script>
document.write('Hello World')
</script>
JavaScript allows programmers to focus on the solution/problem rather that to jump through hoops and API docs. In this article I want to cover some of the basic aspects of JavaScript that can make developing with it fun (if occassionally also frustrating).
Loose Typing
Automatic type casting works well most of the time. It’s a great feature that saves a lot of time and mental energy. There are only a few primitives types in JavaScript:
- String
- Number (both integer and real)
- Boolean
- Undefined
- Null
Everything else is an object (i.e., mutable keyed collections). Read Stackoverflow on “What does immutable mean?”
Also, in JavaScript, there are String
, Number
and Boolean
objects which contain helpers for the primitives. For example:
'a' === new String('a') //false
…but…
'a' === new String('a').toString() //true
…or…
'a' == new String('a') //true
By the way, ==
performs automatic type casting while ===
does not.
Object Literal Notation
Object notation in JavaScript is very readable and compact:
var obj = {
color: "green",
type: "suv",
owner: {
...
}
}
Remember that functions are objects so you could also write:
var obj = function () {
// do something
};
obj.a =1;
console.log (obj.a); // prints 1
Another very similar looking pattern (but completely different in behavior) is when we use function to construct/initialize objects:
var Obj = function (ops) {
this.name = ops.name;
}
var obj = new Obj ({name: 'puppy'});
console.log(obj)
Functions
Functions are first-class citizens, and we treat them as variables, because they are objects. Yes, functions can even have properties/attributes.
Create a Function
var f = function f () {
console.log('Hi');
return true;
}
…or…
function f () {
console.log('Hi');
return true;
}
A function with a property (remember functions are just objects that can be invoked, i.e. initialized):
var f = function () {console.log('Boo');}
f.boo = 1;
f(); //outputs Boo
console.log(f.boo); //outputs 1
Note: the return keyword is optional. In this case it’s omitted and the function will return undefined upon invocation.
Passing Functions as Parameters
Because of the special nature of functions in JavaScript, you can also pass a function as a parameter to another function:
var convertNum = function (num) {
return num + 10;
}
var processNum = function (num, fn) {
return fn(num);
}
processNum(10, convertNum);
Arrays
Arrays in JavaScript are also objects which have some special methods inherited from the Array.prototype global object. Nevertheless, JavaScript Arrays are not real arrays. Instead, they are objects with unique integer (usually 0-based) keys.
var arr = [];
var arr2 = [1, "Hi", {a:2}, function () {console.log('boo');}];
var arr3 = new Array();
var arr4 = new Array(1,"Hi", {a:2}, function () {console.log('boo');});
Prototypal Nature
There are no classes in JavaScript because objects inherit directly from other objects which is called prototypal inheritance. However, there are a few types of inheritance design patterns used by JavaScript developers:
- Classical
- Pseudo-classical
- Functional
Here’s an example of the functional inheritance pattern:
var user = function (ops) {
return { firstName: ops.name || 'John'
, lastName: ops.name || 'Doe'
, email: ops.email || 'test@test.com'
, name: function() { return this.firstName + this.lastName}
}
}
var agency = function(ops) {
ops = ops || {}
var agency = user(ops)
agency.customers = ops.customers || 0
agency.isAgency = true
return agency
}
While not technically classes, they are often referred to as such.
Conventions
Most of these conventions (with semi-colons being an exception) are stylistic, highly preferential and don’t impact the execution.
Semi-Colons
Semi-colons are optional except for two cases:
- In for loop construction: for (var i=0; i++; i<n)
- When a new line starts with parentheses, as in Immediately-Invoked Function Expressions (IIFE):
;(function(){...}())
camelCase
Use lowerCamelCase, except for “class” names which are UpperCamelCase:
var MainView = Backbone.View.extend({...})
var mainView = new MainView()
Variable Naming
_
and $
are perfectly legitimate characters for literals (the jQuery and Underscore libraries obviously use them a lot).
Private methods and attributes within “classes” should start with _
.
Commas
Comma-first approach
var obj = { firstName: "John"
, lastName: "Smith"
, email: "johnsmith@gmail.com"
}
Indentation
Usually JavaScript developers use either tab, 4 or 2 space indentation with each their supporters’ camps being almost religiously split.
White spaces
Usually, there is a space before and after =
, +
, ans the {
and }
symbols. There is no space on function invocation, as in arr.push(1);
, but there’s typically a space when we define an anonymous function as in function () {}
.
Modules
There is no built in support for modules, at least until ES6 (i.e. ECMAScript 6 which is the next version of JavaScript). Everything is in the global scope (a.k.a. window
) and included via <script>
tags. However, there are external libraries that allow for workarounds to support module loading:
- CommonJS
- AMD and Require.js
Node.js uses a CommonJS-like syntax and has built-in support for modules.
To hide your code from the global scope, you can make private attributes/methods using closures and immediately-invoked function expressions (commonly called IIFEs).
Immediately-Invoked Function Expressions (IIFEs)
A basic IIFE with no implementation is:
(function () {
window.yourModule = {
...
};
}());
This snippet show an example of a object with a private attribute and a method:
(function () {
window.boo = function() {
var _a = 1;
var inc = function () {
_a++;
console.log(_a);
return _a;
};
return {
increment: inc
};
}
}());
var b = window.boo();
b.increment();
Now try this:
b.increment();
b.increment();
b.increment();
“this” Keyword
this
mutates/changes a lot (especially in jQuery). A common pattern is to re-assign it to a locally scoped variable before attempting to use this
inside of a closure:
var app = this
$('a').click(function(e){
console.log(this) //most likely the event or the target anchor element
console.log(app) //that's what we want!
app.processData(e)
})
When in doubt, use console.log
!
Pitfalls
JavaScript is the only language I know of that programmers often think they shouldn’t truly learn (instead often relying on things like libraries and copy/paste to get by). However, things like ===
versus ==
, global scope leakage and other aspects of the language might lead to problems down the road. This is why it’s important to understand the language or use an abstraction like CoffeeScript, that abstracts away most of the issues.
Further Learning
If you liked this article and would like to explore JavaScript more, take a look at my free resource: Eloquent JavaScript: A Modern Introduction to Programming. Of course for more advanced JavaScript enthusiasts and pros, there’s my book Rapid Prototyping with JS and intensive programming school HackReactor, where I teach part-time.
This article was originally published at https://webapplog.com/js-fundamentals/
There’s a mistake in the “Object Literal Notation” section. The code after “Remember that functions are objects so you could also write…” is incorrect. Each colon in that function should be an equals sign, and the commas should be semicolons. Yes, functions are objects, but you don’t code functions using object literal notation.
Also, IIFEs can be used for various things that have nothing to do with creating modules or adding things to the global scope, so your “basic IIFE with no implementation” example should not include the line window.yourModule = …
The second IIFE example would produce exactly the same result without the IIFE, since the only thing inside it is an assignment of a function to window.boo.
Thanks for the excellent feedback. I’ve contacted the author and hope to address the issues shortly.
Hi Nathan,
Thanks for spotting `:` issue in the functions are objects section, it really should be:
var obj = function () {
// do something
};
obj.a =1;
console.log (obj.a); // prints 1
Another very similar looking pattern (but completely different in behavior) is when we use function to construct/initialize objects:
var Obj = function (ops) {
this.name = ops.name;
}
var obj = new Obj ({name: ‘puppy’});
console.log(obj);
Hope that helps.
RE: IIFE, the whole point of them is to wrap the code and prevent global scope leakage. In addition to that _a is a private attribute.
Note: Obj with a capital O and _a with an underscore are conventions.
Hi Azat,
Yes, IIFEs let you prevent global scope leakage, but my point was that the IIFE examples in the article don’t illustrate this since they don’t actually have any local variables at all. The _a variable you mention in your comment is not in the IIFE, it’s in the window.boo() function that is declared inside the IIFE. That is, even without the IIFE you could just write window.boo = function() { … } and boo() would be the only thing added to the global scope – nothing else would leak.
You can’t spell “fundamental” without “damn” or “mental” either.
for simple things abstraction is an overkill, learning a new language…just learn Javascript properly
In Python, Hello World is actually shorter than in JS:
print “hello world”
However, all this stuff is unwieldy compared to LISPs concise syntax:
‘hello world
So, by logical extension of your argument, LISP might be the fun-niest language of all. 🙂
Thanks a lot for mentioning about “Comma-first approach”. I was wondering why people use that but didn’t know how to ask google about that. 🙂 Now I understand it.
While I agree with most of the article (especially comma-first) I have to disagree with the semicolon part. I know this is a “religious debate”, but there’s no reason for it to be. Put your semicolons in. Not inserting semicolons can lead to bugs because Automatic Semicolon Insertion may not always get it right. It is a tool created for catching mistakes, after all 😉
Also, for the love of god, please do not use CoffeeScript. That is all.
See: https://stackoverflow.com/questions/2846283/what-are-the-rules-for-javascripts-automatic-semicolon-insertion-asi
Hi Jason,
If you read Isaac Schlueter’s post on semi-colons carefully, you’ll learn that there are only two cases when not having “;” and ASI will break the code (for loops and before statements with parens). So IMHO the benefits far outweigh any drawbacks. Same with CoffeeScript. I was joking that CS is a solution without a problem, but that changed 180 after I joined real prod project on CS. Many people who dislike and frown upon CS have never build any real production apps on it. Also Backbone.js and CoffeeScript work wonderfully well together.