ref – http://chineseruleof8.com/code/index.php/2018/02/06/prototype-and-inheritance-in-js-non-class/
When we declare a function in a js file, the ‘this’ is set to the global object.
When trying to access properties that do not exist, the property gets added to the global object.
1 2 3 4 5 6 7 8 9 |
function Person(name) { console.log(`-- Person --`); console.log(this); // this is the global object. this.userName = name; // adds property userName to the global object console.log(this); } Person('ricky'); console.log(global.userName); // ricky |
In our case, as you can see, the ‘this’ inside of function Person is referencing the global object. We access userName, but it does not exist, so it gets added to the global object. Then we assign ‘name’ to it.
Thus, after invoking Person, global.userName will be ‘ricky’.
However, when we use ‘new’ in front of the function, the function becomes a constructor. The ‘this’ inside the function is an empty object with the name ‘Person’.
Finding that property userName does not exist, it will add userName to Person {}, and assign parameter name to it, becoming: Person { userName: ‘ricky’ }
1 2 3 4 5 6 7 8 9 |
function Person(name) { console.log(`-- Person --`); console.log(this); // Person {} this.userName = name; // adds property userName to the global object console.log(this); // Person { userName: 'ricky' } } let me = new Person('ricky'); console.log(me); // Person { userName: 'ricky' } |
Here’s why this happens.
A function constructor an object. All objects have a ‘prototype’ property. A function constructor will also have a property called ‘prototype, which references a ‘prototype object’. So in our case
1 2 3 4 |
console.log(Person.prototype); // Person {} console.log(Person.prototype.constructor); // [Function: Person] console.log(Person.prototype.__proto__); // {} console.log(Person.prototype.__proto__.constructor); // [Function: Object] |
…and if we go a step further, we come to the prototype chain.
Why do we add properties and functions to the prototype object?
1 2 3 4 5 6 7 8 9 10 11 |
function Person(firstN, lastN) { this.firstName = firstN; this.lastName = lastN; } Person.prototype.fullName = function() { return this.firstName + ', ' + this.lastName; } let me = new Person('ricky', 'Cao'); console.log(me.fullName()); // ricky, Cao |
Se added function property fullName to Person’s prototype object. We declare properties in the prototype object because all instances share it. This saves memory. Whereas if we were to declare it in the function constructor, then every instance would have that copy. If we were to have 1000 instances, then we’d get 1000 fullName functions. This would take up too much memory.
Dangers of ‘new’ and Function
Sometimes, we forget to use new and just use the function instead, like so:
1 2 3 4 5 6 7 8 9 10 11 |
function Person(firstN, lastN) { this.firstName = firstN; this.lastName = lastN; } Person.prototype.fullName = function() { return this.firstName + ', ' + this.lastName; } let me = Person('ricky', 'Cao'); console.log(me.fullName()); // ERROR |
Under this situation, me is undefined because we ran Person as a function.
Thus, we’ll get an error.
What happens when ‘new’ is used
1. New {} is created
2. function is called, this = {}, properties get attached to {}
3. {} linked to prototype via _ _ proto _ _ property. It now points to the prototype object.
4. function automatically return {}