Understanding JavaScript Prototypes and ES6 Class Syntax
Defining Properties and Methods: this vs prototype
// Constructor function
function User(username, userAge) {
this.username = username;
this.userAge = userAge;
this.activityLog = [];
// Using this to define instance methods
this.displayAge = function() {
console.log(this.userAge);
};
}
// Adding methods to the prototype
User.prototype.getUsername = function() {
console.log(this.username);
};
User.prototype.roleList = ['admin'];
// When called with 'new', this acts as a constructor;
// without 'new', it functions as a regular function call
const currentUser = new User('Alice', 30);
const anotherUser = new User('Alice', 30);
console.log('Both prototype and this methods work correctly:');
currentUser.getUsername();
currentUser.displayAge();
anotherUser.getUsername();
anotherUser.displayAge();
console.log('Working with prototype-defined properties:');
currentUser.roleList.push('moderator');
console.log('currentUser.roleList:', currentUser.roleList);
console.log('anotherUser.roleList:', anotherUser.roleList);
console.log('Working with this-defined properties:');
currentUser.activityLog.push('login');
console.log('currentUser.activityLog:', currentUser.activityLog);
console.log('anotherUser.activityLog:', anotherUser.activityLog);
// The constructor property points back to the User function
console.log(User.prototype.constructor);
Output
Both prototype and this methods work correctly:
Alice
30
Alice
30
Working with prototype-defined properties:
currentUser.roleList [ 'admin', 'moderator' ]
anotherUser.roleList [ 'admin', 'moderator' ]
Working with this-defined properties:
currentUser.activityLog [ 'login' ]
anotherUser.activityLog []
[Function: User]
Properties and methods defined using this are created independently for each intsance during object instantiation. Each object receives its own copy of these members. In contrast, prototype-defined members are shared acrosss all instances, with a single implementation referenced by every object created from the constructor.
This distinction explains why developers typically use this for instance-specific properties while leveraging the prototype for shared methods, optimizing memory usage while maintaining instance-speciifc data.
How the new Operator Works
function User(username, userAge) {
this.username = username;
this.userAge = userAge;
}
User.prototype.getUsername = function() {
console.log(this.username);
};
const currentUser = new User('Alice', 30);
The new operator performs three key operations behind the scenes:
- Instance Creation: A new empty object is created, and its
__proto__property is linked toUser.prototype. - Initialization: The constructor function is invoked with the provided arguments, and
thisis bound to the newly created object. - Return: The initialized object is returned as the result.
Custom new Implementation:
function CustomNew(constructorFn) {
const instance = {
'__proto__': constructorFn.prototype
};
return function(...args) {
constructorFn.apply(instance, args);
return instance;
};
}
Prior to ES6, JavaScript developers used constructor functions combined with the prototype property to create reusable object patterns. The prototype mechanism enabled inheritance and method sharing before class-based syntax was introduced.
ES6 Class Syntax
//User.js
class User {
// Constructor method
constructor(firstName, yearsOld) {
this.firstName = firstName;
this.yearsOld = yearsOld;
}
// Class method
describe() {
return (this.firstName + " is " + this.yearsOld + " years old");
}
}
const person = new User('Michael', 25);
console.log(person.describe());