After building a project that attempted to use an object oriented way with Javascript I found that the rules of prototyping in Javascript were very confusing. After reading several articles that weren't really helping I found one that mentioned all the "rubbish" in articles he had read. That led me to an experiment to get to the truth of the matter - code!
So here is the code. Run this, observe its assumptions, and I think you will soon be on your way to really understanding Javascript objects, inheritance and prototypes.
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Object Test for Browsers</title> <style type="text/css"> body { text-align:center; } </style> <script type="text/javascript"> /* objectTest.html */ Object.prototype.deepCopyValues = function() { // returns a deep copy of this without function memebers - quicker return JSON.parse(JSON.stringify(this)); } Object.prototype.deepCopy = function() { // returns a deep copy of this function deepCopyFunctions(org, copy) { if (org == null) { return null; } var keys = Object.keys(org); for (var i = 0; i < keys.length; i++) { var key = keys[i]; var o = org[key]; if (typeof(o) == 'function') { copy[key] = o; } else if (typeof(o) == 'object') { copy[key] = deepCopyFunctions(o, copy[key]); } } return copy; } var copy = this.deepCopyValues(); return deepCopyFunctions(this, copy); } function run() { var o = { a : 1, d : 'string' }; var o2 = o.deepCopy(); var o3 = o; if (o !== o3) { debugger; return 'An object is equal to (===) a reference of itself'; } if (o === o2) { debugger; return 'An object is not equal to (!==) a deep copy of itself'; } if (o == o2) { debugger; return 'An object is not equal to (!=) a deep copy of itself'; } o.a = 2; if (o2.a == 2) { debugger; return 'Changing a member of an object does not affect a deep copy of itself'; } o3 = { a:5 }; if (o.a == 5) { debugger; return 'Changing an object reference to a different object does not change the original object.'; } if (o.__proto__ != ({}).__proto__) { debugger; return 'An object that is not created with new has an empty prototype.'; } if (o2.__proto__ != ({}).__proto__) { debugger; return 'A deep copy of an object not created with new has a null prototype.'; } function fnConstructorA(){ this.a = 1; this.d = 'string'; }; try { fnConstructorA(); } catch (e) { if (e.message != 'Cannot set property \'a\' of undefined' && // Chrome e.message != 'this is undefined') { // Firefox //debugger; return 'Calling a proper constructor without new makes the \'this\' variable in the constructor undefined. message=' + e.message; } } var A = new fnConstructorA(); var A2 = A.deepCopy(); var A3 = A; if ((A.__proto__).__proto__ !== ({}).__proto__) { debugger; return 'A new created object who\'s constructor has no prototype has the default Object prototype.'; } if (A.constructor !== fnConstructorA) { debugger; return 'a new created object\'s constructor is the function that created the object.'; } if (A !== A3) { debugger; return 'An new created object is equal (===) to a reference of itself'; } if (A === A2) { debugger; return 'A new created object is not equal (===) to a copy of itself'; } if (A != A3) { debugger; return 'An new created object is equal (==) to a reference of itself'; } if (A == A2) { debugger; return 'A new created object is not equal (!=) to a copy of itself'; } function fConstructorB(o) { // like Object.create() this.__proto__ = o; this.a = 5; this.data = 'that string'; } var B = new fConstructorB(A); if (B.__proto__ !== A) { debugger; return 'A proper Object.create() call yields an object with a prototype that equals the object given to the function.'; } if (B.a == A.a) { debugger; return 'Values in a child object overshadow the same value in its prototype.'; } if (B.d != 'string') { debugger; return 'Values in a prototype are visible from a child object if that value is not defined in the parent object.'; } B.a = 14; if (B.a != 14) { debugger; return 'A value in a child object can be changed.'; } if (A.a != 1) { debugger; return 'A value in a prototype of an object is not affected if a vaule in a child object is changed.'; } B.__proto__.e = 'new string'; if (B.e != 'new string') { debugger; return 'Changing a prototype\'s value within a child object affects the child objects same value if not defined in the child object.'; } A.f = 'proto changed'; var C = new fConstructorB(A); if (B.f != 'proto changed') { debugger; return 'Changing a prototype object value affects any child objects of it that don\'t have that value defined.'; } if (C.f != 'proto changed') { debugger; return 'Changing a prototype object value affects any child objects of it that don\'t have that value defined.'; } return 'SUCCESS!!!!'; } function runTests() { var str = run(); document.body.innerHTML = str; } </script> </head> <body onload="runTests();"> Testing... </body> </html>