(Borrowed and heavily copied from JavaScript: The Good Parts by Douglas Crockford)
Note: place "use strict";
at the top of your javascript scripts in order to have better type checking.
var empty_object = {};
var stoog = {
"first-name": "Jerome",
"last-name": "Howard"
};
Quotes around the property are optional if the property is a valid javascript variable name (like first_name
) but required if not a valid name (like first-name
).
stooge["first-name"] //"Jerome"
If the property is a valid javascript variable name, we can access it using a '.'
stooge.first-name // not valid, will throw an error
flight.time // valid
If we try to retrieve the value of a non-existent property, we get an undefined
value.
stooge["middle-name"] //undefined
We can use the || operator to fill in defaults:
var middle = stooge["middle-name"] || "(none)";
var status = flight.status || "unknown";
Attempting to retrieve values from undefined
will throw a TypeError
exception. This can be guarded against with the &&
operator:
flight.equipment // undefined
flight.equipment.model // throw "TypeError"
flight.equipment && flight.equipment.model // undefined
stooge["first-name"] = "Lester" // replaces "Jerome"
stooge.nickname = "Curly" // creates a property .nickname and sets the value to "Curly"
Objects are passed around by reference. They are never copied:
var x = stooge;
x.nickname = 'Curly';
var nick = stooge.nickname; // What is the value of nick?
var a = {}, b = {}, c = {}; // a, b, c all refer to different objects
a = b = c = {}; // a, b, c, all refer to the same empty object
No classes in javascript. Instead we use prototypes to inherit object properties.
Every object is linked to a prototype object from which it can inherit properties. All objects created from object literals are linked to Object.prototype
, an object that comes standard with javascript.
This is similar to how declaring a base class in python:
class Deck:
...
is equivalent to declaring a class that inherits from the generic object
class
class Deck(object): # Equivalent to the above class.
...
When you make an object in js, you can specify which object should be the prototype. The create
method creates a new object that uses an old object as its prototype.
var another_stooge = Object.create(stooge); // linking another_stooge to stooge prototype via the create function.
Just like in python, when we make changes to our object the original object prototype is not changed:
another_stooge["first-name"] = "Moe";
stooge["first-name"] // "Lester"
another_stooge["first-name"] //"Moe"
If we call a property that doesn't exist in our object, javascript will look to the object prototype for the property and then the object prototype's prototype all the way back to Object.prototype. This is called delegation
.
The prototype relationship is a dynamic relationship; if we add a new property to a prototype, that property will immediately be visible in all objects that inherit from that prototype.
stooge.profession = 'actor';
another_stooge.profession // 'actor'
To figure out what type of value our property is use the typeof
operator:
typeof flight.number // number
typeof flight.status // string
typeof flight.arrival // object
typeof flight.manifest // undefined
be careful though, it sometimes gives strange answers:
typeof NaN // 'number' lol
typeof Null // 'object' wat
The other option is to use hasOwnProperty
which will return either true
or false
depending on whether the object has the given property. The hasOwnProperty
does not look at the prototype chain.
stooge.hasOwnProperty("profession"); // true
another_stooge.hasOwnProperty("profession"); //false
for (name in another_stooge) { // will return all properties including prototype properties
console.log(name);
}
for (name in another_stooge) { // will only return 'first'
if (another_stooge.hasOwnProperty(name)) {
console.log(name);
}
}
There is no guarantee on the order that the property names will appear. If we want to get names in a specific order, we should use an array instead of a dict-like object.
var i;
var properties = [
'first',
'middle',
'last',
'age',
'profession'
];
for (i = 0; i < properties.length; i += 1) {
document.writeln(properties[i] + ': ' +
another_stooge[properties[i]]);
};
Removing an object property will not touch the prototype property. As a result, if the prototype and the object have the same property name, removing the object property will expose the prototype property.
another_stooge.nickname // "Moe"
delete another_stooge.nickname;
another_stooge.nickname // "Curly"
Javascript has a global variable problem.
[Rant here]
There are three ways to define global variables.
var
statement outside any function:var foo = value;
window
:window.foo = value;
foo = value;
Also note here, that when I say global, I don't mean global to a single script, I mean global to all the scripts referenced in the DOM. That's bad.
Javascript has function-level local scope (but not block scope for some reason...):
Addendum: ES6 introduces block scoping with let
: "let is the new var"
// code here can not use carName
function myFunction() {
var carName = "Volvo";
// code here can use carName
}
And global scope:
var carName = " Volvo";
// code here can use carName
function myFunction() {
// code here can use carName
}
and ...global scope!?
// code here can use carName
function myFunction() {
carName = "Volvo";
// code here can use carName
}
One way to minimize the use of global variables is to create a single global variable for your application:
var MYAPP = {};
That variable then becomes the container for your applicatioon:
MYAPP.stooge = {
first: "Joe",
last: "Howard"
};
MYAPP.flight = {
airline: "Oceanic",
number: 815,
departure: {
IATA: "SYD",
time: "2015-11-22 14:55",
city: "Sydney"
},
arrival: {
IATA: "LAX",
time: "2015-11-23- 10:42",
city: "Los Angeles"
}
};
Later we will see that using closures for information hiding provides another effective global abatement technique.
In [ ]: