Introduction
"UML is probably responsible for the destruction of object-oriented programming. Because its total focus on classes and not on objects has corrupted an entire generation of programmers." - Dave Thomas
"The idea was 'Come and do Scheme in Netscape. Put this programming language into the browser.'" - Brendan Eich
Most people get acquainted with JavaScript like "well, it's an object oriented, dynamically typed, programming language", at least I was. Learning more and more of it, became obvious that the strength of the language lies mostly on its powerful functions. On its first-class functions.
Prototypes are one of the most fundamental JavaScript features. If I start this article with objects, there will be trouble, if I start with functions, there will be double. Either way, I'll soon get into the "Chicken-and-egg" situation.
I'll write some examples and point to some facts. Let's discover the rules of the prototype and build upon the constructors. Something tells me it's best to start, like everybody was starting with this language. You create an object... like this:
Example 1
var myCar = {
color: "silver",
weight: 940
};
But, what's an object? More general on that at Appendix C. For JavaScript, "objects represent collection of named values. The named values, in JavaScript objects, are called properties. Object properties can be both primitive values, other objects, and functions. An object method is an object property containing a function definition." - w3schools
In JavaScript, variables and properties fall into two categories: primitive and reference types. The language has evolved rapidly this last couple of years, so I want to be specific, even punctual. This text will confirm to the ES5.1 standard of June 2011, no more, no less. Five shalt be the number thou shalt count and the number of the counting shalt be five.
Primitive and Reference Types
There are five primitive types: number, string, boolean, null and undefined. This types are passed to functions and operators by value. Passing by value is when you pass a copy of the variable contents to an operator or a function.
Example 2
var text1 = "bird";
var text2 = "bird";
text1 == text2
This means text1
and text2
have the same value, compared letter by letter, although they exist physically distinctive in the computer memory. There are two "bird"s.
A more helpful explanation is, JavaScript "tracks variables for a particular scope with a variable object. Primitive values are stored directly on the variable object". - The Principles of Object-Oriented JavaScript
A variable object is a special object related with an execution context which stores: variables, function declarations and function formal parameters declared in that context.
This two variables are in the global scope and the variable object for the global scope is the global object. Luckily, when we are in the browser console, we can log the global object simply by typing its name, window
. You will get all the properties of window
and if you defined text1
and text2
as "bird", you will see the two of them. Same value, different slot in the global object.
If you pass text1
as a function argument, you will get the contents of text1
copied and slapped into the new variable object of the called function scope. This time by its new name as it was declared in the function's parameter list. In this example as x
.
Example 3
function test(x) {
return x;
}
Unfortunately, we cannot see the variable object of a function. It's an implementation detail. While the global variable object has a defined name for us as window
, the variable object inside a function scope has no specific name, handle or identifier.
One more thing, primitive values are immutable and that's it. You can't change it no more, but you can reassign a new primitive value to your variable.
References
All reference data types in JavaScript represent objects. Some of them we know as arrays, others as functions, as regular expressions... "Reference values are placed as a pointer in the variable object, which serves as a reference to a location in memory where the object is stored." - The Principles of Object-Oriented JavaScript
Example 4
var object1 = {
text: "paragraph"
};
var object2 = {
text: "paragraph"
};
object1 == object2
object1.text == object2.text
Though object2
is a verbatim copy of object1
, they are not considered as equal, because they are not compared by value. These two objects, same as those two string
s: text1
and text2
, exist physically distinctive in the computer memory. They have the same value, but this time, their references are compared for equality. A reference is equal to another only if they have the same address.
The address is not a complex notion like: reference, variable, value, type, etc. that needs to be defined with a combination of other concepts. The address is simply a natural number and it's quite well defined for every object. Let's say, in 32bit desktop versions of the Windows operating system, the total capacity of addressable memory by the OS is 4294967296 bytes. Those bytes are numbered [0..4294967295] and theoretically any even value in this range can be a valid address for those two objects: object1
and object2
.
When JavaScript operator ==
is comparing object1
and object2
, it is comparing the addresses at which those objects exist. For example, if object1
is at address 4000 and object2
is at address 777888, the result of the ==
comparison operation will be false
. If and only if variables object1
and object2
reference the same memory address (example, 123456
), the result of operator ==
will be true
. They will reference the same object.
In any language, the reference must represent at least the address of an object, but whatever a JavaScript reference is, beside the address, it's not my business.
Object Creation
There are three ways to create an object in JavaScript and they all employ a function to do it. Usage of function Object.create
is not elaborated in this text. It deserves an article of its own.
Let's get back to our first example, creating an object by specifying an object literal and by calling new
with constructor Object
.
Example 5
var myCar = {
color: "silver",
weight: 940
};
var hisCar = new Object({color: "blue", weight: 1040});
Now you can clearly see that myCar
is a Suzuki Baleno and that hisCar
is a Hyundai i20, what you cannot see is that the first example of creation implicitly uses the Object
constructor. My goal will be to prove that.
JavaScript has prototypal inheritance. Inheritance is a way of code reuse and objects inherit from their prototype, which in turn is just another object. Newly created objects reuse code and data defined in other already existent objects.
The way all objects in the language reference their parent is via their internal [[Prototype]]
property. Objects have only one parent which in turn will reference its parent in its [[Prototype]]
property...and that linear lookup list is called the prototype chain.
Object myCar
is inheriting a lot of functionality, some property methods to name are: hasOwnProperty
, isPrototypeOf
, propertyIsEnumerable
, toString
, etc. even though myCar
does not implement these methods, it's free to use it.
But, what is the object that implements this method? That would be the "primordial" object. All default reference types: object, functions and arrays inherit from it, if the normal prototype chain is not disrupted.
Alas, this default prototype chain doesn't end with the primordial object, it ends with null
. That is, the [[Prototype]]
property of the primordial object references null
. That's perfectly legal, the primitive value null
is also an object.
Example 6
typeof null
The "Chicken-and-egg" spiral!
Catch-22
My understanding of JavaScript or anything else for that matter is, I'm looking for facts that build up the rules. It was said that objects are of reference type and that null
is one of the primitive types which are not references. That breaks the rules "and when the machine breaks down, we break down".
Not all is lost though, if you have null
assigned to two variables, they are equal. Looks like they are compared by value like primitive types. There is another way of looking at this, what if those variables are referencing an object at the same yet invalid address, i.e., null
? Either way, you think of null
, object or primitive, boolean expressions will give you consistent results.
Things like null
, undefined
or NaN
are hard to reason, but I very much like their ways in JavaScript. The null
viewed as an object is a void reference, one of a kind, at which nothing can be instantiated. You can think of it as an non existent, but real sounding country for which there is no place on the map...
On many occasions, null
is used as an absence of an object where you would usually get one. Say, by a function call that is supposed to return one from the database. Most notably, null
is the last member in the prototype chain.
Similar, undefined
is not only an undefined value, but worse. An undefined data type also. NaN
is a primitive of type number, but has no numerical value and it is the only value that doesn't equal itself. This not only breaks the rules for primitive types, but also breaks the rules of reference types.
Example 7
NaN === NaN
NaN == NaN
Even, the coercing operator ==
is not able to return true
...
Look at myCar
and hisCar
in example 5. My premise is that the first case is a use case of the second one.
Every object has a [[Prototype]]
property that is a reference to some other object. That property happens to have the same value on myCar
as on hisCar
. It is a reference to the same primordial object.
Something is setting to myCar
and hisCar
objects the same [[Prototype]]
property. That something is the Object
constructor function.
Constructor Mechanics
JavaScript comes with some pre built constructors: Object
, Function
, Array
... Some rules of the language depend on those.
All functions in JavaScript are ready set constructors. We don't intend to use all of them as constructors, but there is no difference between a constructor and a normal function. To make a mental note that a function is planned for a constructor, we write it with a first capital letter. That's a convention.
When used with the new
keyword, all functions will return a newly created object. They will all dynamically create a property on that object. Even if you don't intend for them to act as constructors. Even if you don't set anything inside of them. And when I say all, I mean all.
Example 8
function Person(name) {
this.name = name;
}
function multiplyByTwo(n) {
return 2*n;
}
var herCar = {
color: "red",
weight: 1000,
honk: function() {
console.log("honk");
}
};
var obj1 = new Person("steve");
var obj2 = new multiplyByTwo(4);
var obj3 = new herCar.honk();
Obviously, herCar.honk
and myltiplyByTwo
do not explicitly set any properties to obj2
and obj3
and yet they set something. Every function has a prototype
property which is an object. Every function when called with new
is setting its prototype
property to be the [[Prototype]]
property of the object it constructs. This is a crucial fact. If there is only one thing that you remember after reading this article, let it be this.
Example 9
obj1.__proto__ == Person.prototype
obj2.__proto__ == multiplyByTwo.prototype
obj3.__proto__ == herCar.honk.prototype
Person.prototype == herCar.honk.prototype
obj2.__proto__ == obj3.__proto__
Functions are also objects so they too have an internal [[Prototype]]
property. The prototype
property and the hidden [[Prototype]]
property are not the same. The prototype
property is default only on functions.
You can set a property called prototype
on any object you like. If you have no better idea for fun... You can even set the prototype
property of a function to be a primitive value. I could have set the prototype
of constructor Person
to be the numeric value of 8
, but I don't find that funny.
However, you cannot set the [[Prototype]]
property of an object to a primitive value.
In this article, the prototype of an object, [[Prototype]]
and __proto__
, all three things are considered equal and all examples expose this object reference as __proto__
.
When you create an object with the object literal notation, its prototype (parent) becomes Object.prototype
.
Example 10
myCar.__proto__ == hisCar.__proto__
myCar.__proto__ == Object.prototype
So the prototype chain or the lookup list for data and code reuse of myCar
would be: myCar --> Object.prototype --> null
.
For obj1
, it would be: obj1 --> Person.prototype --> Object.prototype --> null
.
When you create your own constructor function, at least, you are inserting one member (object) in the prototype chain between Object.prototype
and your newly created object that is returned from the constructor. In this case, the inserted object is Person.prototype
.
The prototype
property of your constructor function is a very very important object upon which you build all future inheritance for the objects you create.
Example 11
Person.prototype.doubleItsName = function() {
return this.name + this.name;
}
obj1.doubleItsName()
var Pesonia = function(surname) { this.surname = surname };
Personia.prototype = obj1;
var obj4 = new Personia("mcconnell");
obj4.name
obj4.surename
obj4.doubleItsName()
I've just set the prototype chain for obj4
to be: obj4 --> obj1 (Personia.prototype) --> Person.prototype --> Object.prototype --> null
.
Or should I say: obj4 --> obj4.__proto__ --> obj1.__proto__ --> Object.prototype --> null
.
And I can override the name
of obj4
and obj1
.
Example 12
obj1.name = "joey";
obj4.name = "billy";
And I can reuse the code from obj1
in obj4
.
Example 13
obj4.doubleItsName()
obj1.doubleItsName()
In example 11, I didn't have the name
property as own property of obj4
so, JavaScript was delegating though the prototype chain of obj4
. It did found name
on obj1
.
Also, the method doubleItsName
is not found on obj4
, not even on obj1
. It is deeper in the prototype chain. It's on an object I have no other name to call than Person.prototype
.
The Birth of a Miracle
When you use the new
keyword on a function, JavaScript returns a newly constructed object, unless the function is already returning an object. Normally called, function herCar.honk
returns undefined
and function multiplyByTwo
returns a number. They perfectly match that criteria. These two functions do not set anything inside that new object, so it has only the [[Prototype]]
property.
As we discovered, the __proto__
property of an object is the prototype
property of its constructor. A criminal behaviour of functions is that they set the constructor
property of the prototype object to be the function itself, instead as it should be the Object
constructor.
So, the constructor
property of multiplyByTwo
's prototype
is set to be the function multiplyByTwo
(Chicken-and-egg). Therefore, obj2
which inherits from multiplyByTwo.prototype
inherits also this property.
Example 14
multiplyByTwo.prototype.constructor == multiplyByTwo
obj2.__proto__.constructor == multipleByTwo
multiplyByTwo.prototype.hasOwnProperty("constructor")
obj2.constructor == multiplyByTwo
obj2.hasOwnProperty("constructor")
obj2.constructor(6)
obj2
inherits the constructor
property through its prototype chain.
You remember that when I created obj2
, I called multipleByTwo
with argument 4 (example 8)? Because the newly created object returned as obj2
and parameter n
of function multiplyByTwo
were in the same scope (JavaScript has function scope), they both exist even after the function multiplyByTwo
returns. Whoop-de-doo a closure.
I cannot access argument 4 as parameter n
, because I have lost the scope, but the activation object that was created by the execution context when multipleByTwo
was running on the execution stack is still there. It has to be, obj2
is part of that same activation object.
I'll redefine multipleByTwo
to show you this. Now the newly created object will have a method called giveN
.
Example 15
function multiplyByTwo(n) {
this.giveN = function() {
return n;
}
return 2*n;
}
var obj2 = new multiplyByTwo(5);
obj2.giveN()
obj2.constructor(7)
obj2.giveN()
When I call multiplyByTwo
as a constructor with the new
keyword, the context, i.e., this
in the function changes. It is the newly created object. For that moment, the new object has no other identifier than this
and is in the same scope as parameter n
. So is the newly anonymous function that returns n
, which I assign to this
as property giveN
.
Upon return of function multiplyByTwo
, this
will be assigned to obj2
and it will have a member function giveN
that was part of the same scope as was obj2
and argument 5. The scope is again lost, but the activation object of the execution context when multiplyByTwo
run, is preserved. We've created a closure, because we have a reference to at least one member (obj2
, but obj2.giveN
also) of that scope.
Now when I run obj2.constructor
as a function with a different argument, for instance 7, I get that to be the return value from member function giveN
. Why does now the return value from giveN
change from 5 to 7? Does multiplyByTwo
as a function rewrite parameter n
from the closure? Not exactly.
In fact, when you call multiplyByTwo
like this obj2.constructor(
7)
it is not the same as calling multiplyByTwo(
7)
. You are calling that function as a method of obj2
and so this
inside the function becomes obj2
. Next, you create a new anonymous function that you reassign as property giveN
of obj2
. You rewrite the old property and that anonymous function only knows of argument 7 as it sees parameter n
. You are not recreating obj2
so you still have that old argument 5 and first time created function giveN
on the same activation object as obj2
, but you have lost reference to them.
Example 16, More Fun
var t;
function multiplyByTwo(n) {
t = this.giveN;
this.giveN = function() {
return n;
}
return 2*n;
}
var obj2 = new multiplyByTwo(9);
obj2.giveN()
obj2.constructor(11)
obj2.giveN()
t()
By the way, if I call multiplyByTwo
as a normal function, without the new
keyword, I would create a new property on the global object. Function giveN
becomes global as a side effect. A good example of how not to do things.
Programming for the Masses, not the Classes
Bill Venners: "But by delegation, you do mean this object delegating to that object without it being a subclass?"
James Gosling: "Yes -- without an inheritance hierarchy. Rather than subclassing, just use pure interfaces. It's not so much that class inheritance is particularly bad. It just has problems."
The way I have created Person
constructor is not a JavaScript pattern, it's more like what a Java programmer would do.
Example 17
function Person(name) {
this.itsName = function() {
return name;
}
}
var obj1 = new Person("jim");
obj1.itsName()
This is a JavaScript Person
constructor. Now you have a, more then private
, private name
.
The byproduct of nested functions are closures, but the real reason why closures instantiate is in the first classness of functions in JavaScript. The rules that all other variables obey are obeyed by functions, too.
When you call a function in JavaScript and in many other languages, the local variables that are defined inside the function, also called automatic variables, are instantiated at that moment. First class functionality means that local (nested) functions are also instantiated this way automatically and they share the same scope as the local variables. This is something you also want to remember from this article, so I will repeat it. Whenever you call a function that has nested functions defined inside its body, they are all automatically instantiated anew.
Those functions live the same life as the automatic variables. If you happen to return an automatic (nested) function from the outer function to some variable, the garbage collector can no longer dispose of that automatic function from memory, since now you have a reference to it. You can call it. And since that nested function had access to all the peers defined in the same scope of the outer function: local variables, parameters, other local functions and local objects, the garbage collector cannot dispose of its peers too, so we have a closure.
Standard Pascal has nested functions also, but it does not have closures because functions lack first class citizenship. That is to say, it's nested functions are not created anew as local variables, but are created once upon compile time. Only the stack frame of the nested function is created/destroyed at runtime. Static noise...
Other notable built in constructors of JavaScript are: RegExp
, Array
, Function
... As you would have expected, the Function
constructor is responsible for creating functions. Consistent to the object construction examples, no matter in what way you create a function.
Example 18
function addTwo(n) { return n+2 }
var addThree = function(n) {return n+3};
var addFour = new Function("n", "return n+4");
addTwo.constructor == addThree.constructor
addThree.constructor == addFour.constructor
addTwo.constructor == Function
Object.constructor == addThree.constructor
Array.constructor == Function
All functions are created by the Function
constructor. And so, Function.prototype
as a rule gets inserted as a [[Prototype]]
property to all objects created by it and you know what they say, functions in JavaScript are objects as well.
Example 19
addTwo.__proto__ == Function.prototype
Object.__proto__ == Function.prototype
Array.__proto__ == Function.prototype
RegExp.__proto__ == Function.prototype
What is this object, the Function.prototype
? It's a native function. A pice of code that always stays translated in its immediately executable form by the JavaScript engine. One of the most crucial pieces of code in the entire JavaScript runtime. As you can see Function.prototype
is Object.__proto__
, the most immediate ancestor in the prototype chain of constructor function Object
. What is then Function.__proto__
?
The rule was that any object's __proto__
property is the constructor's prototype
property. What is the constructor of Function?
Example 20, Catch-22
Function.constructor == Function
Function.__proto__ == Function.prototype
At last, let's se how a constructor's prototype delegation relates to Function
and Object
built-in JS constructors, example Person
:
Person --> Function.prototype --> Object.prototype --> null
.
Or: Person --> Object.__proto__ --> Object.prototype --> null
.
Functions are somewhat more rich, they have in their prototype chain the primordial function and the primordial object. Objects only inherit the primordial object.
Appendix C
When programmers talk about objects, they talk about the same thing only at the same level of programming languages. There are low level and high level programming languages.
"A high-level programming language is a programming language with strong abstraction from the details of the computer".
For a C programmer, an object is an instance of a data type. For an advanced C++ programmer, it is also an instance of a data type, but they'll more commonly refer to objects as being instances of a class.
Advanced C++ programmers know that only the data members of the class are the once being instantiated. It's methods are static and have only one instance in the text or code segment of the compiled program.
A beginner C++ programmer considers an object to be an instance of a class. A Java (crippled version of C++) developer does not only see objects as instances of a class, but solely as a product of the 'class
' keyword.
I advisedly did not say same level of abstraction, but used "the same level of programming languages". Higher level of programming languages are supposed to have a higher level of abstraction. JavaScript being of greater than Java, Java greater than C++ and C++ offering greater level of abstraction over C.
Abstraction can be seen as factoring out common properties from the specific ones, but in this elementary example, a Java object is a special case of a C++ object, being only able to use single inheritance (C++ has multiple). Both, Java and C++ definitions of an object fail into a special case of a C object.
You have to separate that in your head, the concept of object and class.
Don't fool yourself that, because C hasn't got the 'class
' keyword, it can't implement classes. "The very first Java compiler was developed by Sun Microsystems and was written in C", "the JRE is written in C" and "Most of the Sun's implementation of Java -JVM- is written in C". C can implement Java features, but it doesn't work the other way around. :)
There are many examples whereby using an instance of a Java class, you end up using an instance of a C class, especially in the Android system where said Java classes are only wrappers around C shared libraries.
If I have to be very brief, I'd say a class is a complete set of definitions of a data type and its accompanying functions. All nicely encapsulated in one file. But, that's just my crude way of saying it.
Here is a more profound statement: "A procedure which is capable of giving rise to block instances which survive its call will be known as a class; and the instances will be known as objects of that class." - Structured Programming (Dahl, Dijkstra and Hoare)
History
- 9th December, 2019: Initial version