Notes Javascript - Objects
Objects
JavaScript is designed on a simple object-based paradigm. An object is a collection of properties, and a property is an association between a name (or key) and a value. A property's value can be a function, in which case the property is known as a method.
Objects in JavaScript, just as in many other programming languages, can be compared to objects in real life. In JavaScript, an object is a standalone entity, with properties and type. Compare it with a cup, for example. A cup is an object, with properties. A cup has a color, a design, weight, a material it is made of, etc. The same way, JavaScript objects can have properties, which define their characteristics.
In addition to objects that are predefined in the browser, you can define your own objects.
Objects vs. JSON
A Javascript Object is a code object. Syntax-wise they look almost identical, but there is a subtle difference. JSON requires all identifiers to be wrapped in double quotes. JSON is meant to be used to store structured data and allow that structured data to be transmited just like XML and YAML. Objects are a programming construct. They are meant to be used in code.
A Simple Analogy
Imagine you have purchased furniture from a store, and you want it delivered. However the only one left in stock is the display model, but you agree to buy it.
In the shop, the chest-of-drawers you've purchased is a living object:
var chestOfDrawers = { color: "red", numberOfDrawers: 4 }
However, you cannot send a chest-of-drawers in the post,
so you dismantle it (read, stringify it).
It is now useless in terms of furniture.
It is now JSON. It is in flat pack form.
Note: notice that the identifiers color and numberOfDrawers, in the JSON code below, are now wrapped in double quotes.
{"color":"red","numberOfDrawers":4}
When you receive it, you then rebuild the chest-of-drawers (read, parse it). It is now back in object form.
Creating New Objects
You can create an object using an object initializer. Alternatively, you can first create a constructor function and then instantiate an object by invoking that function with the new operator.
Using Object Initializers
Object initializers are also called object literals. "Object initializer" is consistent with the terminology used by C++.
The syntax for an object using an object initializer is:
const obj = { property1: value1, // PROPERTY NAME MAY BE AN IDENTIFIER 2: value2, // OR A NUMBER "property n": value3, // OR A STRING };
let x; if (cond) { x = { greeting: "hi there" }; }
const myHonda = { color: "red", wheels: 4, engine: { cylinders: 4, size: 2.2 }, };
function Car(make, model, year) { this.make = make; this.model = model; this.year = year; } const myCar = new Car("Eagle", "Talon TSi", 1993); const kenscar = new Car("Nissan", "300ZX", 1992); const vpgscar = new Car("Mazda", "Miata", 1990); const rand = new Person("Rand McKinnon", 33, "M"); const ken = new Person("Ken Jones", 39, "M");
function Person(name, age, sex) { this.name = name; this.age = age; this.sex = sex; }
function Car(make, model, year, owner) { this.make = make; this.model = model; this.year = year; this.owner = owner; } const car1 = new Car("Eagle", "Talon TSi", 1993, rand); const car2 = new Car("Nissan", "300ZX", 1992, ken); car2.owner.name; car1.color = "black";
// Animal PROPERTIES AND METHOD ENCAPSULATION const Animal = { type: "Invertebrates", // DEFAULT VALUE OF PROPERTIES displayType() { // METHOD WHICH WILL DISPLAY TYPE OF Animal console.log(this.type); }, }; // CREATE NEW ANIMAL TYPE CALLED animal1 const animal1 = Object.create(Animal); animal1.displayType(); // Logs: Invertebrates // CREATE NEW ANIMAL TYPE CALLED fish const fish = Object.create(Animal); fish.type = "Fishes"; fish.displayType(); // Logs: Fishes
// DOT NOTATION myCar.make = "Ford"; myCar.model = "Mustang"; myCar.year = 1969; // BRACKET NOTATION myCar["make"] = "Ford"; myCar["model"] = "Mustang"; myCar["year"] = 1969;
Object Property Names
An object property name can be any JavaScript string or symbol, including an empty string. However, you cannot use dot notation to access a property whose name is not a valid JavaScript identifier. For example, a property name that has a space or a hyphen, that starts with a number, or that is held inside a variable can only be accessed using the bracket notation. This notation is also very useful when property names are to be dynamically determined, i.e. not determinable until runtime. Examples are as follows:
const myObj = {}; const str = "myString"; const rand = Math.random(); const anotherObj = {}; // CREATE ADDITIONAL PROPERTIES ON myObj myObj.type = "Dot syntax for a key named type"; myObj["date created"] = "This key has a space"; myObj[str] = "This key is in variable str"; myObj[rand] = "A random number is the key here"; myObj[anotherObj] = "This key is object anotherObj"; myObj[""] = "This key is an empty string"; console.log(myObj); // { // type: 'Dot syntax for a key named type', // 'date created': 'This key has a space', // myString: 'This key is in variable str', // '0.6398914448618778': 'A random number is the key here', // '[object Object]': 'This key is object anotherObj', // '': 'This key is an empty string' // } console.log(myObj.myString); // 'This key is in variable str'
In the above code, the key anotherObj is an object, which is neither a string nor a symbol. When it is added to the myObj, JavaScript calls the toString() method of anotherObj, and use the resulting string as the new key.
You can also access properties with a string value stored in a variable. The variable must be passed in bracket notation. In the example above, the variable str held "myString" and it is "myString" that is the property name. Therefore, myObj.str will return as undefined.
str = "myString"; myObj[str] = "This key is in variable str"; console.log(myObj.str); // undefined console.log(myObj[str]); // 'This key is in variable str' console.log(myObj.myString); // 'This key is in variable str'
This allows accessing any property as determined at runtime:
// ACCESS DIFFERENT PROPERTIES BY CHANGING THE CONTENTS OF THE VARIABLE let propertyName = ""; propertyName = "make"; myCar[propertyName] = "Ford"; propertyName = "model"; myCar[propertyName] = "Mustang"; console.log(myCar); // { make: 'Ford', model: 'Mustang' }
Beware of Bracket Notation Injection Pitfalls
However, beware of using square brackets to access properties whose names are given by external input.
This may make your code susceptible to object injection attacks.
Nonexistent properties of an object have value undefined (and not null).
myCar.nonexistentProperty; // UNDEFINED
Enumerating Properties
There are three native ways to list/traverse object properties:
- for...in loops. This method traverses all of the enumerable string properties of an object as well as its prototype chain.
- Object.keys(myObj). This method returns an array with only the enumerable own string property names ("keys") in the object myObj, but not those in the prototype chain.
- Object.getOwnPropertyNames(myObj). This method returns an array containing all the own string property names in the object myObj, regardless of if they are enumerable or not.
You can use the bracket notation with for...in to iterate over all the enumerable properties of an object. To illustrate how this works, the following function displays the properties of the object when you pass the object and the object's name as arguments to the function:
function showProps(obj, objName) { let result = ""; for (const i in obj) { // Object.hasOwn() IS USED TO EXCLUDE PROPERTIES FROM THE OBJECT'S // PROTOTYPE CHAIN AND ONLY SHOW "own properties" if (Object.hasOwn(obj, i)) { result += `${objName}.${i} = ${obj[i]}\n`; } } console.log(result); }
The term "own property" refers to the properties of the object, but excluding those of the prototype chain. So, the function call showProps(myCar, 'myCar') would print the following:
myCar.make = Ford myCar.model = Mustang myCar.year = 1969
The For-In code above is equivalent to:
function showProps(obj, objName) { let result = ""; Object.keys(obj).forEach((i) => { result += `${objName}.${i} = ${obj[i]}\n`; }); console.log(result); }
Inheritance
All objects in JavaScript inherit from at least one other object. The object being inherited from is known as the prototype, and the inherited properties can be found in the prototype object of the constructor.
Defining Properties For All Objects Of One Type
You can add a property to all objects created through a certain constructor using the prototype property. This defines a property that is shared by all objects of the specified type, rather than by just one instance of the object. The following code adds a color property to all objects of type Car, and then reads the property's value from an instance car1.
Car.prototype.color = "red"; console.log(car1.color); // "red"
Defining Methods
A method is a function associated with an object, or, put differently, a method is a property of an object that is a function. Methods are defined the way normal functions are defined, except that they have to be assigned as the property of an object. See also method definitions for more details. An example is:
objectName.methodName = functionName; const myObj = { myMethod: function (params) { // DO SOMETHING }, // THIS WORKS TOO! myOtherMethod(params) { // DO SOMETHING ELSE }, };
where objectName is an existing object, methodName is the name you are assigning to the method, and functionName is the name of the function.
You can then call the method in the context of the object as follows:
objectName.methodName(params);
Methods are typically defined on the prototype object of the constructor, so that all objects of the same type share the same method. For example, you can define a function that formats and displays the properties of the previously-defined Car objects.
Method Type | Description |
---|---|
Constructor Method | Method code will be added to every instance of the object. |
Prototype Method | All instances of the object will share the method. |
Car.prototype.displayCar = function () { const result = `A Beautiful ${this.year} ${this.make} ${this.model}`; console.log(result); };
Notice the use of this to refer to the object to which the method belongs. Then you can call the displayCar method for each of the objects as follows:
car1.displayCar(); car2.displayCar();
Using this For Object References
JavaScript has a special keyword, this, that you can use within a method to refer to the current object. For example, suppose you have 2 objects, Manager and Intern. Each object has its own name, age and job. In the function sayHi(), notice the use of this.name. When added to the 2 objects, the same function will print the message with the name of the respective object it's attached to.
const Manager = { name: "Karina", age: 27, job: "Software Engineer", }; const Intern = { name: "Tyrone", age: 21, job: "Software Engineer Intern", }; function sayHi() { console.log(`Hello, my name is ${this.name}`); } // add sayHi function to both objects Manager.sayHi = sayHi; Intern.sayHi = sayHi; Manager.sayHi(); // Hello, my name is Karina Intern.sayHi(); // Hello, my name is Tyrone
this is a "hidden parameter" of a function call that's passed in by specifying the object before the function that was called. For example, in Manager.sayHi(), this is the Manager object, because Manager comes before the function sayHi(). If you access the same function from another object, this will change as well. If you use other methods to call the function, like Function.prototype.call() or Reflect.apply(), you can explicitly pass the value of this as an argument.
Defining Getters & Setters
A getter is a function associated with a property that gets the value of a specific property. A setter is a function associated with a property that sets the value of a specific property. Together, they can indirectly represent the value of a property.
Getters & Setters Can Be Either
- defined within object initializers, or
- added later to any existing object.
Within object initializers, getters and setters are defined like regular methods, but prefixed with the keywords get or set. The getter method must not expect a parameter, while the setter method expects exactly one parameter (the new value to set). For instance:
const myObj = { a: 7, get b() { return this.a + 1; }, set c(x) { this.a = x / 2; }, }; console.log(myObj.a); // 7 console.log(myObj.b); // 8, returned from the get b() method myObj.c = 50; // Calls the set c(x) method console.log(myObj.a); // 25
The myObj object's properties are:
myObj.a — a number myObj.b — a getter that returns myObj.a plus 1 myObj.c — a setter that sets the value of myObj.a to half of the value myObj.c is being set to
Getters and setters can also be added to an object at any time after creation using the Object.defineProperties() method. This method's first parameter is the object on which you want to define the getter or setter. The second parameter is an object whose property names are the getter or setter names, and whose property values are objects for defining the getter or setter functions. Here's an example that defines the same getter and setter used in the previous example:
const myObj = { a: 0 }; Object.defineProperties(myObj, { b: { get() { return this.a + 1; }, }, c: { set(x) { this.a = x / 2; }, }, }); myObj.c = 10; // Runs the setter, which assigns 10 / 2 (5) to the 'a' property console.log(myObj.b); // Runs the getter, which yields a + 1 or 6
Which of the two forms to choose depends on your programming style and task at hand. If you can change the definition of the original object, you will probably define getters and setters through the original initializer. This form is more compact and natural. However, if you need to add getters and setters later — maybe because you did not write the particular object — then the second form is the only possible form. The second form better represents the dynamic nature of JavaScript, but it can make the code hard to read and understand.
Returning Objects From Methods
window.addEventListener("DOMContentLoaded", (event) => { let resultDiv = document.getElementById("results1"); let btnGetObj = document.getElementById("btnGetObj"); let returnObj = {}; btnGetObj.addEventListener("click", function() { returnObj = getObjInfo(); resultDiv.innerHTML = returnObj.firstName +" "+ returnObj.lastName +", "+ returnObj.jobTitle; },false); let getObjInfo = function() { let person = { firstName: "Dirk", lastName: "Harriman", jobTitle: "Lead Developer", }; return person; }; });