Custom Type
All about Custom Types in Javascript
Before class was introduced to Javascript, programmers had to use Custom Type in order to achieve Object Oriented Programming (OPP).
This concept will allow us to go deeply on the fundamentals of Objects and understand how Objects are created and linked to each other.
Object Type
In JavaScript, functions are objects.
Do not confuse: many of the built-in constructors, like Object, Array, and Function, are actually functions. Thus Object is type of 'function'
function greet(name) {
return `Hello, ${name}`;
}
typeof greet // 'Object'
typeof Object // 'function'The Object (even though it is a function) can have several properties:
Object.assign
Object.create
Object.hasOwn
Object.freeze
Object.seal
...moreAnd every Object has prototype properties:
Object.prototype.__defineGetter__;
Object.prototype.__defineSetter__;
Object.prototype.constructor();
Object.prototype.hasOwnProperty();
Object.prototype.isProtoTypeOf();
...moreHow to create an Object
Let's analyze how the creation of a Object looks under the hood.

- The builder of all Objects (already created by default)
- Creates the
prototypeobject - Has a property called
prototypethat points to aObject.prototype
Allows to create a new Object based on the constructor function
let object1 = new Object();
let object2 = new Object();- The new
object1is created with a property:__proto__ __proto__object points to its parentObject.prototype- All new Objects created share the same
Object.prototype
It's the parent of all Objects. There is only one Object.prototype (already created).
All new Objects created should point to this. This has all the default properties of an object, which can be overriden by a new Object (allowed by Inheritance!!)
Note that also it has a property called __proto__ set to null
Useful Object's Methods
Assume the Prototype Chain (LinkedList):
Object.create(obj)
Allows to create a child Object from a parent Object
let cheesecake = Object.create(dessert);Object.getOwnPropertyNames(obj)
Returns all properties of the object passed, as string[]
Object.getOwnPropertyNames(cheesecake)
// [constructor, name, calories, minCalories, displayDessert, ...]
Object.getOwnPropertyNames(dessert)
// [constructor, minCalories, displayDessert, showDessert, ...]
Object.getOwnPropertyNames(Object.prototype)
// [constructor __defineGetter__, __defineSetter__, hasOwnProperty, ...]Object.getPrototypeOf(obj)
Return the Prototype of the object passed
Object.getPrototypeOf(cheesecake) // returns dessert
Object.getPrototypeOf(dessert) // returns Object.prototypeObject.isPrototypeOf(obj)
dessert.isPrototypeOf(cheesecake) // True
Object.prototype.isPrototypeOf(dessert) // True
cheesecake.isPrototypeOf(Object.prototype) // FalseChain of Properties
Now, let's discuss how chain of properties works in Objects, which is kinda weird but the diagram and example will help.
Notice that all these new Objects created share the same __proto__. Thus, they all share the same properties from Object.prototype.
Assume a new object3 points to another object1, which points to Object.prototype.
We want to access the property toString, which is not declared in any of the new objects created.
To search toString property, traversing is done from bottom (child) to up (parent). The Object.prototype is the parent of all objects, which is the end of the traverse (end of the chain)

Here is the code.
function findProperty(src, toString) {
let currObj, answer;
/* Traversing the prototype chain */
currObj = src;
while (currObj !== null) {
let currObjProperties = Object.getOwnPropertyNames(currObj);
let found = currObjProperties.find(elem => elem === toString);
if (found) {
answer = found
return answer;
}
currObj = Object.getPrototypeOf(currObj); // go parent object
}
return answer; // if not found, return undefined
}Function Properties
arguments.length
Returns the number of arguments passed to the function.
function generateResults(x,y) {
console.log(arguments.length)
console.log(arguments[0]);
console.log(arguments[1]);
}
generateResults(); // Output: 0, undefined, undefined
generateResults("Hello"); // Output: 1, "Hello", undefined
generateResults(1,2); // Output: 2, 1, 2function.length
Returns the number of parameters that the function is expected to take.
function generateResults(x) {
console.log(x);
}
generateResults.length // 1
function moreResults(x,y) {
console.log(x, y);
}
moreResults.length // 2function.valueOf()
Returns the function in plain text
generageResults.valueOf()
// -> 'function generateResults(x) { console.log(x); }'this
In Java, this references the current Object we are operating. this is fixed (does not change)
In Javascript, this also references the current Object we are operating, but this changes where is pointing to depending on the scenerio.
Note:
By default this is referencing to window
Allows associating functions to an object at runtime
Can set this using apply(), call(), or bind()
window.singer = "Lionel"
function printInfo() {
console.log(this.singer);
// this -> the context where this function will work
}
printInfo() // "Lionel"
// this -> references to window
let obj = new Object();
obj.singer = "Elvis"
obj.singerInfo = printInfo()
obj.singerInfo() // "Elvis"
// this -> references to object
apply()
Allows to specify to the function what this is refering to
function.apply(obj , args)Example
let obj = new Object()
obj.terpConstant = 20
window.terpConstant = 10
function product(x,y){ return x * y * this.terpConstant }
product(2,5) // `this` referes to `window` // 2*5*10 = 100
product.apply(this, 3, 3) // `this` refers to `window` // 3*3*10 = 90
product.apply(obj, 3, 3) // `this` refers to obj // 3*3*20 = 180call()
Similar as .apply(), useful for Inheritance to populate superclass/parent-class
function.call(object, args)Example
product.call(this, 3, 3) // `this` refers to `window` // 3*3*10 = 90
product.call(obj, 3, 3) // `this` refers to obj // 3*3*20 = 180bind()
The bind() method creates a new function that, when called, has its this keyword set to the first arugment. The args allows partially apply arguments, similar to currying functionality.
function.bind(object, args)Example
const obj = {name: 'Jose'};
function greet(greeting, punctuation) {
console.log(greeting + this.name + punctuation);
}
const boundGreet = greet.bind(obj, "Hello, ");
boundGreet("!"); // -> "Hello, Jose!"Function Class without class keyword 🔨
A function can also work as a Class. By convention, a function class name starts with Capital Letter.
Warning:
Doing in this way, if we were creating 50 instances of Computer, then we were creating 50 instances of getMemory and setMemory functions.
So this method is inefficient. Can we do better? yes, Sharing Prototypes
// constructor function / Custom Type for Computer
function Computer(model, memory) {
this.model=model;
this.memory=memory;
// DO NOT USE LAMBDA WITH 'THIS'
this.getMemory = function(){ return this.memory }
this.setMemory = function(newMemory){ this.memory=newMemory }
}
Computer("mac", 100); // properties are set to 'window' object
// Create an object from constructor function
let pc = new Computer("PC", 30); // properties are set to 'pc' object
pc.setMemory(100); // property set to 'pc' objectSharing Prototypes 🤝
We can set these instance functions to the parent Object.prototype, so that all new objects created shares the same common function, without repeately creating one each time a new object is created.
Note:
Only set to .prototypes properties that share in common between all objects. If want to set a property that is unique between all objects, set it into function constructor
/* function constructo / Custom Type for Car */
function Car(model, year) {
this.model=model;
this.year=year;
}
// Car.prototype.model = "ford" // DO NOT DO THIS
// Car.prototype.year = "2020" // DO NOT DO THIS
Car.prototype.getYear = function(){ return this.year }
Car.prototype.setYear = function(newYear){ this.year=newYear }
How atually defining a Function Class
A long story just to get here. Defining a function class, the right way!, without class keyword
// Constructor function / Custom Type for Student
function Student(name, credits, courses){
this.name=name;
this.credits=credits;
this.courses=[...courses];
}
// Set Common Properties for all Objects / Static members / Class variables
Student.prototype = {
constructor: Student, // it says: I belong to this constructor
college: "UMCP",
info: function() {
document.writeln(`
${this.name}, ${this.credits}, ${this.courses}, ${this.college} `)
}
};
// Create Unique Students
let student1 = new Student("Kelly", 15, [414,420]);
let student2 = new Student("Jose", 42, [414,420]);
let student3 = new Student("Kevin", 1, [414,420]);
let student4 = new Student("Martin", 60, [414,420]);
Function Inheritance
To understand Inheritance, imagine two custom type, one is a subset of another. All common properties of a Student are shared with GradStudents, such as:
- ID
- Name
- Credits
But GradStudents may have properties that doesn't have all Students
- Office Room
- Office Hours
- Bachelor's degree
- Thus,
GradStudentis a subset ofStudentset.
/*------ Grad Class / Custom Type for Grad ------*/
function GradStudent(name, credits, courses, advisor) {
// Calls super class constructor
Student.call(this, name, credits, courses);
// This says: call Student Class, this = GradStudent, pass arguments
this.advisor = advisor;
}
// Set Static members for all instances GradStudent
GradStudent.prototype = new Student();
GradStudent.prototype.constructor = GradStudent;
GradStudent.prototype.getAdvisor = function() { return this.advisor; }
// Create Unique Grads
let graduateStudent1 = new GradStudent("Kelly", 15, [414, 420], "Dr. Smith");
let graduateStudent2 = new GradStudent("Wiley", 15, [631, 632], "Dr. Will");