Prototype is a javascript mechanism that allows javascript object inheritance from another object. Let's see some samples for object creation.
"use strict";
function Foo(){}
var a = {};
var b = [];
var c = new Object();
var d = new Array();
var e = Object.create(null);
var f = Object.create(Function);
var g = function(){};
var h = Foo;
var i = new Foo();
var j = Object.create(Foo);
console.log(typeof a); // object
console.log(typeof b); // object
console.log(typeof c); // object
console.log(typeof d); // object
console.log(typeof e); // object
console.log(typeof f); // object
console.log(typeof g); // function
console.log(typeof h); // function
console.log(typeof i); // object
console.log(typeof j); // object
console.log("\n");
console.log(a instanceof Object); // true
console.log(b instanceof Object); // true
console.log(c instanceof Object); // true
console.log(d instanceof Object); // true
console.log(e instanceof Object); // false
console.log(f instanceof Object); // true
console.log(g instanceof Object); // true
console.log(h instanceof Object); // true
console.log(i instanceof Object); // true
console.log(j instanceof Object); // true
All variables in above are object. Except for e, all objects are instance of Object,
In other words, these objects are inherited from Object directly or indirectly.
To create an object, we can use operator new, call Object.create(),
or use {} for general object [] for array.
a function object can be created via function definition and function expression.
Object.__proto__
"use strict";
function Foo(){}
// new operator
var a = new Object();
var b = new Array();
var c = new Function();
var d = new Foo();
// pre-defined object
var e = Object;
var f = Array;
var g = Function;
// spical
var h = Foo;
var i = function test(){};
var j = {};
var k = [];
// uses Object.create()
var l = Object.create(null);
var m = Object.create(Object);
var n = Object.create(Array);
var o = Object.create(Function);
var p = Object.create(Foo);
// create from existing object
var q = Object.create(a);
var r = Object.create(d);
var s = Object.create(e);
var t = Object.create(h);
var u = Object.create(i);
var v = Object.create(j);
var w = Object.create(k);
var x = Object.create(l);
var y = Object.create(p);
var z = Object.create(q);
console.log("\n")
console.log("new operator..................");
console.log(a.__proto__ === Object.prototype);
console.log(b.__proto__ === Array.prototype);
console.log(c.__proto__ === Function.prototype);
console.log(d.__proto__ === Foo.prototype);
console.log("pre-defined object..................");
console.log(e.__proto__ === Function.prototype);
console.log(f.__proto__ === Function.prototype);
console.log(g.__proto__ === Function.prototype);
console.log("spical..................");
console.log(h.__proto__ === Function.prototype);
console.log(i.__proto__ === Function.prototype);
console.log(j.__proto__ === Object.prototype);
console.log(k.__proto__ === Array.prototype);
console.log("Object.create()..................");
console.log(l.__proto__ === undefined); // empty object, no property
console.log(m.__proto__ === Object);
console.log(n.__proto__ === Array);
console.log(o.__proto__ === Function);
console.log(p.__proto__ === Foo);
console.log("Object.create(existing obj)..................");
console.log(q.__proto__ === a);
console.log(r.__proto__ === d);
console.log(s.__proto__ === e);
console.log(t.__proto__ === h);
console.log(u.__proto__ === i);
console.log(v.__proto__ === j);
console.log(w.__proto__ === k);
console.log(x.__proto__ === undefined); // x's ancestor is an empty object
console.log(y.__proto__ === p);
console.log(z.__proto__ === q);
........
The upper code shows that objects have a property called __proto__.
Base on the output from upper code:
When creating an object using Object.create(para), the object's __proto__ will point to the parameter of Object.create(para)
null is a special case, object, which creating via Object.create(null), will make it's __proto__ empty, so does it's descendants.
Via new operator, {}, and [] to create objects, object's __proto__ will point to constructor's prototype.
__proto__ of pre-defined object, function variable and function declaration are leading to Function.prototype.
their __proto__ point to constructor's prototype.
Referring to Mark Kahn's answer on __proto__ VS. prototype in JavaScript,
__proto__ is the actual object that is used in the lookup chain to resolve methods.
Then we know how to make prototype chain work.
"use strict";
function Foo(){}
Foo.prototype.bar = 333;
// a.__proto__ --> Foo.prototype
var a = new Foo();
console.log(a.bar === 333);
"use strict";
function Foo(){}
Foo.bar = 333;
// a.__proto__ --> Foo.prototype
var a = new Foo();
console.log(a.bar === undefined);
"use strict";
function Foo(){}
Foo.prototype.bar = 333;
// a.__proto__ --> Foo
var a = Object.create(Foo);
console.log(a.bar === undefined);
"use strict";
function Foo(){}
Foo.bar = 333;
// a.__proto__ --> Foo
var a = Object.create(Foo);
console.log(a.bar === 333);
Object.prototype
There is a property called prototype. prototype is different from __proto__.
// variables are from upper code
........
console.log("new operator............");
console.log(a.constructor === Object); console.log(a.prototype === undefined);
console.log(b.constructor === Array); console.log(b.prototype === undefined);
console.log(c.constructor === Function); console.log(c.prototype !== undefined); / ** cunstructor is Function */
console.log(d.constructor === Foo); console.log(d.prototype === undefined);
console.log("pre-defined object......");
console.log(e.constructor === Function); console.log(e.prototype === Object.prototype); / ** cunstructor is Function */
console.log(f.constructor === Function); console.log(f.prototype === Array.prototype); / ** cunstructor is Function */
console.log(g.constructor === Function); console.log(g.prototype === Function.prototype); / ** cunstructor is Function */
console.log("spical..................");
console.log(h.constructor === Function); console.log(h.prototype === Foo.prototype); / ** cunstructor is Function */
console.log(i.constructor === Function); console.log(i.prototype !== undefined); / ** cunstructor is Function */
console.log(j.constructor === Object); console.log(j.prototype === undefined);
console.log(k.constructor === Array); console.log(k.prototype === undefined);
console.log("Object.create().........");
console.log(l.constructor === undefined); console.log(l.prototype === undefined); // empty object, no property
console.log(m.constructor === Function); console.log(m.prototype === Object.prototype); / ** cunstructor is Function */
console.log(n.constructor === Function); console.log(n.prototype === Array.prototype); / ** cunstructor is Function */
console.log(o.constructor === Function); console.log(o.prototype === Function.prototype); / ** cunstructor is Function */
console.log(p.constructor === Function); console.log(p.prototype === Foo.prototype); / ** cunstructor is Function */
console.log("Object.create(existing obj)");
console.log(q.constructor === Object); console.log(q.prototype === undefined);
console.log(r.constructor === Foo); console.log(r.prototype === undefined);
console.log(s.constructor === Function); console.log(s.prototype === Object.prototype); / ** cunstructor is Function */
console.log(t.constructor === Function); console.log(t.prototype === Foo.prototype); / ** cunstructor is Function */
console.log(u.constructor === Function); console.log(u.prototype !== undefined); / ** cunstructor is Function, */
console.log(v.constructor === Object); console.log(v.prototype === undefined);
console.log(w.constructor === Array); console.log(w.prototype === undefined);
console.log(x.constructor === undefined); console.log(x.prototype === undefined);
console.log(y.constructor === Function); console.log(y.prototype === Foo.prototype); / ** cunstructor is Function */
console.log(z.constructor === Object); console.log(z.prototype === undefined);
If an object's constructor is NOT Function, then its prototype will equal to undefined.
NOT every object has prototype.
When creating an object with new operator, prototype will be used for building __proto__.
However, NOT every object that has prototype can use new operator to build its descendant's objects.
// variables are from upper code
........
var objArray = [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z];
for (var ii = 0 ;ii< objArray.length; ii++){
try{
var temp = new objArray[ii]();
console.log(temp);
}catch(err){
console.log("-- error on 'new "+ String.fromCharCode(97+ii)+"()'");
}
}
/*
-- error on 'new a()'
-- error on 'new b()'
Object {}
-- error on 'new d()'
Object {}
[]
function anonymous() {}
Foo {}
test {}
-- error on 'new j()'
-- error on 'new k()'
-- error on 'new l()'
-- error on 'new m()'
-- error on 'new n()'
-- error on 'new o()'
-- error on 'new p()'
-- error on 'new q()'
-- error on 'new r()'
-- error on 'new s()'
-- error on 'new t()'
-- error on 'new u()'
-- error on 'new v()'
-- error on 'new w()'
-- error on 'new x()'
-- error on 'new y()'
-- error on 'new z()'
*/
console.log(c); // function anonymous() {}
console.log(e); // function Object() {}
console.log(f); // function Array() {}
console.log(g); // function Function() {}
console.log(h); // function Foo() {}
console.log(i); // function test() {}
console.log("\n")
console.log(m); // Function {}
console.log(n); // Function {}
console.log(o); // Function {}
console.log(p); // Function {}
console.log(s); // Function {}
console.log(t); // Function {}
console.log(u); // Function {}
console.log(y); // Function {}
// others just forms as normal object, like console.log(q) outputs "Object {}"
An object having constructor property does not mean it can create its descendants via new.
By default, only the object forms as function xxxxx(){} can new its descendants.
Scilicet, only function variable and function definition use with new.
Object.prototype More
Now we are focusing on object creation with new operator.
We know that function variable and function definition can be used for newing objects,
The chart below will illustrate prototype better.
"use strict";
function Foo(){}
// part 1
console.log(Foo === Foo.prototype.constructor);
Let's just look at Foo and Foo.prototype.
The figure on right hand side shows classic relation between Foo and Foo.prototype.
Foo's prototype property points to Foo.prototype,
And Foo.prototype's constructor prototype refers back to Foo.
Function definition and function variable are inherited form Function.
The relation between Function and Function.prototype is very similar to Foo and Foo.prototype.
"use strict";
function Foo(){}
// part 1
........
// part 2
........
// part 3
........
var temp = new Foo();
console.log(temp.constructor === Foo);
console.log(temp.__proto__ === Foo.prototype);
Foo.prototype.a = 333;
console.log(temp.a === 333);
For all objects that are created by new operator, these objects are descendants of Object.
temp.constructor is referring to Foo function because temp is constructed by Foo.
Since temp.__proto__ is used for prototype chain accessing, temp.__proto__ refers to Foo.prototype.
In the future, I will explain more of object inheritance in javascript.
fig 4.prototype-4
Prototype Chain vs. Scope Chain
In previous post I had talked about scope chain to get value. Here the is a question, how do value be retrived?
Variables are looked up on the scope chain, starting with the current execution context and going up the tree of enclosing execution contexts.
Properties are looked up firstly on the base object, then on that object's [[Prototoype]] chain (i.e. its internal prototype).
To test the value retrived from Scope Chain and Prototype Chain,
I wrote the code as follow.
"use strict";
function Foo(){
var a = "scope";
this.readValue = function(){
console.log(a); // scope
console.log(this.a); // proto
}
}
Foo.prototype.a = "proto_2";
var temp = new Foo();
temp.readValue();
The output will be "scope" and "proto". a is Foo function's local variable,
so readValue function accesses a via Scope Chain.
On other the hand, when readValue is called by temp, then this.a is referred to temp.a.
Since temp doesn't have it own property a,
temp will access its Prototype Chain to get the value of this.a