Object Oriented Programming is a programming paradigm that centers around modeling problems as objects that have behavior (they perform actions) and state (they have characteristics that distinguish between different objects). JavaScript objects support this paradigm, but we won't need it in this book. For now, we'll discuss objects as complex data structures, similar to arrays.
Objects store a collection of key-value pairs: each item in the collection has a name that we call the key and an associated value. Contrast this with arrays, which associate values with ordered indexes. Other languages have similar key-value data structures, but they may use different names like dictionaries, associative arrays, maps, and hashes. Some developers may even use these terms regarding JavaScript objects, but it's better to use the correct name: objects.
An object's keys are strings or symbols, but the values can be any type, including other objects. We can create an object using object literal syntax:
let person = {
name: 'Jane',
age: 37,
hobbies: ['photography', 'genealogy'],
};
You can also write that on a single line, which is handy in node
:
> let person = { name: 'Jane', age: 37, hobbies: ['photography', 'genealogy'] }
This code shows an object named person
that has 3 key-value pairs:
name
key.
age
key.
hobbies
key.
Braces ({}
) delimit the list of key-value pairs contained by the object. Each key-value pair ends with a comma (,
), and each pair has a key, a colon (:
), and a value. The comma that follows the last pair is optional. Though the keys are strings, we typically omit the quotes when the key consists entirely of alphanumeric characters and underscores. The values of each pair can be any type.
We can access a specific value in an object in two ways: 1) dot notation and 2) bracket notation.
> person.name // dot notation
= 'Jane'
> person['age'] // bracket notation
= 37
With dot notation, we place a dot (.
) and a key name after the variable that references the object. With bracket notation, we write the key as a quoted string and put it inside square brackets. Most developers prefer dot notation when they can use it. However, if you have a variable that contains a key's name, you must use bracket notation:
> let key = 'name'
> person[key]
Let's add some more key-value pairs to the person
object:
> person.height = '5 ft'
= '5 ft'
> person['gender'] = 'female'
= 'female'
> person
= { name: 'Jane', age: 37, hobbies: ['photography', 'genealogy'], height: '5 ft', gender: 'female' }
In this example, we use both dot notation and bracket notation to add two new key-value pairs to the person
object.
If you want to remove something from an existing object, you can use the delete
keyword:
> delete person.age
= true
> delete person['gender']
= true
> delete person['hobbies']
= true
> person
= { name: 'Jane', height: '5 ft' }
delete
removes the key-value pair from the object and returns true
unless it cannot delete the property (for instance, if the property is non-configurable).
Key-value pairs are also called object properties in JavaScript. We can also use "property" to refer to the key name; the meaning is typically clear from context. For instance, we can talk about the name
property for the person
object without mentioning the value.
If a variable declared with const
is initialized with an object, you can't change what object that variable refers to. You can, however, modify that object's properties and property values:
> const MyObj = { foo: "bar", qux: "xyz" }
> MyObj.qux = "hey there"
> MyObj.pi = 3.1415
> MyObj
= { foo: 'bar', qux: 'hey there', pi: 3.1415 }
> MyObj = {} // Uncaught TypeError: Assignment to constant variable.
As with arrays, this behavior can be confusing, and it occurs because of the same "variables are pointers" concept that we'll discuss in the next chapter. Essentially, a const
declaration prohibits changing what thing the const
points to, but it does not prohibit changing the content of that thing. Thus, we can change a property in a const
object, but we can't change which object the const
points to.
You can use Object.freeze
with objects to freeze the property values of an object, just like you can with arrays:
> const MyObj = Object.freeze({ foo: "bar", qux: "xyz" })
> MyObj.qux = "hey there"
> MyObj
= { foo: 'bar', qux: 'xyz' }
As with arrays, Object.freeze
only works one level deep in the object. If your object contains nested arrays or other objects, the values inside them can still be changed unless they are also frozen.
You may remember that JavaScript has two categories of data types: primitives and objects. The primitive types are strings, numbers, booleans, null
, undefined
, bigints, and symbols. Primitive types are the simplest, most basic types in JavaScript.
Objects include, but aren't limited to, the following types:
We learned about simple objects in the previous section; they're structures that contain multiple named values. Arrays are also objects, but they use integer indexes instead of keys. We learn about Date and Function objects in the Core Curriculum.
Objects are complex values composed of primitive values or other objects. For example, an array object (remember: arrays are objects) has a length
property that contains a number: a primitive value. Objects are usually (but not always) mutable: you can add, remove, and change their various component values.
Primitive values are always immutable; they don't have parts that one can change. Such values are said to be atomic; they're indivisible. If a variable contains a primitive value, all you can do to that variable is use it in an expression or reassign it: give it an entirely new value. All operations on primitive values evaluate as new values. Even something like 0 + 0
evaluates to a new value of 0
.
> let number = 20
> let newNumber = number + 1
> newNumber
= 21
> number
= 20
> let object = { a: 1, b: 2, c: 3 }
> object.c = object.c + 1
= 4
> object
= { a: 1, b: 2, c: 4 }
The above example illustrates the difference between an immutable primitive value and a mutable object. The +
operation on line 2 returns a new value (21
), and assigns it to newNumber
; the original value of number
(20
), remains unchanged. In contrast, writing a new value to the object
's c
property on line 10 changes the object's value. Note, however, that the c
property has an entirely new number in it, precisely like what happened on line 2.
One of the most essential concepts in JavaScript is that functions are not just code. They are, in fact, objects. This means that functions can be assigned to variables, passed to other functions as arguments, and returned by other functions. This is a remarkably handy feature. It helps make JavaScript the powerful and useful language it is despite its many faults.
Let's take a look at some examples of functions as objects. First, we can assign a function to a variable:
function hello() {
console.log("Hello there!");
}
hello(); // Prints "Hello there!"
let greet = hello; // `greet` now points to the `hello` function
greet(); // Prints "Hello there!"
Note that both variables, hello
and greet
, reference the same function object: the function we declared on lines 1-3. The hello
variable is created by the function declaration and is automatic. As with any other object in JavaScript, assigning it to another variable makes both variables point to the same object.
At first, this doesn't seem like it will be useful. However, it's this same principle that lets us write function expressions instead of function declarations:
let hello = function() {
console.log("Hello there!");
}
hello(); // Prints "Hello there!"
This feature also lets us define methods as object methods. For instance, consider JavaScript's built-in Array.prototype.push
method, which we met here. It might be defined something like this (don't worry about understanding this code):
Array.prototype.push = function(newValue) {
this[this.length] = newValue;
}
let array = [1, 2, 3];
array.push(4);
Don't worry about what this
means in the above code snippet (and the next one as well). You'll learn more than you ever wanted to know about this
in a future course. You won't need to understand it until then.
More importantly, this feature lets us pass functions around as arguments and return values. For instance, consider Array.prototype.forEach
, which we met here. The forEach
method takes a callback function as an argument; it then calls that function once for each element in the array. It might be implemented something like this:
Array.prototype.forEach = function(callback) {
for (let index = 0; index < this.length; index += 1) {
callback(this[index]);
}
}
let array = [1, 2, 3];
array.forEach(function callback(value) { console.log(value) })
In the same way, we can define functions that return other functions:
function greeter(greeting) {
return function(name) {
return console.log(`${greeting} ${name}`);
}
}
let hello = greeter('Hello');
let hi = greeter('Hi');
console.log(hello('Trevor')); // prints "Hello Trevor"
console.log(hello('Ginni')); // prints "Hello Ginni"
console.log(hi('Spencer')); // prints "Hi Spencer"
console.log(hi('Grace')); // prints "Hi Grace"
This last example uses a closure that lets the returned function remember the value of greeting
after greeter
returns. We'll discuss closure in a lot of depth later in the curriculum. It's another essential concept in JavaScript (and in many other languages). For now, though, we're only using it to demonstrate the usefulness of functions that return functions.
The fact that functions are objects is a compelling and versatile feature of JavaScript. You will see it a lot more often as you move through the JavaScript portions of the curriculum. The takeaway here is that functions are objects, not just code. If you want to assign or pass a function to something, you can. It may take some practice, but getting comfortable with this concept will open up new worlds.
Objects and primitive values are the data and functions that you use in your program. Anything that isn't data or a function is neither a primitive value nor an object. That includes:
if
, return
, try
, while
, and break
new
, function
, let
, const
, and class
An interesting and handy feature of JavaScript objects is that they can inherit from other objects. When an object bar
inherits from object foo
, we say that foo
is the prototype of bar
. The practical implication is that bar
now has access to properties defined on foo
even though it doesn't define those properties itself.
Object prototypes and inheritance have a great deal of relevance in Object Oriented Programming (OOP). We discuss these concepts here since it is relevant to our discussion of iterating over object properties in the next section. All you need to know right now is that inheritance lets one object use the properties defined by another object and that prototypes implement inheritance in JavaScript.
The static method Object.create
provides a simple way to create a new object that inherits from an existing object:
let bob = { name: 'Bob', age: 22 };
let studentBob = Object.create(bob);
studentBob.year = 'Senior';
console.log(studentBob.name); // => 'Bob'
Object.create
creates a new object and sets the prototype for that object to the object passed in as an argument. Our example creates a new object named studentBob
that uses bob
as its prototype. That is, it creates an inheritance relationship from studentBob
, the child object, to bob
, the parent object.
Since studentBob
inherits from bob
, we can use the name
property even though studentBob
doesn't explicitly define it. Object.create
is one way to use inheritance in JavaScript. We learn more about it and other techniques in our JavaScript courses.
Since most objects have multiple properties, you may want to iterate over an object's keys, values, or both. There are several ways to perform these operations in JavaScript.
The for/in
loop behaves similarly to an ordinary for
loop. The syntax and semantics are easier to understand since you don't need an initializer, ending condition, or increment clause. Instead, the loop iterates over all the keys in the object. In each iteration, it assigns the key to a variable which you then use to access the object's values. As always, seeing a concept in action is helpful:
let person = {
name: 'Bob',
age: 30,
height: '6 ft',
};
for (let prop in person) {
console.log(person[prop]);
} // => Bob
// 30
// 6 ft
In the above example, we iterate over the person
object using the for/in
loop. Line 7 declares a variable prop
which, in each iteration, receives a key from the object until the object runs out of key-value pairs. We use prop
inside the loop body to access and log the corresponding value.
Note that we use bracket notation within the loop. We can't use dot notation here since prop
is a variable that contains a property name. The name prop
is not the actual property name. That distinction is subtle, so stop a moment to think about it.
For instance, in the second iteration of the loop, prop
will be set to age
. If we try to use person.prop
, though, it will evaluate to undefined
since prop
is not one of the property names in the person
object. We actually want person.age
, but we can't use that since we'll want a different property name during each iteration. When we write person[prop]
, prop
gets evaluated as a variable. Thus, person[prop]
gets evaluated as person['age']
, and that returns the value of the desired property.
One feature -- or downside, depending on how you look at it -- of for/in
is that it iterates over the properties of an object's prototypes as well:
let obj1 = { a: 1, b: 2 };
let obj2 = Object.create(obj1);
obj2.c = 3;
obj2.d = 4;
for (let prop in obj2) {
console.log(obj2[prop]);
} // => 3
// 4
// 1
// 2
The first two items output by the above code are the "own properties" of obj2
, and those are followed by the properties of the prototype object (obj1
).
This behavior is undesirable when you want to limit iteration to an object's own properties, i.e., properties it defined for itself, not properties it inherited. We can use the hasOwnProperty
method to get around that problem. It takes the name of a property and returns true
if it is the name of one of the calling object's own properties, false
if it is not.
let obj1 = { a: 1, b: 2 };
let obj2 = Object.create(obj1);
obj2.c = 3;
obj2.d = 4;
for (let prop in obj2) {
if (obj2.hasOwnProperty(prop)) {
console.log(obj2[prop]);
}
} // => 3
// 4
The Object.keys
static method returns an object's keys as an array. You can iterate over that array using any technique that works for arrays. For instance:
let person = {
name: 'Bob',
age: 30,
height: '6 ft',
};
let personKeys = Object.keys(person);
console.log(personKeys); // => ['name', 'age', 'height']
personKeys.forEach(key => {
console.log(person[key]);
}); // => Bob
// 30
// 6 ft
Note that Object.keys
returns the object's own keys: it does not include any keys from the prototype objects.
Older versions of the ECMAScript standard (prior to ES6) don't guarantee the iteration order for an object's property keys. Many JavaScript engines took advantage of this non-guarantee. In older versions of JavaScript, you can't rely on any particular iteration order. Even in the same engine, you might get different results in separate runs of a program.
Modern versions of the standard (ES6+) guarantee the iteration order for an object's property keys. For the most part, you can assume that the iteration order is the order in which the keys get added to the object. However, be careful: if you have any symbol keys (we don't talk about symbol keys in this book) or keys that look like non-negative integers ('0'
, '1'
, '2'
, etc.), JavaScript will group the non-negative integers first followed by other string-valued keys, and, finally, the symbolic keys. For clarity, you should only rely on the iteration order when you know that all of the keys will be alphabetic.
Unlike JavaScript arrays (which, you may remember, are objects), most JavaScript objects don't have an abundance of methods that you can apply in your day to day usage. Most operations on objects involve iterating over the properties or their values. More often than not, you'll reach for methods that extract the keys or values of an object and then iterate over the resulting array. We saw an example of that when we used Object.keys
to extract an object's keys as an array and then iterated over the array.
Let's take a look at some other useful methods:
This static method extracts the values from every own property in an object to an array:
let person = { name: 'Bob', age: 30, height: '6ft' };
let personValues = Object.values(person);
console.log(personValues); // => [ 'Bob', 30, '6ft' ]
Be careful: remember that you can't predict the order of the values in the returned array.
While Object.keys
and Object.values
return the keys and values of an object, respectively, the Object.entries
static method returns an array of nested arrays. Each nested array has two elements: one of the object's keys and its corresponding value:
let person = { name: 'Bob', age: 30, height: '6ft' };
console.log(Object.entries(person)); // => [[ 'name', 'Bob' ], [ 'age', 30 ], [ 'height', '6ft' ]]
You may sometimes want to merge two or more objects, i.e., combine the key-value pairs into a single object. The Object.assign
static method provides this functionality:
> let objA = { a: 'foo' }
= undefined
> let objB = { b: 'bar' }
= undefined
> Object.assign(objA, objB)
= { a: 'foo', b: 'bar' }
Object.assign
mutates the first object. In the above example, the properties from the objB
object get added to the objA
object, altering objA
permanently in the process:
> objA
= { a: 'foo', b: 'bar' }
> objB
= { b: 'bar' }
Note that objB
isn't mutated. If you need to create a new object, use an empty object as Object.assign
's first argument. Note that Object.assign
can take more than two arguments:
> objA = { a: 'foo' }
= undefined
> objB = { b: 'bar' }
= undefined
> Object.assign({}, objA, objB)
= { a: 'foo', b: 'bar' }
> objA
= { a: 'foo' }
> objB
= { b: 'bar' }
This code mutates neither objA
nor objB
and returns an entirely new object.
This chapter and the last discussed two fundamental data structures: objects and arrays. It can seem overwhelming when you look at all of the different ways to represent data with code. Don't feel daunted, however. You'll use objects and arrays often. The more you use them, the more you'll learn about them. It's impossible to learn everything in the beginning, so put some effort into learning the basics, then build from there.
When you need to choose between an object or an array to store some data, ask yourself a few questions:
Do the individual values have names or labels? If yes, use an object. If the data doesn't have a natural label, an array should suffice.
Does order matter? If yes, use an array.
Do I need a stack or queue structure? Arrays are good at mimicking simple "last-in-first-out" stacks and "first-in-first-out" queues.
As you grow as a developer, your familiarity with these data structures may affect which one you choose to solve a problem. Practice and experiment with each to find out which data structure works best in which situations.
You now have a good start at understanding the remarkable features that objects provide. Key-value pairs are a fundamental concept that comes up often in other technologies, so it's good to understand it as well as you can manage.
Given the following code, how can you access the name of the person?
let person = {
name: 'Bob',
occupation: 'web developer',
hobbies: 'painting',
};
person.name;
person['name'];
Video Walkthrough
Which of the following values are valid keys for an object?
1
'1'
undefined
'hello world'
true
'true'
All the listed values are valid keys. Note, though, that JavaScript coerces the non-string key values to strings. Given the listed values, 1
and '1'
represent the same key, as do true
and 'true'
. This equivalency will bite you at some point; it's inevitable, so be ready.
> let myObj = {}
> myObj[true] = 'hello'
> myObj['true'] = 'world'
> myObj[true]
= 'world'
Video Walkthrough
Use object literal syntax (e.g., { key: value, ... }
notation) to create an object that behaves as an array in a for
statement. The object should contain at least 3 elements. You should place your code between the braces in the let
statement:
let myArray = {
};
for (let i = 0; i < myArray.length; i += 1) {
console.log(myArray[i]);
}
Arrays use positive integers starting with 0
as indexes. An array also must have a length
property.
let myArray = {
#highlight
0: 'a',
1: 'b',
2: 'c',
length: 3,
#endhighlight
};
for (let i = 0; i < myArray.length; i += 1) {
console.log(myArray[i]);
}
Our array-like object isn't a perfect mimic of a regular JavaScript array, however. In particular, it doesn't modify the length
property when you add or delete elements. It also doesn't support methods like forEach
, filter
, and push
.
Video Walkthrough
Create an array from the keys of the object obj
below, with all of the keys converted to uppercase. Your implementation must not mutate obj
.
let obj = {
b: 2,
a: 1,
c: 3,
};
The order of the array does not matter.
let objKeys = Object.keys(obj);
let upperKeys = objKeys.map((key) => key.toUpperCase());
console.log(upperKeys); // => [ 'B', 'A', 'C' ]
console.log(obj); // => { b: 2, a: 1, c: 3 }
The challenge of this exercise is to figure out how to iterate through the properties of obj
. We showed two ways in this chapter: for/in
with hasOwnProperty()
and Object.keys()
. The former involves a bit more work, so we use Object.keys()
combined with map()
to extract and uppercase the keys into an array.
We can also use forEach
, though it requires a bit more effort:
let upperKeys = [];
let objKeys = Object.keys(obj);
objKeys.forEach(function(key) {
upperKeys.push(key.toUpperCase());
});
console.log(upperKeys); // => [ 'B', 'A', 'C' ]
Video Walkthrough
Create a new object named myObj
that uses myProtoObj
as its prototype.
let myProtoObj = {
foo: 1,
bar: 2,
};
let myObj = Object.create(myProtoObj);
Video Walkthrough
Which of the following values are primitive values? Which are objects? Which are neither?
"foo"
3.1415
[ 'a', 'b', 'c' ]
false
foo
function bar() { return "bar"; }
undefined
{ a: 1, b: 2 }
Primitive Values
"foo"
3.1415
false
undefined
Strings, numbers, booleans, and undefined
are all primitive values (as are null
, bigints, and symbols).
Objects
[ 'a', 'b', 'c' ]
(arrays are objects)
function bar() { return "bar"; }
(functions are objects)
{ a: 1, b: 2 }
Neither
foo
is an identifier. Identifiers are used to name things that have values, such as variables and functions, but they are not values by themselves. Thus, they are neither primitive values nor objects.
Video Walkthrough
Add a qux
property with value 3
to the myObj
object we created in the previous exercise. Now, examine the following code snippets:
let objKeys = Object.keys(myObj);
objKeys.forEach(function(key) {
console.log(key);
});
for (let key in myObj) {
console.log(key);
}
Without running this code, can you determine whether these two snippets produce the same output? Why?
myObj.qux = 3;
Both snippets iterate over the keys of myObj
. However, for..in
iterates over all of the object's keys, including those in the prototype object, myProtoObj
. Thus, snippet 2 logs:
qux
foo
bar
Snippet 1 iterates solely over myObj
's "own" properties - that is, those defined directly on the object, not its prototype. Thus, it logs:
qux
We can add a conditional to snippet 2 to get the same output from for..in
: all we need to do is check whether the key is myObj
's own property:
for (let key in myObj) {
if (myObj.hasOwnProperty(key)) {
console.log(key);
}
}
Video Walkthrough
Create a function that creates and returns a copy of an object. The function should take two arguments: the object to copy and an array of the keys that you want to copy. Do not mutate the original object.
The function should let you omit the array of keys argument when calling the function. If you do omit the argument, the function should copy all of the existing keys from the object.
Here are some examples for your reference:
let objToCopy = {
foo: 1,
bar: 2,
qux: 3,
};
let newObj = copyObj(objToCopy);
console.log(newObj); // => { foo: 1, bar: 2, qux: 3 }
let newObj2 = copyObj(objToCopy, [ 'foo', 'qux' ]);
console.log(newObj2); // => { foo: 1, qux: 3 }
let newObj3 = copyObj(objToCopy, [ 'bar' ]);
console.log(newObj3); // => { bar: 2 }
function copyObj(sourceObject, keys) {
let destinationObject = {};
if (keys) {
keys.forEach(function(key) {
destinationObject[key] = sourceObject[key];
});
return destinationObject;
} else {
return Object.assign(destinationObject, sourceObject);
}
}
Video Walkthrough
What does the following program log to the console? Why?
let foo = {
a: 'hello',
b: 'world',
};
let qux = 'hello';
function bar(argument1, argument2) {
argument1.a = 'hi';
argument2 = 'hi';
}
bar(foo, qux);
console.log(foo.a);
console.log(qux);
This program logs 'hi'
and 'hello'
to the console. This is because objects are mutable and primitives are immutable.
When bar
is invoked on line 13, argument1
is initialized to an object and argument2
is initialized to a string value.
Line 9 reassigns an object property (argument1.a
), which will mutate the object. Line 10 reassigns a variable, which completely replaces the value of the local argument2
variable.
Thus, line 9 mutates foo
by reassigning its a
property to a new value ('hi'
), and line 10's variable reassignment doesn't effect any variables outside of bar
.
Because foo
was mutated, the access to foo.a
on line 15 logs 'hi'
. On the other hand, the variable reassignment on line 10 does not mutate qux
's value, so line 16 logs hello
- the original value of the qux
variable.
Video Walkthrough
How many primitive values are there in the following expression? Identify them. How many objects are there in the expression? Identify those objects.
[1, 2, ["a", ["b", false]], null, {}]
There are 6 primitive values and 4 objects:
Primitive Values | Objects |
---|---|
1 |
[1, 2, ["a", ["b", false]], null, {}] |
2 |
["a", ["b", false]] |
"a" |
["b", false] |
"b" |
{} |
false | |
null |
The outermost set of brackets defines an array (an object) that contains 5 elements. The elements with values 1
, 2
, and null
are all primitive values, while ["a", ["b", false]]
is a nested array, and {}
is nested object. The nested array has 2 elements, one of which is a primitive value ("a"
), while the other is yet another nested array. Finally, this innermost array contains two elements, "b"
and false
, both of which are primitive values.
Video Walkthrough
Write some code to replace the value 6
in the following object with 606
:
let obj = {
foo: { a: "hello", b: "world" },
bar: ["example", "mem", null, { xyz: 6 }, 88],
qux: [4, 8, 12]
};
You don't have to search the object. Just write an assignment that replaces the 6
.
obj.bar[3].xyz = 606;
obj["bar"][3]["xyz"] = 606;
Video Walkthrough
Consider the following code:
function foo(bar) {
console.log(bar, bar, bar);
}
foo("hello"); // should print "hello hello hello"
bar("hi"); // should print "hi hi hi"
As written, this code will raise an error on line 6. Without creating a new function or changing lines 5 or 6, update this code to work as intended.
function foo(bar) {
console.log(bar, bar, bar);
}
//highlight
let bar = foo;
//endhighlight
foo("hello"); // prints "hello hello hello"
bar("hi"); // prints "hi hi hi
All we have to do here is create an alias for foo
. We do that by simply initializing a bar
variable with a reference to the foo
function.
Video Walkthrough
Consider the following code:
function foo(bar) {
console.log(bar());
}
foo(); // Should print 'Welcome'
foo(); // Should print 3.1415
foo(); // Should print [1, 2, 3]
As written, this code will raise an error on line 5. Without modifying foo
, update this code to print the desired text.
function foo(bar) {
console.log(bar());
}
//highlight
foo(function() { return "Welcome" });
foo(function() { return 3.1415 });
foo(function() { return [1, 2, 3] });
//endhighlight
Since foo
expects a function argument (as indicated by the call to bar
on line 2), we know we need to pass each invocation a function that returns the desired value. Thus, we define and pass functions that return "Welcome"
, 3.1415
, and [1, 2, 3]
in the three invocations.
Video Walkthrough
Identify all of the variables, object property names, primitive values, and objects shown in the following code (assume the code has not been executed). Don't panic if you miss a few items - this exercise is more challenging than it looks.
function hello(greeting, name) {
return greeting + ' ' + name;
}
function xyzzy() {
return { a: 1, b: 2, c: [3, 4, 5], d: {} };
}
const howdy = hello('Hi', 'Grace');
let foo = xyzzy();
Variables: hello
, xyzzy
, greeting
, name
, howdy
, and foo
. Remember that function names and parameters are variables. In addition, constants created with const
are variables that can't be reassigned. Property names in an object are not variables.
Property Names: a
, b
, c
, d
. It's also worth noting that array indexes are property names, so 0
, 1
, and 2
are property names for the [3, 4, 5]
array. (Don't worry if you missed the array indexes as property names.)
Primitive values: ' '
, 1
, 2
, 3
, 4
, 5
, 'Hi'
, and 'Grace'
are the most obvious primitive values. It's worth noting that object property names are usually strings, and strings are primitive values. Thus, we should also include 'a'
, 'b'
, 'c'
, and 'd'
in the list and 0
, 1
, and 2
for the array indexes of [3, 4, 5]
.
Objects: The functions hello
and xyzzy
are both objects, as are the following:
{ a: 1, b: 2, c: [3, 4, 5], d: {} }
[3, 4, 5]
{}
.
Since we didn't run the code, 'Hi Grace'
is not one of the primitive values in this code. That primitive won't be created until we run the code.
Video Walkthrough
Identify all of the variables, object property names, primitive values, and objects in the following code. This problem is even more challenging than the previous one.
function bar(arg1, arg2) {
let foo = 'Hello';
const qux = {
abc: [1, 2, 3, [4, 5, 6]],
def: null,
ghi: NaN,
jkl: foo,
mno: arg1,
pqr: arg2,
};
return qux;
}
let result = bar('Victor', 'Antonina');
Note that some items in this program may appear in multiple categories.
Variables: bar
, arg1
, arg2
, foo
, qux
, and result
.
Property Names: abc
, def
, ghi
, jkl
, mno
, pqr
, 0
, 1
, 2
, and 3
. Don't forget that array indexes are property names.
Primitive values: 'Hello'
, 1
, 2
, 3
, 4
, 5
, 6
, null
, NaN
, 'Victor'
, and 'Antonina'
are the most apparent primitive values. Since property names are strings in most cases, and strings are primitive values, we should also include 'abc'
, 'def'
, 'ghi'
, 'jkl'
. 'mno'
, 'pqr'
, '0'
, '1'
, '2'
, and '3'
.
Objects: The bar
function, the array [1, 2, 3, [4, 5, 6]]
, the array [4, 5, 6]
, and the object assigned to qux
on line 3 are all objects.
Video Walkthrough