For some while now we have been creating new objects using the new keyword, then ES5 came along with Object.create(), then ES6 brought us classes, and finally we have a much easier solution.
The new keyword
Using the new
keyword is how many people first learn about constructing objects in JavaScript.
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
this.getDisplayName = function () {
return this.firstName + " " + this.lastName;
};
}
var paul = new Person("Paul", "Wilkins");
console.log(paul.getDisplayName()); // Paul Wilkins
Then, the new
keyword was found to be problematic because it disguised how prototypes work, and people from other languages didnât really need to learn how JavaScript works when it came to the prototype.
About the most that they learned was that the getDisplayName()
method could be acquired using inheritance over the prototype chain:
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
Person.prototype.getDisplayName = function () {
return this.firstName + " " + this.lastName;
};
Later on though when Person is inherited by Employee, you need to tell Employee that itâs prototype was Person, and then also to fix the constructor back to what it should have been.
function Employee(firstName, lastName, employeeId, hourlyRate) {
this.employeeId = employeeId;
this.hourlyRate = hourlyRate;
return Person.call(this, firstName, lastName);
}
Employee.prototype = new Person();
Employee.prototype.constructor = Employee;
var paul = new Employee("Paul", "Wilkins", 1001, 120);
console.log(paul.getDisplayName()); // Paul Wilkins
Due these prototype and constructor issues, using the new
keyword was all becoming quite a mess.
ES5 Object.create()
With ES5 came Object.create(), which was added by the request of Douglas Crockford to fix those prototype issues.
var person = {
firstName: "",
lastName: "",
getDisplayName: function () {
return this.firstName + " " + this.lastName;
}
};
var employee = Object.create(person, {
business: {value: "Disk Daemon Services"}
});
var paul = Object.create(employee);
The prototypes all worked well with Object.create()
, but it came at the expense of how easy it was to create each object which required several statements to create an employee.
var paul = Object.create(employee);
paul.firstName = "Paul";
paul.lastName = "Wilkins";
paul.employeeId = 1001;
paul.hourlyWage = 120;
console.log(paul.getDisplayName()); // Paul Wilkins
It didnât take long though for Crockford to stop using Object.create()
, but it had nothing to do with the method itself. Instead, security investigations with adverts found that the this
keyword along with other techniques are unsafe, and so under the ADSafe scheme the this
keyword was banned.
ES6 Classes
Next came ES6 with the class syntax, which is more familiar to programmers coming from other languages and is widely said to be âjust sugarâ that implements what is already in JavaScript.
class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
getDisplayName() {
return this.firstName + " " + this.lastName;
}
}
class Employee extends Person {
constructor(firstName, lastName, employeeId, hourlyWage) {
super(firstName, lastName);
this.employeeId = employeeId;
this.hourlyWage = hourlyWage;
}
}
var paul = new Employee("Paul", "Wilkins", 1001, 120);
console.log(paul.getDisplayName()); // Paul Wilkins
One of the problems with the class structure is that it hasnât fixed any of the security dangers with the this keyword, and people using classes arenât likely to learn about other potentially better aspects of JavaScript.
Simplifying object creation
Classes are a reasonable solution for memory conservation when youâre making hundreds of thousands of objects, but most of the time you wonât be, and there is still the danger of the this
keyword.
We can do away with the this
keyword entirely and use simple objects instead when creating objects.
function makePerson(spec) {
function getDisplayName() {
return spec.firstName + " " + spec.lastName;
}
return Object.freeze({
getDisplayName
});
}
var paul = makePerson(firstName: "Paul", lastName: "Wilkins");
console.log(paul.getDisplayName()); // Paul Wilkins
All functions inside of makePerson are private methods and can easily access the spec
info.
The only strange thing here is Object.freeze()
, where we use it to protect the public methods from being changed or replaced.
Separate data within the constructor is also easily dealt with, using a state
object, and we now have better control over whether information can be accessed. For example, the firstName
and lastName
arenât visible now.
function makeEmployee(spec) {
let {employeeId, hourlyWage} = spec;
let {getDisplayName} = makePerson(spec);
function updateWage(wage) {
hourlyWage = wage;
}
return Object.freeze({
business: "Disk Daemon Services",
getDisplayName,
updateWage
});
}
var paul = makeEmployee({
firstName: "Paul",
lastName: "Wilkins",
employeeId: 1001,
hourlyWage: 120
});
console.log(paul.getDisplayName()); // Paul Wilkins
This type of object construction also favors composition over inheritance, which is something has been recommended to us programmers now for many years.
Some good coverage about this classless type of Object construction is found in the following article: Arguably the best approach to create objects in JavaScript.