In previous sections, we’ve studied how to use JavaScript objects to model complicated data and how to declare functions that can be used as function objects, which can be stored in variable. By combining these knowledge, we can create an object in the sense of Object Oriented Programming: a object with state and behavior.
To recap, a JavaScript object is a group of properties. Each property has a name and a value. Previously we create a single object by listing the properties of the object in { }
. This is known as object literal.
let circle = {
x: 2, y: 3, // x, y-coordinate of center of the circle
radius: 5,
fillColor: 'red'
};
A property can take ‘anything’ as value, including number, string, boolean, array, object, and even function. When we add a property with a function value, we’ve added a method to the object.
let circle = {
x: 2, y: 3,
radius: 5,
fillColor: 'red',
area: function () {
return Math.PI * this.radius ** 2
}
};
console.log(`Area of the circle is ${circle.area()}`)
You call a method of an object with the dot notation (similar to Java). circle.area
refers to the function object we defined for the area
property. You run this method by adding ( )
(possibly with parameters). In this example, it is circle.area()
.
In the body of a method, this
refers to the object on which the method is invoked. (Think ‘area of this circle’)
There is a shorthand notation to define methods in an object literal. In this example, we also define another method resize
.
let circle = {
x: 2, y: 3,
radius: 5,
fillColor: 'red',
area() {
return Math.PI * this.radius ** 2;
},
resize(factor) {
this.radius = this.radius * factor;
}
};
circle.resize(2);
console.log(`Area of the circle is ${circle.area()}`);
Here is a reminder about writing methods. Never use arrow functions as methods.
let circle_with_incorrect_method = {
x: 2, y: 3,
radius: 5,
// arrow functions CANNOT work as methods
area: () => Math.PI * this.radius ** 2
};
circle_with_incorrect_method.area() // returns NaN
The reason that the above example does not work is that when an arrow function is called, it does not set this
to refer to the object on which the method is invoked. In this case, this
does not point to circle
, and this.radius
does not exist (or, is undefined
).
Technical note: In this example,
this
refers to the ‘global object’, which iswindow
in browsers, orglobalThis
in general.this.radius
would refer to a global variable namedradius
, if it exists.So, how does an arrow function obtain
this
when invoked? It obtains from the surrounding (usually an outer function). Read this reference for an example.
You might notice that object literals include some methods not defined by you. For example, point.toString()
is called when JavaScript tries to concatenate a string with the object.
let point = { x: 2, y: 3 };
console.log('This point is ' + point);
The method .toString()
is actually inherited from the prototype of the object. But this version of toString
is not very informative. You can define a more useful version of toString
by redefining it in point
.
let point = {
x: 2,
y: 3,
toString() { return `(${this.x},${this.y})` }
};
console.log(`This point is ${point}`);
JavaScript’s use of prototype to implement inheritance is different from that of Java and Python. You can learn more about that in this reference.
Add a method quadrant()
to the object literal point
. The method returns the quadrant in which the point resides. (sample answer)
let point = {
x: -2,
y: 3,
toString() { return `(${this.x},${this.y})` },
quadrant() { /* your work */ }
};
// prints "The point (-2,3) is in quadrant II"
console.log(`The point ${point} is in quadrant ${point.quadrant()}`);
Write an object literal that implements the queue data structure. Use the following template to write your solution. (sample answer)
let q = {
// data properties to hold the content of the queue
// ...
enqueue(elem) {
// add 'elem' at the end of queue
},
dequeue() {
// remove the element at the head of queue, and return the element
},
count() {
// returns the number of elements in the queue
},
toString() {
// return the content of the queue as string
}
};
q.enqueue('a'); q.enqueue('b'); q.enqueue('c');
console.log( q.dequeue() ); // print 'a'
console.log( q.count() ); // print 2
q.enqueue('d');
console.log(`The queue contains ${q}.`);