Fred
Brack Raleigh, NC |
This is not a full tutorial on JavaScript objects but rather an introduction to their basic features and how to manipulate them. Want more detail? Try Learn-JS, which helped me; and my favorite reference site is W3Schools. I created this web page to help me document my understanding of JavaScript objects as I progressed from novice to intermediate user ... and because I like to document stuff for others! I hope you find it useful.
In JavaScript, objects are king. If you understand
objects, you understand JavaScript.
- W3Schools
The W3Schools introduction to objects is good, so let me offer my modified version here. Think of a real-life object like a car. It has certain properties, like its model number, weight, or color; and it has what we can refer to as methods which would include things like starting and stopping. All cars have those properties, but their values are different. The methods are generally the same.
So to translate our basic understanding of an object to JavaScript, let's get coding! We can start with a so-called primitive value (not an object):
var car = "Mercedes";
We interrupt to note that in our discussion of ARRAYS, we identified our car model array as carModels. Here we want to make the clear distinction that we are creating an OBJECT, so we are changing the name to carObject. |
To give us more information about the car and generalize a structure for talking about other car models, we create a car object. It might look in part like this (but note that my use of the word Object in the object's name is just for clarity, and is not required):
var carObject = { brand:"Mercedes", model:"GLE350", color:"navy" };
The first difference we note is the use of brackets to define the object. Second we see that object properties are defined by naming them followed by a colon (not an equal sign). This is called named values. And the properties are separated by commas. Spacing and line breaks don't matter; and in fact, you are more likely to see an object defined this way:
var carObject = { brand:"Mercedes", model:"GLE350", color:"navy" };
So we have an object (carObject) with multiple properties (brand, model, and color), each one of which has an assigned value. You can access or set an object's properties by using either of two specifications:
carObject.model; // = GLE350 carObject["model"]; // = GLE350
Which method you use might depend on how you are determining which properties to access. For instance, if the property is dynamically declared in a variable, you would use the second method, replacing "model" with the variable name. (If you replaced "model" in the first example with a variable containing "model," then you would fail because the property name is assumed to be as-written in that format, not a variable itself. Try it yourself!) Another reason for choosing the second method is if your property name has a blank, as in "gross wt" for example.
You can also add a new named value this way:
carObject.year = 2020;
We should also point out that certain "things" in JavaScript are always objects: Dates, Arrays, Functions, Maths (like Math.round), and regular expressions (like "/x/"). And objects are referred to as "mutable"; that is, they are addressed by reference, not value. Thus the statement:
newObject = carObject;
... does not make a copy of carObject: newObject points to carObject. It is carObject, and any changes to it change carObject.
OK, here's where things get tricky! An object method defines an action that can be taken on an object. So you can't do that with just text: you need to create a function (or utilize an existing one) as part of the object definition. So, for example, we would expand our definition of the car object to include another pairing: name and function.
var carObject = { ... desc: function () { return this.color + " " + this.model + " " this.brand } ...
Our method name is "desc," it is part of the definition of our object, and its value is dynamically built by combining the three previous properties.
But what's "this"? (pun intended...) "this" is a special JavaScript notation meaning "the object it belongs to." In a method, that means the owning object -- carObject in our case. So we would read the return statement as returning the three property values of the current object (car) separated by blanks. By the way, the function definition has no name and no operands. It doesn't need a name because it only operates internally to the object.
Because desc is a method, not a property, that means it must be called with parens after its name, even when you have no operands to pass:
carObject.desc(); // = Navy Mercedes GLE350
But you could pass an operand. Suppose the function definition of the method was changed to the following and the method was invoked with an operand:
desc: function (name) { return this.color + " " + this.model + " " + this.brand + " owned by " + name } carObject.desc("Fred"); // = Navy Mercedes GLE350 owned by Fred
We interrupt to discuss the use of alert below. alert pops a message to the screen. You may wish to substitute 'console.log' for 'alert' to place the output in the console debugging console. |
You might wish to do something with each and every value in an object. To do so, you could use a for...in loop:
var carObject = { brand:"Mercedes", model:"GLE350", color:"navy" }; var index; // you could alternatively place "var" in front of "index" below for (index in carObject) { alert(carObject[index]); // or do whatever you want to the value here ... }
index is an arbitrary variable name. This displays each of the 3 values (but not the property names) one by one. If you want the property names, use the Object.keys method discussed below.
There are a number of built-in method that you can use with objects. They all begin with the word Object and take the current object name as an operand.
Object.keys creates an array containing the property names in the object (called keys here):
var carObject = { brand:"Mercedes", model:"GLE350", color:"navy" }; alert(Object.keys(carObject)); // = brand,model,color alert(Object.keys(carObject).length); // = 3
Although we won't explain the syntax in detail here, this is how you could
access both the properties and values together. Briefly,
forEach executes a function once for every element of
an array; the => is a shorthand notation for a simple function;
and the so-called backtick (`
),
also called a template literal, is an extended form of a quote which allows the
$ to permit variable substitution in the string. Remember
that Object.keys returns an array, not an object, so we are applying the forEach
method against the array returned by Object.keys.
var carObject = { brand:"Mercedes", model:"GLE350", color:"navy" };
Object.keys(carObject).forEach( key => {
let value = carObject[key];
alert(`${key}: ${value}`);
}
);
// This yields three lines of output such as "brand: Mercedes"
Here's an alternative approach to the above example where instead of returning separate lines of property/value pairs, you get the pairings in an array by using the Object.entries method:
var carObject = { brand:"Mercedes", model:"GLE350", color:"navy" }; var entriesArray = Object.entries(carObject); for (var key in entriesArray) { alert(key+". "+entriesArray[key][0]+"="+entriesArray[key][1]); }
If the notation entriesArray[key][0] is bugging you, it reads like this: entriesArray[key] accesses the current array entry, starting with 0. Having that entry, knowing that Object.entries created a nested array, we now access both the first and second entries in the nested array (keys 0 and 1) separately for purposes of presentation. Thus entriesArray[key][0] is the first nested array entry (the property name of the first entry).
For this, you use the Object.values method with your object name as the operand.
var carObject = { brand:"Mercedes", model:"GLE350", color:"navy" }; // object myCarArray = Object.values(carObject); // array alert(myCarArray.toString()); // string: Mercedes,GLE350,navy
Object.create creates a new object using an existing one as a prototype. Oh my, there is complication here which seems to be rarely documented! (I credit DigitalOcean for educating me on this point.) The new object does not actually contain all the properties and values of the prototype object: it just gives you access to them. You can add or delete properties and values to or from your copy of the object though. Depending on how you access the object, you may or may not see the prototype's properties directly, as demonstrated in this example:
myObject1 = { a: 1, b: 2, c: "Fred" }; // our prototype myObject2 = Object.create(myObject1); alert("myObject2 = "+Object.values(myObject2)); // = nothing, it's EMPTY! myObject2.d = "Brack"; // the FIRST real value of the new object alert("myObject2 = "+Object.values(myObject2)); // = Brack (now it has something) for (var index in myObject2) { // will find all four values, but starting with the new 'd' alert(index+". "+myObject2[index]); }
Object.assign will create a new object from one or more existing ones. In this case, the created object does contain all the values of the original object(s).
myObject1 = { a: 1, b: 2 }; myObject2 = { b: 3, c: "Fred" }; // note that b has a different value here myObject12 = Object.assign(myObject1, myObject2); // b gets overridden alert(Object.values(myObject12)); // = 1,3,Fred
There is an alternate way to do this by using the spread operator (...). But caution, as spread is not supported by Internet Explorer.
myObject1 = { a: 1, b: 2 }; myObject2 = { b: 3, c: "Fred" }; myObject12 = { ...myObject1, ...myObject2 }; alert(Object.values(myObject12)); // = 1,3,Fred
Remember our discussion about sorting arrays? It's similar but a little more complicated for sorting objects in an array, since there are multiple key:value pairs, as opposed to just values. Let's take a practical example to learn how to do this.
I created an object consisting of DVD titles, along with the year each DVDs was released. Here is an example:
dvd[1] = { title:"Bombshell", year:2020 } dvd[2] = { title:"A Bigger Splash", year:2016 } dvd[3] = { title:"A Bug's Life", year:2020 }
How do you sort an object? You need to use the optional comparison-function of the sort method. Normally, the function looks something like this:
myArray.sort( function(a,b) {return a-b} ));
But with objects, you don't have single values to compare: you have multiple key:value combinations. You have to choose which one or ones will be involved in your sort. So to sort solely by year, your function would look like this (using ternary notation -- see Basic Comparison and Logical Operators):
dvd.sort( (a, b) => (a.year > b.year) ? 1 : -1 );
You are asking each value of year to be compared with the next one, and if the first one is greater, you return a 1 (true), versus -1 (false). And that's fine for year alone, but your titles will be out of sequence, since they are not included in the sort comparison. (Look at the original list: "Bombshell" will appear before "A Bug's Life" when just the years are sorted.) So to remedy this, you need to add the second field to the formula (title in our case):
dvd.sort( (a, b) => (a.year+a.title > b.year+b.title) ? 1 : -1 );
Now the titles are included in the sort, after year, so the sequence will come out correctly.
Not discussed here (yet anyway) are:
# # # # #
Fred