JavaScript OOP Course 2: Objects Recap
References
- Code with Mosh: The Ultimate JavaScript Mastery Series – Part 2
- W3School: JavaScript Tutorial
See also:
Object Literals
const circle = {};
The Objects are collections of key:
value
pears.
const circle = {
radius: 1, // Property, Number
location: { // Property, Object Literals
x: 1, // Property, Number
y: 1 // Property, Bumber
},
draw: function() { // Method, a Function
console.log('draw');
}
};
The circle
object above has 3 members: 2 properties (radius
and location
) and 1 method (draw
). The properties are used to hold values. The methods are used to hold logic. When an Object has one or more methods we can say it has behavior.
The syntax of method definition could be simplified by removing the function keyword and the colon delimiter.
const circle = {
radius: 1, // Property, Number
location: { // Property, Object Literals
x: 1, // Property, Number
y: 1 // Property, Bumber
},
draw() { // Method, a Function
console.log('draw');
}
};
console.log(circle);
{radius: 1, location: {…}, draw: ƒ}
Factories
The Factory functions produce Objects. In the above example is used ES6 feature where when the names of the key and the variable that will produce the value are identical, instead of property: property,
we can write just property
, within the object returned by the factory function.
function createCircle(radius, location) {
return {
radius,
location,
draw() {
console.log('draw');
}
}
}
const circle1 = createCircle(1, {x: 1, y: 1});
const circle2 = createCircle(2, {x: 1, y: 1});
console.log(circle1, circle2);
{radius: 1, location: {…}, draw: ƒ}
{radius: 2, location: {…}, draw: ƒ}
Constructors
Constructor functions are another way to create an object. Unlike the Factory functions which return an object itself, the Constructor functions are used with the new
operator that creates the new object.
function Circle(radius, location) {
this.radius = radius;
this.location = location;
this.draw = function() {
console.log('draw');
}
}
const circle1 = new Circle(1, {x: 1, y: 1});
const circle2 = new Circle(1, {x: 1, y: 1});
console.log(circle1, circle2);
Circle {radius: 1, location: {…}, draw: ƒ}
Circle {radius: 2, location: {…}, draw: ƒ}
The Constructor functions are the base for proper understanding the ES6 Classes. The ES6 Classes will be discussed in a separate chapter, but let's see how the Class equivalent to the above Constructor function looks like.
function Circle(radius, location) {
this.radius = radius;
this.location = location;
this.draw = function() {
console.log('draw');
}
}
const circle1 = new Circle(1, {x: 1, y: 1});
const circle2 = new Circle(1, {x: 1, y: 1});
console.log(circle1, circle2);
Circle {radius: 1, location: {…}, draw: ƒ}
Circle {radius: 2, location: {…}, draw: ƒ}
Constructor Property
Every object in JavaScript has a property called Constructor. It references to the function that was used to construct (or create) the object.
Object created by ES6 Class
class Circle {
constructor(radius) {
this.radius = radius;
}
}
const circle = new Circle(1);
circle.constructor;
class Circle {
constructor(radius) {
this.radius = radius;
}
}
Object created by Constructor function
function Circle(radius) {
this.radius = radius;
}
const circle = new Circle(1);
circle.constructor;
ƒ Circle(radius) {
this.radius = radius;
}
Object created by Factory function
function createCircle(radius) {
return { radius }
}
const circle = createCircle(1);
circle.constructor;
ƒ Object() { [native code] }
Object created by Object Literals
const circle = { radius: 1 };
circle.constructor;
ƒ Object() { [native code] }
Built-in Constructor functions in JavaScript
ƒ Object()
=>new Object();
{};
ƒ String()
=>new String('string');
'string';
"string";
`string`;
ƒ Boolean()
=>new Boolean(true|false);
true;
false;
ƒ Number()
=>new Number(1);
1;
2;
ƒ Function()
=>new Function('arguments', `function content`)
function name(arguments) { function content };
ƒ Error()
=>throw new Errot('the error message');
- etc.
Functions are Objects
The Functions in JavaScript are objects. And they have built-in methods and properties.
function Circle(radius) {
this.radius = radius;
}
Circle.name;
'Circle'
Circle.length; // return the number of the arguments
1
Circle.constructor
ƒ Function() { [native code] }
There is Constructor function, called Function()
, that creates the functions when we define them in literals. So when we define a function in the following way.
function Circle(radius) {
this.radius = radius;
}
In the background the JavaScript engine does something like this.
const Circle = new Function('radius', `this.radius = radius;`);
const circle = new Circle(1);
console.log(circle);
{radius: 1}
Function's Methods – .call()
and .apply()
- See also: JavaScript Functions: Changing
this
function Circle(radius) {
this.radius = radius;
}
Circle.call({}, 1); // This expression is equal to 'new Circle(1)'
Circle.apply({}, [1]); // This expression is equal to 'new Circle(1)'
Value vs Reference Types
- See also: JavaScript Basics: Types
- Value types (Primitives) are copied by value, Reference types (Objects) are copied by a reference.
The Value types (Primitives) in JavaScript are:
- Strings
- Numbers
- Booleans
- undefined
- null
- Symbol (ES6)
When we work with primitives (value types) the value is stored inside the variable. This the primitives become independent.
let x = 10;
let y = x;
x = 20;
console.log(x, y); // note the two variables will have different value
20 10
The Reference types in JavaScript are – at all in JavaScript all Reference types are Objects:
- Objects
- Arrays
- Functions
When we work with objects (reference types) the value is stored into an internal JavaScript object and the value of the object's variable is just a reference to that internal object.
let x = {value: 10};
let y = x;
x.value = 20;
console.log(x.value, y.value); // note the two 'variables' will have the same value
20 20
When we copy x
to y
the reference is copy not the value, thus the two variables (x
and y
) referencing to the same JavaScript object in the memory.
Another Value vs Reference Types example
let number = 10;
function increase(number) {
number++; // this 'number' Variable is complete independant of the number varible defined outside
} // So when we pass the 'number' variable as argument to the function its value
increase(number); // is coppied to the internal variable. So the value of the global variable 'number'
console.log(number); // is not touched by the function and we will see 10 on the console
10
let obj = { value: 10};
function increase(obj) {
obj.value++; // this 'obj' Object will have the same reference
} // as the Object passed to the function as argument
increase(obj); // So the value of the global Object 'obj'
console.log(obj); // will be changed by the function and we will see 11 on the console
{value: 11}
Adding or Removing Properties – DOT and BRACKET Notation
The objects in JavaScript are dynamic, that means after creating them we can add properties (or methods) to them or delete some properties.
function Circle(radius) {
this.radius = radius;
this.draw = function() {
console.log('draw');
};
}
const circle = new Circle(1);
Add a property via DOT notation
circle.location = {x: 1, y: 1};
consloe.log(circle);
Circle {radius: 1, location: {x: 1, y: 1}, draw: ƒ}
Add a property via BRACKET notation
circle['visible'] = true;
consloe.log(circle);
Circle {radius: 1, location: {x: 1, y: 1}, visible: true, draw: ƒ}
The Bracket notation is useful in some cases, especially when we pass the name of the property as value of a variable:
const propertyName = 'location';
console.log(circle[propertyName]);
{x: 1, y: 1}
Another useful application of the Bracket notation is when we have special characters in the property name.
circle['center-location'] = {x: 1, y: 1};
console.log(circle['center-location']);
{x: 1, y: 1}
Delete a property of an Object
delete circle['center-location'];
delete circle.location;
Enumerating Properties
function Circle(radius) {
this.radius = radius;
this.draw = function() {
console.log('draw');
};
}
const circle = new Circle(10);
For iterating over the properties of an Object we can use the for.. in..
loop that will loop over the Properties and the Methods of the object.
for (let key in circle) {
console.log(key, circle[key]);
// | |-> Return the value of the key
// |-> Retyrn the key name
}
radius 10
draw ƒ () { console.log('draw'); }
In order to get only the Properties but not the Methods we can use the typeof
operator within an if
statement.
for (let key in circle) {
if (typeof circle[key] !== 'function')
console.log(key, circle[key]);
}
radius 10
In order to get all the keys of an Object as an Array we can use the method Object.keys(obj)
. With this approach we cannot separate the Properties and the Methods.
const keys = Object.keys(circle);
console.log(keys);
(2) ['radius', 'draw']
In order to test whether an Object has a certain Property we can use if.. in..
statement as follow.
if ('radius' in circle)
console.log('Circle has a radius.');
Circle has a radius.
Abstraction – Private Properties and Methods
Abstraction means: Hide the details and complexity and show (or expose) only the essentials.
function Circle(radius) {
this.radius = radius;
this.defaultLocation = {x: 0, y: 0};
this.computeOptimumLocation = function(factor) {
console.log('compute', factor);
};
this.draw = function() {
this.computeOptimumLocation(0.1);
// this.defaultLocation ...
// this.radius ...
console.log('draw');
};
}
const circle = new Circle(10);
In order to apply Abstraction and hide the defaultLocation
property and computeOptimumLocation
method we must rewrite our Constructor function in the following way where these members are defined as local variables (within the constrictor function's closure).
function Circle(radius) {
this.radius = radius;
let defaultLocation = {x: 0, y: 0};
let computeOptimumLocation = function(factor) {
console.log('compute', factor);
};
this.draw = function() {
computeOptimumLocation(0.1);
// defaultLocation ...
// this.radius ...
console.log('draw');
};
}
const circle = new Circle(10);
Getters and Setters
- See also: JavaScript Functions: Getter and Setters
Here is how to get access to a private members of an object.
function Circle(radius) {
this.radius = radius;
let defaultLocation = {x: 0, y: 0};
this.getDefaultLocation = function() {
return defaultLocation;
};
}
const circle = new Circle(10);
console.log(circle.defaultLocation());
{x: 0, y: 0}
In order to access the defaultLocation
as property (not like a method as in the example above) we need to use Getter function. Also we can us a Setter function in order to set values to a private properties. Here is how this could be implemented within our Constructor function.
function Circle(radius) {
this.radius = radius;
let defaultLocation = {x: 0, y: 0};
Object.defineProperty(this, 'defaultLocation', {
get: function() {
return defaultLocation;
},
set: function(value) {
if (!value.x || !value.y)
throw new Error('Invalid location property');
defaultLocation = value;
}
});
}
const circle = new Circle(10);
console.log(circle.defaultLocation);
{x: 0, y: 0}
circle.defaultLocation = {x: 5, y: 6};
console.log(circle.defaultLocation);
{x: 5, y: 6}