Data Type Methods & Built-in Objects
On a certain level, everything in JavaScript is an object. Your string variables have object methods, and even a hard-coded string has those same methods. The same is true of numbers, Arrays, and, of course, all the built-in objects you can access just by virtue of the fact that you're writing JavaScript code.
For example, Arrays have the .join()
method which turns the Array into a string; and strings have the .split()
method which turns the string into an Array.
And complementing those methods attached to the individual String, Number, and Array "objects" in your code, there are the Standard Built-in Objects. These objects' classes have methods that are sometimes uniquely useful, like Array.isArray()
or Object.keys()
.
Of course MDN has a full list of all the built-ins, but it's probably more useful if I hand-pick a few: Object, Number, Math, Date, String, RegExp, Array, JSON, and Promise are the ones I use the most.
Arrays
If a variable is a box, then an Array is a box full of boxes. The internal boxes are numbered, starting with #0 for the first box. Those boxes can also be subdivided into boxes, as many layers deep as you need.
The following code sample contains a smattering of things you need to know about Arrays:
let arr = [];
arr[0] = 'Arrays';
arr.push('rule');
console.log( arr[1] );
if (arr.indexOf('rule') > -1) { }
let str = arr.join(' ');
Finding Your Way Around Arrays
Arrays never use dot notation -- they only use square brackets. Since you can have arrays within arrays, and objects within arrays, let's go into some example cases.
const arrayOfObjects = [
{ doctor: '1st', actor: 'Hartnell' },
{ doctor: '9th', actor: 'Eccleston'}
];
console.log( arrayOfObjects[0].actor );
Array element position numbers start at 0 and increment by 1 from there. So, arrayOfObjects[0]
addresses the first element of arrayOfObjects
. The first element of arrayOfObjects
is an object, so we can address one of its fields by adding .actor
.
Next, let's look at an array of arrays, aka a "multi-dimensional array".
const arrayOfArrays = [
['navy blue', 'forest green', 'dark grey', 'black'],
['sky blue', 'bright green', 'light grey', 'white']
];
console.log( arrayOfArrays[1][3] );
Arrays can contain arrays that hold arrays full of arrays, on and on, with more and more dimensions of data complexity.
Later in this lesson, you'll see how to step through arrays using two types of loops designed specifically for that job. There are a lot more than two, though!

Array Methods
There are many other methods automatically attached to every Array object you create in your code. MDN has a comprehensive guide, of course. To get you started, here are a few key Array methods.
.indexOf()
and Other Search Methods
Assume you have an Array called arr
and you want to know if something is in that Array, or even want to know where it is in that Array. The code arr.indexOf(thing)
returns the index of thing
in Array arr
. When you're using .indexOf
simply to test whether the thing
is in the Array, remember, Arrays start at index 0, meaning a 0 is a hit. So in a conditional like that, think like this:
- -1 is false (the
thing
is not in the Array),
- greater than -1 is true (the
thing
is in the Array).
let arr = ['Arrays', 'rule'];
if ( arr.indexOf('rule') > -1 ) {
}
There's also .find()
which lets you specify a testing function. .find()
returns the first Array entry that makes your testing function return true
.
let arr = ['Arrays', 'rule'];
let match = arr.find( (item) => {
return item.startsWith('Array');
} );
console.log( match );
The Array method .lastIndexOf()
is just like .indexOf()
but it starts its search at the end of the Array and steps backwards until it finds a match. It returns -1 if no match was found, or if a match was found it returns the index of the found match.
.push()
and Other Insertion Methods
The .push()
method inserts one or more new elements at the end of the Array.
const arr = ['Arrays', 'rule'];
arr.push('and');
arr.push('objects', 'do', 'too');
console.log( arr.length );
The .unshift()
method adds one or more new elements at the beginning of the Array.
const arr = ['Eccleston', 'Tennant', 'Smith', 'Capaldi', 'Whittaker'];
arr.unshift(
'Hartnell', 'Troughton', 'Pertwee', 'Baker',
'Davison', 'Baker', 'McCoy', 'McGann'
);
console.log( arr );
Finally the .splice()
method. This method is versatile: it deletes, it inserts, it'll chop off half the array or more… all depending on how you use it. For insertion at some arbitrary point in the Array, you need the index for the new entry (or entries). Anything currently at that position or later, will be bumped. Let's say you have the index in the variable index
… you would do:
const arr = ['apple', 'banana', 'grape'];
arr.splice(index, 0, 'canteloupe', 'dragonfruit');
console.log( arr );
That second argument to .splice()
, which is set to 0 above, is the number of elements you want to delete at that position. So you can replace, insert, do a mass delete and an insert at the same time, etc. You can also do deletions without insertions using .splice()
, because the list of new entries is an optional parameter. And, you can chop off every element after index
using .splice()
, by leaving off the second parameter.
.join()
and Other Misc Methods
The .join()
method turns an Array into a string, using a string as a linker between elements. Example:
const arr = ['Arrays', 'rule', 'and', 'so', 'do', 'objects'];
console.log( arr.join(' ') );
The Array.isArray()
static class method is how we deal with the fact that the following code outputs the string, "object":
let x = [1, 2, 3, 4, 5];
console.log( typeof x );
(typeof
will be covered later this lesson; for now just understand it outputs a string, equal to the category of whatever's to the right of the word typeof
.)
So we can't use typeof
to find out if something is an Array. We have to use Array.isArray()
. It's not theVariable.isArray()
, it's Array.isArray(theVariable)
, because this is a static class method rather than an object method.
The .map()
method creates a new Array whose elements are generated by a callback function. You provide the callback function, which is run once per element in the Array. The callback function automatically receives inputs so long as you set the function signature to receive them. The usual signature is either (item)
which gives you the element (each one, one iteration at a time), or (item, index)
which gives you the element plus its index in the Array.
const arr = [1, 2, 3, 4, 5];
const a = arr.map( (item) => {
return item*5;
});
console.log( a );
The .filter()
method copies a portion of an Array. You provide a function as the first (and usually only) argument to .filter()
. This function gets run once for every element in the Array. The function receives input automatically, you just have to set your signature to handle those inputs. The two most commonly used signatures are (item, index)
where item
is the element and index
is its index; or (item)
-- same as before, minus the index.
const arr = [1, 2, 3, 4, 5];
const a = arr.filter( (item, index) => {
if (index > 0 && index < 4) return true;
else return false;
});
console.log( a );
a[0] = ' Swiss';
console.log( arr );
Interestingly, the above script didn't perform the same exact way across JavaScript implementations. For one thing, the documentation for .filter()
says the copy remains linked to the source Array, and therefore changing an element in one Array, should change the other; but the two Arrays were independent in both Node.js and Opera Browser (which is a fork of Chrome, under the hood). Also, my browser assigned a[0]
the value of "Swiss"
before outputting the content of a
; that is, the console.log()
method was somehow delayed, but only in the browser. I have no explanation for this quirk and my only advice is, test often.
Moving On
Arrays do a lot more than what I've demonstrated here (we'll cover one more Array method later this lesson). Always remember you can learn more reading the MDN reference for Arrays, just as soon as you get comfortable reading its more technical format.

How to Find your Way around Objects
Just like with Arrays, it's all fantastic that an object can hold multiple values, but that does nothing for you if you don't know how to access the data inside.
Objects & Sub-Objects
Let's say you have an object like this:
const fruitColor = {
banana: "yellow",
apple: "red",
pear: "green",
}
I think we all know by now that this will output red
:
console.log( fruitColor.apple );
You can continue chaining dots and field names so long as necessary to address nested objects:
const fruitColor = {
banana: "yellow",
apple: { fuji: "red", grannySmith: "green" },
pear: "green",
}
To output red
from the above data, we now:
console.log( fruitColor.apple.fuji );
Finding Object Properties Using Variables
But what if the choice of fruit was unknown until runtime? What if you needed to use a variable to select into the fruitColor
object, to identify which property you want? There is an alternate syntax for that:
let whichFruit = 'banana';
console.log( fruitColor[whichFruit] );
Since apple
was broken down to fuji
and grannySmith
in a sub-object, dynamic addressing gets a little more involved. If we want to output red
from that data:
let whichFruit = 'apple';
let whichType = 'fuji';
if (whichType)
console.log( fruitColor[whichFruit][whichType] );
else
console.log( fruitColor[whichFruit] );
You can mix and match dot notation vs square brackets as needed. For example, these are both the same thing:
console.log( fruitColor.apple['fuji'] );
console.log( fruitColor['apple'].fuji );
But this doesn't work:
console.log( fruitColor["apple.fuji"] );
By the way, you might notice I left off the curly braces on the if
/else
above. Any control flow feature that involves curly braces (so, almost all of them) can be done without the curly braces -- in which case, only one statement will happen (or not happen) as a result of that control flow feature. This is illustrated in the example code above: both the if
and the else
each have only one line indented under them. (That indentation is only to remind the programmer about the scope of each block (or line) of code -- indentation isn't a flow control feature in JavaScript.)
A Peek Ahead
Finally, looping through objects. We're getting ahead a bit, but we'll come back to it later this lesson. You use the Object.keys()
method to get an Array of the keys, and then you loop through that Array, using the values you find along the way to select into the object. Like this:
const obj = {
ball: "round",
seeSaw: "flat",
truck: "complex"
};
const keys = Object.keys( obj );
keys.forEach( (item) => {
console.log( obj[item] );
});
So far we've only discussed one of the "comparison operators" -- that being ===
the Strict Equality operator. But there are many operators you can use to make a conditional:
Operator | Effect | Example |
=== | returns true if values are equal and of equal type |
if (x === y) |
== | returns true if values are equal after type conversion |
if (x == y) |
< | returns true if number to left is smaller than number to right; or if string to left comes alphabetically earlier than the string on the right |
if (x < y) |
> | returns true if number to left is larger than number to right; or if string to left comes alphabetically after the string on the right |
if (x > y) |
<= | returns true if value to left of operator is less than or equal to the value on the right |
if (x <= y) |
>= | returns true if value to the left is greater than or equal to the value on the right |
if (x >= y) |
!== | returns true if the two values are not equal or don't have equal type |
if (x !== y) |
!= | returns true if the two values are not equal after type conversion |
if (x != y) |
in | returns true if the string on the left of the operator is a key in the object to the right of the operator |
if ("message" in error) |
typeof | returns a string describing the type of the variable after the operator |
if (typeof str === 'string') |
instanceof | returns true if the class reference on the right, is anywhere in the ancestry of the object on the left |
if (obj instanceof Object) |

for
loops and the Array.forEach
Method
I'm going to be honest with you, dear students. I don't know a single one of the newfangled for
loops, just what I call the "traditional for
loop". It looks like this:
for (let i = 0; i < something.length; i++) {
}
As you can see in the parentheses above, the for
statement has three arguments separated by semicolons:
- The first one happens when the loop is about to begin
- The second one is a conditional that determines whether the loop happens, or happens again
- The third one happens after each run of the loop
So in the example above:
- A variable called
i
is declared and defined as 0
.
- Then
i
is compared to something called something.length
. Arrays have a .length
property that contains the number of entries in that Array. Remember, Arrays start numbering their entries at 0; this is why we're checking for i < something.length
(i
less than something.length
) instead of i <= something.length
(i
less than or equal to something.length
).
- Then the
// looped code
area happens (which presumably contains actual code instead of just a one-line comment).
i++
happens, which adds 1 to the stored value of i
.
So how does this control flow structure help you walk through an Array and address every element one by one? Because it gives you a variable (named i
) that's going to loop every whole number from 0 to that Array's .length - 1
That's perfect:
let something = ["truck", "train", "aeroplane"];
for (let i = 0; i < something.length; i++) {
console.log( something[i] );
}
But there's a shorter way that's great in most situations. As long as you don't need to return
anything, you can:
let something = ["truck", "train", "aeroplane"];
something.forEach( (item, i) => {
console.log( item );
});
As shown above, Array.forEach
takes a callback as an argument. The callback will be called once for each element of something
. Each time the callback is called, item
and i
will be pre-filled with the next element of something
and the index of that element. (The "index" is the number you'd put between square brackets to get that element.)
The Increment (++
) and Decrement (--
) Operators
You'll have noticed that the traditional for
loop has i++
as the third statement in its funky signature. This is the "Increment Operator". It adds 1 to the variable it's attached to, assuming it's a number. You can subtract 1 from a variable just as easily, which for example is how you'd do a for
loop that counts backwards: i--
.
while
Loops
Another situation you'll occasionally find yourself in that calls for the Increment Operator in a loop: you want a loop that runs until a condition is met. But you also want a limit to how many times the loop will run -- rather than strictly waiting for that condition to change. This is a job for the while
loop.
let isDone = false;
let i = 0;
while ( !isDone && i < 10000 ) {
isDone = checkStatus();
i++;
}
We know the while
loop will run at least one iteration because i
starts at 0 and isDone
starts as false
. Why does that mean at least one iteration? Because after while
is a conditional that says: evaluate as true
if isDone
is false
or evaluates to false
, and i
is less than 10,000. If the conditional evaluates as true
, then the loop runs. After that loop, it checks the conditional again, and, if the conditional still evaluates to true
, the script runs the loop again.
We got this result out of the while
conditional thanks to some new things: the "Logical Not" operator, and the "Logical And" operator (plus we used the "Less Than" comparison operator, covered earlier this lesson). Logical Not inverts whether something will evaluate as true. Logical And allows you to chain together conditional logic; the condition on the left and the condition on the right must both evaluate as true in order for the conditional to evaluate true as a whole.
Inside the loop we get the return value of some function called checkStatus()
which we can assume exists. Hopefully, whoever programmed the code above, checked the return value data type first, and confirmed it eventually outputs something that evaluates as true! We store the return value of checkStatus
in the variable isDone
which is the same variable checked in the while
conditional. Then we increment i
. Here the loop ends, with control flow now hitting the while
conditional to see if it's still true. If it's false, control flow skips to the next line after the while
loop's closing bracket. But if the while
loop's conditional evaluates as true
forever, the script will keep running that loop forever, or until someone or something shuts it down.

Lesson Conclusion
Manipulating and navigating your data is the essence of programming. Every category of software must manipulate data, and to do that, often, a coder must navigate data structures. There are often situations such that, not only do you have a complex data structure, but you have an array full of those structures. Therefore, it's important to understand loops, and know which loop is best for which job. Remember, the traditional for
loop might be slightly more cumbersome to write out, but it lets you return
effectively. Today's lesson also unlocked the conditional part of if
and while
, by exposing the conditional operators. And not last but least, I explained what the ++
and --
operators do.
null
and undefined
Data Types
In programming there are multiple ways to say "nothing". The null
and undefined
data types correspond to the built-in null
and undefined
values. The value null
is nothing special… it's just "nothing". Meanwhile undefined
can mean the variable has been created, but hasn't yet been assigned a value. (Or, it can also mean someone deliberately set a variable to equal undefined
.)
Both null
and undefined
evaluate to false
. As a matter of style, I tend to set my variables to undefined
when I need to signify they're in a "reset" state; and I tend to return null
(or just false
itself) from functions if some criteria wasn't met.
You can use the typeof
operator to see if (typeof someVariable === 'undefined')
but it's quicker to check the value: if (someVariable === undefined)
loses both the typeof
operator, and the quotes on undefined
.
Garbage Collection
Memory management is hard, and JavaScript is neither the best nor worst at it. Its primary means of keeping a trim footprint is called "garbage collection" or GC. The GC system relies on you, the programmer, to keep a clean house in your code.
Data disappears from your program, freeing up memory, when there are no more variables pointing to it. Remember from part 1 of this series: any data type that reports itself as an object (or an Array) gets variables that all point to a single instance of the data. It takes special techniques to copy an object or Array. If your code says const whatever = someObject;
you now have one copy of someObject, referenced from two different variable names.
This matters because, someday, you'll be building indexes of data. You'll need to be able to clear individual entries from those indexes, to keep your programs memory-efficient.
Async/await - Asynchronous Functions
Our most basic control flow pattern -- top line to bottom line, one line at a time -- is no longer guaranteed, thanks to modern programming features. Programs can now run functions asynchronously, which means functions can activate and do whatever, but your outer scope code doesn't wait for the function to finish before continuing with the next line of code. As a beginner, you need to understand this fundamental side of the language: the asynchronous side. Because, someday soon, you will do something like:
let someVariable = someFunction( options );
…and everything will seem fine, but you won't receive any data in someVariable
… or if you do have any data in someVariable, it's some object called a Promise. In this situation you should check the declaration of someFunction
to see if it is async
. An async function
's declaration looks like one of these:
async function someFunction( options ) {
}
const someFunction = async ( options ) => {
}
const someFunction = async function( options ) {
}
async someMethod( options ) {
}
someMethod: async function ( options ) {
}
someObject.someMethod = async ( options ) => {
}}
someObject.someMethod = async function( options ) {
}}
Solution 1: await
the async
If it's async
, you should adjust the code where you call that function. Instead of
let someVariable = someFunction( options );
…you add await
before the function call, e.g….
let someVariable = await someFunction( options );
This won't solve every situation. But in many cases, a quick console.log()
will confirm you now have the data you expected, instead of a Promise. If it doesn't solve your situation, then you probably need to know about "Thenables" and Callbacks.
A callback is a function you pass into another function as input. I don't mean like this:
y.setHeight( x.getHeight() );
That's passing the function's output as the input of another function. I'm talking about passing the function itself as input. Remember, everything is a variable, even a function:
y.setHeight( x.getHeight );
What's the difference? No parentheses on the function being inputted as a callback. Why do this? Storing the function gives you the ability to re-run the callback, giving you a fresh return value based on up-to-the-picosecond data.
There's also the anonymous callback. An anonymous function or anonymous callback is a nameless function made up on-the-fly. So yes, you can actually write a function in the middle of calling another function. That is a very common practice actually, and would look like this:
aFunctionThatExpectsACallback( () => {
}
);
Many asynchronous functions don't await
properly and their programmers worked around this by accepting a callback. The callback is run when the job is done.
Solution 3: "Thenables" & Promises
Thenables are objects that have a .then()
method which takes a callback as an argument. The callback happens when the Thenable finishes its other tasks. The standard JavaScript "Promise" object is a Thenable. So here is another way to force your code to wait until the Promise has turned into a value:
let foo = await anAsyncFunction();
function someHandlerFunctionForWhenItsDone(data) {
}
foo.then( someHandlerFunctionForWhenItsDone );
foo.then( (data) => {
});
As shown in "method 2" just above, the callback will usually have at least one parameter, which is your output data, automatically populated into the callback's input when the task is done. The spec for any given callback will also often have an error object as an input parameter; again, this is auto-filled when your callback starts to run.
In Conclusion (For Now) regarding Asynchronous Code
Asynchronous features are a good thing. They make interfaces faster. But as a beginner, they can seem like voodoo and a curse. Just remember there's always a way to get the result you want; the three techniques above will get you through basically all situations.

Objects
If you're one of those students who started with hands-on tutorials before seeking to learn the basics, then you've probably used objects before. Anytime you see the dot notation such as Array.isArray()
or someString.split()
or Object.keys()
that's either an object, or a class.
Sharp-eyed students may have noticed I included strings as objects! But in part 1, strings were one of the data types not counted as an object! On a certain level, everything is an object in JavaScript; the two categories of data types from lesson 1, the "primitives" vs "objects", still stands as the line between values being copied vs objects being referenced. Try not to think about it too hard.
Choose any real-world object in your line of sight. It has properties: it has at least one color, at least one shape, is it shiny or dull, is it powered, is it rechargeable, and so on. In programming, that's your most basic object. An object is a variable type. When used at this simplistic level, its equivalents in some other languages include the dictionary, or the hash, or the associative array. The most basic JavaScript object is just a variable that holds sub-variables. You can define one very easily on the fly, almost anywhere in your code:
const ball = {
color: 'red',
bounciness: 1.0,
size: 'large',
};
Notice this is basically identical to JSON, the JavaScript Object Notation language.
This kind of object is very useful, but not very powerful. What if it could handle its own bounce command? Of course, it can:
const ball = {
color: 'red',
bounciness: 1.0,
size: 'large',
isBouncing: false,
bounce: (milliseconds) => {
console.log('The ball starts bouncing.');
ball.isBouncing = true;
setTimeout( (obj) => {
console.log('The ball stops bouncing.');
obj.isBouncing = false;
}, milliseconds, ball);
},
};
ball.bounce(1000);
console.log(ball.isBouncing);
setTimeout( () => {
console.log(ball.isBouncing);
}, 1500);
But what if you need your ball code to be reusable? You create a class. Then your code can say something like this:
const zipper = new Ball( { color: 'red', bounciness: 1.0, size: 'large' } );
zipper.bounce(1000);
Object Classes
A class is like a second data type, for objects. Writing class-based code unlocks new features in object-oriented programming, such as using this
to mean "this instance of the class as an object".
Making object classes is easy:
class Ball {
constructor(arg) {
this.color = arg.color;
this.bounciness = arg.bounciness;
this.size = arg.size;
}
bounce(milliseconds) {
console.log('The ball starts bouncing.');
this.isBouncing = true;
setTimeout( (obj) => {
console.log('The ball stops bouncing.');
obj.isBouncing = false;
}, milliseconds, this);
}
}
const zipper = new Ball({color: 'red', bounciness: 1.0, size: 'large'});
zipper.bounce(1000);
Important but dumb thing about terminology: A function that resides inside an object or object class, is called a "method". Methods are functions. Go ahead, do a console.log(typeof zipper.bounce)
and see if it doesn't confirm - methods are functions. Almost everything that's true about methods is true about functions, and virtually everything I'll ever say about functions also applies to methods. They're the same thing (so why couldn't they have the same name?) …
The method constructor
runs whenever there's a new Ball()
. The constructor
method receives the arguments given to new Ball()
. Here we have it configured for a single argument:
class Ball {
constructor(arg) {
And when new Ball()
is called, we create an object on the fly to serve as the single argument:
const zipper = new Ball( {color: 'red', bounciness: 1.0, size: 'large'} );
Extending Classes
Remember the analogy about putting code on top of code, like layers in an image? This is where you do that. Consider if we rework the Ball
class like so:
class Toy {
constructor(arg) {
this.name = arg.name;
this.color = arg.color;
this.size = arg.size;
this.brand = arg.brand;
this.cost = arg.cost;
this.owner = arg.owner;
}
}
class Ball extends Toy {
constructor(arg) {
super(arg);
this.bounciness = arg.bounciness;
}
bounce(milliseconds) {
}
}
The extends
keyword creates a parent/child relationship between two classes. The line super(arg)
in the constructor
for Ball
refers to the constructor
of Toy
. In this relationship, Toy
is known as the "parent class". The "child class" is Ball
. The child class absorbs all the properties of the parent, including methods.
But that's not all. The new child class is also linked to the parent of the parent class, and the whole ancestry, leading back to a class that doesn't extend anything. Every property and method from that family line, gets absorbed into the child class.
Anytime the same method name appears in two or more classes in the family line, the child class overrides. The child class method can (and often should) call the parent class method, similarly to how the parent's constructor was called from the child's constructor:
class Toy {
constructor() {
}
setColor(color) {
this.color = color;
console.log('The color ' + color + ' was saved.');
}
}
class Ball extends Toy {
constructor() {
super();
}
setColor(color) {
super.setColor(color);
console.log('The ball changed colors!');
}
}
super.setColor()
calls the setColor
method on the parent class. It's almost always a good idea to put the input from the child method, into the input for the parent method, as I've done in the example:
setColor(color) {
super.setColor(color);

Now that we've swept through the most powerful features of objects and classes, let's wind this lesson down with two more class features that are often overlooked by rookie programmers. Both of them deal with the keyword static
and how it modifies properties and methods.
Classes as Data Tables
When you normally give properties to a class, you do so by defining the properties in the class constructor. (Yes, you might declare or even define normal class properties above the constructor, but sometimes you will have more complex initialization for a property than what is allowed in that format, and why divide up your initialization between two parts of the code?) This makes it so the property is associated with an instance of that class, aka an object of that class. You can't use those properties until you create a new instance of the object:
const zipper = new Ball( {color: 'red', bounciness: 1.0, size: 'large'} );
console.log(zipper.color);
But it's also possible to put data into a class in such a way that you don't need an object of that class to access those properties. These are called "static properties". Consider:
class Toy {
static purpose = 'fun';
}
console.log(Toy.purpose);
This is a great place to put configuration flags, error messages, and so forth. Don't forget that these properties can be objects, so you might wind up with:
class Toy {
static ERR = {
UBROKE: 'You broke it!',
NOTFUN: "You don't find it fun anymore.",
PENYWS: 'The toy fell into a sewer grate!'
};
}
throw( Toy.ERR.PENYWS );
Classes as Function Libraries
The same trick works on methods. Functions that seem logically grouped within a class's overall concept, are excellent candidates for static methods in that class. Perhaps we had already written a throw()
function before creating the Ball
class.
function throw(obj, thrower, target) {
console.log(thrower + " throws the " + obj + " to " + target + ".");
obj.owner = target;
}
It might make more sense for this to become an object method, depending on your project. But perhaps we want the code to be capable of throwing non-ball objects in the manner of a ball. So we move throw
to the Ball
class with just one tiny change:
static throw(obj, thrower, target) {
console.log(thrower + " throws the " + obj.name + " to " + target + ".");
obj.owner = target;
}
When to Use static
and When Not To
There's two simple guidelines about whether or not your method or property ought to be a static:
- Needs an Object: if the method needs to read from, or write to, live object properties, then the method probably shouldn't be static. (However, don't forget the example above for
Ball.throw
, where the object was passed in as an argument, making the code more powerful because it can operate on anything.) Same for properties: if a property describes a specific object, it shouldn't be static. There is no exception for properties. Static properties should describe all objects of the class. You wouldn't make size
a static property of a Ball class, but you might make shape
a static property equal to "round" (if you wanted to troll American football fans).
- Object More Convenient: Soon it will be common for you to encounter objects full of objects as you code. Normally each class goes in its own file called a module; to use a static class feature or make a new object of a class, you have to go back to the top of your file and
require
that class module. It's a minor hassle and totally avoidable if an object of that class is already conveniently available in your code's current context. In that case, common sense favors making the method or property not static.
Lesson 2 Conclusion
I know we're going through these topics at breakneck speed. Pace yourself through each lesson as you see fit. I think coding is exciting, and I want these lessons to be here for people who want to binge this education.