JavaScript is a bit confusing for developers experienced in class-based languages (like Java or C++), as it is dynamic and does not provide a class implementation per se (the class keyword is introduced in ES2015, but is syntactical sugar, JavaScript remains prototype-based). Ref.
- A constructor makes an object
"based on"its own prototype. (JS doesn't do copy). - A constructor makes an object
linked toits ownprototype.
- Function call with a
newkeyword
- It means a object linkage. It comes when we create a
new object.
- When we call an method/property/object of an object, if it is not found then it is delegated the chaining by
[[Prototype]].
ob.__proto__- Object.getPrototypeOf(ob)
- ob.constructor.prototype
-
__proto__brings direct access to [[Prototype]]. So, the__proto__setter allows the[[Prototype]]of an object to be mutated. -
prototypeis the object that is used to build__proto__when we create an object withnew. -
[[Prototype]]is an object specifies its prototype via the internal property.
const foo = {
getName: function() {
return this.name;
}
};
const a1 = {
[[prototype]]: foo, // __proto__: foo
name: 'sajib',
}
a1.getName(); // sajibprototypeis not available on the instances themselves (or, other objects), but only on the constructor functions.prototypeis only available on functions since they are copied fromFunctionandObject, but in anything else it is not. However,__proto__is available everywhere.- Any method declared directly to
Functionwill be considered asstatic method, which meansit exists as local property ofthat Functiononly and won't available to its instances.
function A() {
function staticMethod() {
// this is static method and can't access from the instance of A
}
}
const ob = new A();
ob.staticMethod();
// TypeError: A.staticMethod is not a function- Common OO patterns
- Prototype
InheritancevsBehavior Delegation
Every single 'object' is built by a constructor function call
function User(name) {
this.name = name;
}
User.prototype.getName = function() {
return this.name;
}
const o1 = new User('a1');
const o2 = new User('a2');
o2.sayHello = function() {
alert(`Hello, ${this.getName()}.`);
};
o1.constructor === User;
o2.constructor === o2.constructor;
o1.__proto__ === User.prototype;
o1.__proto__ === o2.__proto__;When an inherited function is executed, the value of this points to the inheriting object, not to the prototype object where the function is an own property.
var ob = {
a: 2,
f1: function() {
return this.a;
}
}
console.log(ob.f1()); // 2, this = ob
// o1 is an object that inherits/linked-to from 'ob'
const o1 = Object.create(ob);
o1.a = 5;
console.log(o1.f1()); // 5, this = o1 not ob!var a = {a: 1};
// a ---> Object.prototype ---> null
var b = Object.create(a);
// b ---> a ---> Object.prototype ---> null
console.log(b.a); // 1 (inherited)
var c = Object.create(b);
// c ---> b ---> a ---> Object.prototype ---> null
var d = Object.create(null);
// d ---> null
console.log(d.hasOwnProperty);
// undefined, because d doesn't inherit from Object.prototypeclass Shape {
constructor(h, w) {
this.height = h;
this.width = w;
}
}
class Rectangle extends Shape {
constructor(h, w) {
super(h, w);
}
getArea() {
return this.height * this.width;
}
}
const o1 = new Rectangle(2, 3);function Shape(h, w) {
this.height = h;
this.width = w;
}
const Rectangle = Object.create(Shape.prototype);
Rectangle.getArea = function() {
return this.height * this.width;
}
const o1 = Object.create(Rectangle)
console.log(o1);const ob = {};
ob.__proto__.a = 'Hello!';
console.log(obj); // {}
console.log(obj.a); // Hello!
console.log(ob.hasOwnProperty('a')); // false
console.log(ob.__proto__.hasOwnProperty('a.')); // trueproto = null | | proto === Object.prototype | | ob
function User(name) {
this.name = name;
}
User.prototype.getName = function() {
return this.name;
}
const o1 = new User('a1');
o1.getName(); // a1
o1.getName = function() { // shadowing, in class system it is called 'method overriding in polymorphism
// invoking User.prototype.getName but inside that method this = User.prototype
// so, this.name = undefined
// for o1
console.log(`Hello, I'm ${this.__proto__.getName()}.`);
// for o2, but for o1 it will go to Object.prototype & there is not 'name'
console.log(`Hello, I'm ${this.__proto__.__proto__.getName()}.`); // for o2
}
o1.getName();
// if we create a new object from o1 (say, o2) then we need two __proto__ to invoke User.prototype.getName
o2 = Object.assign(o1, {a: 1});
// we can skip prototype chain & invoke User.prototype.getName directly
console.log(`Hello, I'm ${User.prototype.identify.call(this)}.`);function User(name) {
this.name = name;
}
User.prototype.getName = function() {
return this.name;
}
User.prototype.sayHello = function() {
// this = o1, deligate to prototype chain & find getName() into User.prototype
return console.log(`Hello, ${this.getName()}.`);
}
const o1 = new User('a1');function Shape(height, width) {
this.height = height;
this.width = width;
}
Shape.prototype.getHeight = function() {
return this.height;
}
Shape.prototype.getWidth = function() {
return this.width;
}
function Rectangle(height, width) {
Shape.call(this, height, width);
}
// Rectangle.prototype = new Shape(); // or,
Rectangle.prototype = Object.create(Shape.prototype);
// NOTE: .constructor is broken here, need to fix
Rectangle.prototype.getArea = function() {
return this.getHeight() * this.getWidth();
}
const o1 = new Rectangle(2, 3);
const o1 = new Rectangle(4, 5);
o1.getArea(); // 6
o1.getArea(); // 20function Shape(height, width) {
this.height = height;
this.width = width;
}
Shape.prototype.getHeight = function() {
return this.height;
}
Shape.prototype.getWidth = function() {
return this.width;
}
function Rectangle(height, width) {
Shape.call(this, height, width);
}
// Rectangle.prototype = new Foo(); // or,
Rectangle.prototype = Object.create(Shape.prototype);
// NOTE: .constructor is broken here, need to fix
Rectangle.prototype.getArea = function() {
return this.getHeight() * this.getWidth();
}
const r1 = new Bar(2, 3);
r1.getArea(); // 6const Shape = {
init: function(height, width) {
this.height = height;
this.width = width;
},
getHeight: function() {
return this.height;
},
getWidth = function() {
return this.width;
},
}
const Rectangle = Object.create(Shape);
Rectangle.getArea = function() {
return this.getHeight() * this.getWidth();
}
const r1 = Object.create(Bar);
r1.init(2, 3);
r1.getArea(); // 6- nicer syntax
- it's actually syntactical sugar, cause behind the scene prototype chain is working
- it has some limitations also
class Shape {
constructor(height, width) {
this.height = height;
this.width = width;
}
getHeight() {
return this.height;
}
getWidth() {
return this.width;
}
}
const o1 = new Shape(2, 3);
const o2 = new Shape(4, 5);
o1.getHeight(); // 2
o1.getHeight(); // 4
class Rectangle extends Shape {
getArea() {
return this.getHeight() * this.getWidth();
}
}
const r1 = new Rectangle(6, 7);
const r2 = new Rectangle(8, 9);
r1.getArea(); // ??
r2.getArea(); // ??const o = {a: 1};
// o --> Object.prototype -->
// o.__proto__ === Object.prototype // true
// o has no own property named 'hasOwnProperty' but the properties exists in Object.prototype so, o will inherits it
const b = [1, 2];
// b --> Array.prototype --> Object.prototype --> null
// b.__proto__ === Array.prototype // true
// indexOf, forEach, etc. will be inherited from Array.prototype
function f() {
return 1;
}
// f --> Function.prototype --> Object.prototype --> null
// f.__proto__ == Function.prototypeDig into some examples from MDN
var shape = function () {};
var p = {
a: function () {
console.log('aaa');
}
};
shape.prototype.__proto__ = p;
var circle = new shape();
circle.a(); // aaa
console.log(shape.prototype === circle.__proto__); // true
// or
var shape = function () {};
var p = {
a: function () {
console.log('a');
}
};
var circle = new shape();
circle.__proto__ = p;
circle.a(); // a
console.log(shape.prototype === circle.__proto__); // false
// or
function test() {};
test.prototype.myname = function () {
console.log('myname');
};
var a = new test();
console.log(a.__proto__ === test.prototype); // true
a.myname(); // myname
// or
var fn = function () {};
fn.prototype.myname = function () {
console.log('myname');
};
var obj = {
__proto__: fn.prototype
};
obj.myname(); // myname-
The
__proto__property has been standardized in the ECMAScript 2015 language specification for web browsers to ensure compatibility, so will be supported into the future. It is deprecated in favor ofObject.getPrototypeOf/Reflect.getPrototypeOfandObject.setPrototypeOf/Reflect.setPrototypeOf(though still, setting the[[Prototype]]of an object is a slow operation that should be avoided if performance is a concern) -
The
__proto__property can also be used in an object literal definition to set the object[[Prototype]]on creation, as an alternative toObject.create(). -
The prototype object has a prototype of its own, and so on until an object with
nullas its prototype. By definition,nullhas no prototype, and acts as the final link of prototype chain. -
Nearly all objects in JS are instances of
Objectwhich sits on the top of a prototype chain. -
The
prototypal inheritancemodel itself is, in fact, more powerful than the classic model. It is, for example, fairly trivial to build a classic model on top of a prototypal model. -
We can't extend an
Objectvar Ob = {}; class User extends Ob { // <--- Error }
class Foo {
constructor(who) {
this.me = who;
}
identify() {
return this.me;
}
}
class Bar extends Foo {
constructor(who) {
this.x = 1; // <--- error!
super(who); // <-- this must come first
}
}
const b1 = new Bar('b1');