Professional Documents
Culture Documents
And if you notice, this time, I still have that string literal,
but this time I'm using single quotes.
Because in JavaScript there's really no difference
between double and single quotes.
You also notice I omitted that semi-colon, which in JavaScript is OK.
So my colons are actually optional.
And we can just declare them in line like this, and I can have--
So, if you notice, I actually have three different types all in this array.
So I have a string, I have a number, and I have a function.
And this is perfectly fine.
JavaScript doesn't really care what you throw in an array.
You can have all sorts of different variable types.
And so say we want to access those things in an array.
Anybody care to guess exactly how I would do that?
So say I want to execute that function.
How might I go about doing that?
Any guesses?
Yeah.
Yeah, exactly.
The array of two.
So if you're familiar with other languages, a lot of them
have the same syntax for indexing into an array.
And so since we have three things in this array,
we do what's called zero indexing, whereby the first index in that array
is called the zeroth index.
And then you count from there.
So we have zeroth here, first, and second here.
And so say I want to access that function.
I just do array 2, and I get that function.
And say I now wanted to execute that function.
I can, say, execute it like that.
So this is something that you might see in other languages,
but JavaScript you can do it as well.
You can just grab that function out of that array and execute it like that.
Say I wanted a for loop, and I wanted to console log everything in that array.
I can do it just almost like C. So I can do for--
this time we'll use let for the variable.
We'll talk a little more about that later.
So I start at zero.
Well i is less than the raised length.
[TYPING]
So say I have this value, and I want to know exactly what type that is.
So there's this operator called typeof in JavaScript where I can invoke this.
And that will give me the type of whatever that variable holds.
So if I were to run this code, then it would say number,
because that is the type.
So right now in order to execute my JavaScript,
I'm using this thing called node, which, as mentioned earlier,
is basically a command line runtime for JavaScript, which is built off of V8.
Any browser has a console where you can also just type JavaScript directly
in there.
So if I were to open up the tools on Chrome, which is my browser of choice,
I actually get this, which is a JavaScript console built
into all of these browsers.
So if you guys are using Chrome at home, and you want to follow along,
you're welcome to open up the developer tools here.
Go to console, and you have your own JavaScript interpreter here.
So say I wanted to do that thing where I do const x equals 42,
and I wanted to get the type of that variable.
I can just do typeof x, and it will output that number.
So there's a little bit of a caveat with this, where this might surprise you.
So if we remember back a few slides, we talked about all the different types.
One of them is undefined, one of them is null.
And say I want to get typeof null.
So what should it output?
Yes?
AUDIENCE: String.
JORDAN HAYASHI: String, why would you say string?
Cool, so another good question would be so when should I use double equals
versus triple equals?
And people generally say you should never use double equals,
because that means you have to know exactly how every single thing coerces.
And not only you, but every single person who reads your code
should know what all these values coerce do.
And some of them might be somewhat surprising.
So we have a chart here that talks about the JavaScript equality table.
For those of you who have the slides opened, you can click on that link,
and it will bring you to the repo that has this.
Basically, some of these strings are somewhat strange.
Like how empty array is double equals to false, which doesn't really
make a ton of sense.
A lot of these don't really make a ton of sense
and, basically, never use that double equals because it
might have some strange behaviors.
And simple.
So good, you got them all.
Nice.
And say I actually wanted to use not a string literal inside these brackets.
I could also do that.
So I could do isTeaching and do o and then pass in this variable
here with a value of isTeaching, and that will set that key.
And then say I wanted to do o of greet and give that same function.
Cool, and so those three objects are basically the same thing.
It's just three different ways of declaring objects.
You can also nest objects.
So say I wanted an object within an object.
That's also fine.
Say I wanted to do something like this.
[TYPING]
Cool.
That's actually--
AUDIENCE: I have a question.
JORDAN: Yeah.
AUDIENCE: Do the elements of the objects all have to be labeled with strings?
Like if a key were numbered, say, would that work?
JORDAN: Um, So anything, so anything here, is interpreted as a string.
So say, we were to do, like this,
That would be, basically, one as a string.
So that, this value here will be cast as a string.
And that's what will be used as the key.
So the question was, can we use numbers, or anything
other than strings as keys in objects?
And the answer is, kind of, because everything will just
be cast to a string.
Yeah, great question.
Let's actually copy and paste this into our browser.
And we confirm that it works.
And so, how might we go about getting those values back out?
So, it's basically the same way we got them in.
So if we do dot, we can see, oh, these are all, this is the browser saying,
oh, these are all the keys of the object.
So I can do o3.address, and it will give me back the object.
And say we want to get this number out of here.
How might we do that?
[TYPING]
AUDIENCE: Dot and the number.
JORDAN: Exactly, dot number.
Alternatively, we could have also done this.
[TYPING]
And gotten the same thing.
Any questions with objects?
Yeah.
AUDIENCE: Is there a conventional way to do that?
Or is it kind of like reference?
JORDAN: To do what?
AUDIENCE: Just between dot number and [INAUDIBLE]..
Great-- So, let's talk a little bit about this thing, where
I was talking about mutating objects.
So say--
[TYPING]
Yes.
AUDIENCE: dot a equals--
JORDAN: Yes, so I can update this to be anything else.
[TYPING]
Cool, but say I actually did this.
[TYPING]
So, we mentioned that line nine, we're taking the keys and values of o,
and merging those into a new object.
And then that line 11 we're taking o2, getting the dot object,
so accessing the value with the key called object.
And then setting that object's key called key to new value.
And then now console logging o to object dot key.
Yeah.
AUDIENCE: So, new value?
JORDAN: Yes, so this, so the guess is new value,
and that is absolutely correct.
So this, so-- line nine here is doing what's called a shallow copy.
Which is just grabbing the keys and values of some object,
and just setting those blindly into some other object.
As opposed to what would be called a deep copy.
Where that would take the keys and values.
And then if the values are objects, you'd
also take those objects keys and values.
Do that recursively, and basically get every single layer deep cloned.
But since object assigned just takes the keys and values dumbly,
if we have an object in there, update that object's key.
o.obj and o2.obj are still referencing that same object in memory,
so since we updated--
we mutated that object, it would update in both o2 and o.
Does that makes sense?
Great, any questions about this?
Yeah.
AUDIENCE: How would you do a deep copy?
JORDAN: So, how would you do deep copy?
That's a great question.
There are multiple different ways.
So most people would say use a library.
Meaning, rather than implementing this thing on your own,
just take somebody else's implementation.
But lets, let's actually do that.
That's a good question.
So, how would we do a deep copy?
[TYPING]
And do that.
So first, let's define--
[TYPING]
Equals-- what?
Otherwise--
[TYPING]
And then at the very end, we can just return that new object.
[TYPING]
Cool.
Anybody see any bugs?
Candy opportunity.
All right, let's just go ahead and test this.
So-- let's do--
Do copy o--
o3.obj.key.
No, great.
So, arrays as well, are also stored by reference.
So if we were to do the same exact example,
and rather than updating the object, we updated that array,
we'd end up with the same exact results.
And so, if we were to update our deep copy function to also take
care of arrays, all we have to do is also check, rather than
checking object, also check against arrays or any other data types
that we're going to check.
If you do str.toUpperCase--
Now we, we're left with a new string with all uppercase.
So, these are just functions that we can invoke on any non-primitive that
gives us something else.
That is available to all non-primitives of a given type.
So each object stores a reference to its prototype.
Meaning, it has all the-- it knows about all of these methods.
And it stores a reference to the object in order to know where these methods--
the code to actually the run that lies.
And say we have a prototype chain where there
are a bunch of different methods of the same name.
Whichever one is bound most tightly to the instance has the priority.
So say we have an object in an array, where array is the--
So say we have a value that is of type array, up the prototype chain
we have arrays, its prototype is array, that prototype is object.
Say we have the same named method on both of these.
If we call that method, the one that's bound most tightly the,
array will take priority.
So let's actually show that.
So, say we have something like--
Cool.
Most primitive types had object wrappers.
And so we talked about how primitive types don't
have any methods associated with them.
But primitive types also have wrappers that
have prototypes associated with them.
What the heck does that mean?
So if I were to do 42.toString, It's going
to be like what the heck do you mean?
42.toString--
[TYPING]
Right I told you that these primitive values don't have methods.
And so 42.toString doesn't really make sense.
But say I were to do this thing const num = 42 and did num.toString,
[TYPING]
That will actually do something.
And that's a little bit strange.
This is another one of those JavaScript interesting behaviors.
Because all of the primitive values have wrappers
that give them access to a bunch of methods.
And JavaScript will automatically do what's called boxing for you.
Which it says, hey, like I know 42 is a primitive,
but if you call this toString method, I know what you mean.
I'm going to box this 42 number with this prototype that
has a bunch of these methods on it.
So if I were to do 42.toString that would make sense.
And if I num.__proto__,
[TYPING]
That actually exists.
But 42.__proto__
[TYPING]
Does not.
Does that make sense?
Again not something you're going to use everyday, just something that
is helpful to know in case you run into the strange corner cases.
Cool.
So why would we use a reference to the prototype?
And what is the alternative there?
Anybody care to give a shot at that?
So this is going back a little bit too deep copying versus shallow copying.
AUDIENCE: So, maybe if the initial object is massive.
And then you just want to do something, like, after it?
JORDAN: Yeah, so if the initial object is massive, like, what happens then?
So the alternative is basically to clone every single--
to deep copy every single prototype every single time
you couldn't new value.
Which is, safe, because that number and all of its methods
are all encapsulated within that specific variable.
But it's also a little bit expensive in both performance,
because you have to do that deep copy every single time.
And also of memory, because the object starts to get pretty large.
And if you have an array of like 100 different things,
all hundreds of those, deep copying every single prototype gets pretty big.
And you actually override that to be some function that will return 100.
Now what happens if I call a num.toString?
Wait a second.
[TYPING]
So that could have some dangerous penalties right?
So if I were to change the prototype of the number class,
even though num was declared 100 lines prior to be the number 42.
And we tried num.toString here and it returned 42.
If we were to change the prototype later,
it affects everything that has ever happened.
So num.toString now starts returning 100.
And everything that will happen.
So if I were to do--
[TYPING]
Cool.
Exactly.
So the pointer is still pointing to the same object.
The reference has not changed.
So we mutated that object, but it's still
pointing to that same place in memory.
It's still pointing to the object that exists over here
and that reference has not been changed.
Does that distinction make sense to people?
It's a pretty important one.
So I said before that if I tried to do something like this, what happens here?
Error.
Why?
Because it's a constant and we can't update a constant.
And we can confirm this.
Oh.
It'll actually tell us, hey, a TypeError.
You call this a constant variable but you're trying assign it.
That's not OK.
But if we did--
Cool.
What do you guys think would happen if I tried to do this?
Undefined?
So let's try to run it.
Error.
So since these things are block scoped, it
means the variable is declared at the line that it is written.
And if we try to use it before then, it actually does not even exist at all.
So if I tried to console.log something called thisIsAConst here, remember,
the JavaScript interpreter is just reading down
and it won't see like, hey, what the heck is thisIsAConst?
I have no idea what that is so I'm just going to Error here.
So the other thing we said, we can have these things called a var.
If I did var thisIsAVar equal to 50, that's fine, I can update it.
Get
And a few things are hoisted.
Var the-- actually, the declaration of the creation of an empty variable
are hoisted.
Function definitions are hoisted as well, but const and let
are not, as we saw if we tried to access the variable name of the const or let,
then it errors.
But with a var, the declaration of that variable is actually hoisted.
And we can talk a little bit more about how that works in a second,
but let's actually play with it a little bit more.
So function definitions are hoisted.
So let's clean up this file a little bit.
so this is console.log.
So who can tell me what the difference is between line 21 and line 25?
OK.
Yep.
So repeating for the camera, line 25, thisIsHoisted is declared as a constant
so it cannot be changed, whereas line 21 is declared as a function and so it can
be changed, which is absolutely correct.
What happens if I try to do this up here?
What's hoisted?
Uh huh.
Exactly.
So repeating for the camera., and I'll bring up the code as well.
So down here, so the first time when we declared this with the let,
it's not declared at all.
This variable does not exist at all.
So this is not hoisted, the JavaScript does not
know what that means on line 1.
But when I use a var here, remember, it hoists
the declaration of this variable.
So it creates a variable called thisIsHoisted.
However, it does not assign it a value until line 25 is executed.
And so at line 1, thisIsHoisted exists.
It's just equal to undefined.
And if I try to invoke it like a function,
it says, hey, this is an undefined variable,
I can't invoke it like a function, this is a TypeError.
And so even though both of these things errored, the reason that they errored
is slightly different.
In case one when they're declared using a const or a let,
that variable just does not exist at all.
However, when we declared using var, the variable exists.
It's just undefined, and so if we try to invoke it like a function,
and it says, hey, like, this is undefined, it's not a function.
Does that make sense to everyone?
Uh huh.
OK.
So the question is, like, why can we declare two variables
with the same name when they look like they're in the same scope,
specifically with this var keyword?
Which is another thing where it's a bug/feature that a lot of people use.
And so if JavaScript were to be updated and that bug/feature were to disappear,
a lot of code would break.
So a lot of people have took advantage of this.
And basically it's the same thing as like, why is typeof a null object?
Yeah, you should-- so there's not really a good reason to use var anymore.
With ES6, everything supports const and let.
And so I've been using them in all of the examples except for these.
And I think you should definitely use them as well.
The reason I'm getting var is one, so if you see this,
you know what's kind of going on, and because a lot of legacy code-- a lot
of code written two, five years ago, 10 years ago
uses var just because it was the only option.
One thing that I didn't mention earlier is that you can also
declare a variable like this.
And a lot of this will overlap with that window object in the browser,
but since the browser API has things in it that is not necessarily
used in the command line stuff like, give me a DOM node
or give me CSS on this DOM node, stuff like that, that doesn't really
make sense in a command line interface, therefore those things
are not on this global object.
And then if we try to type global, here in the window,
that doesn't really make sense either.
There's another thing that's just kind of important to know
but you might not ever take advantage of it.
But does that-- does that make sense?
Cool.
So let's move onto something that we'll discuss a lot more in the next lecture
but I'll go ahead and introduce the concept in this lecture
and leave you with a little bit of a teaser.
So who here has heard of a closure before?
All right.
So let's do this.
if I were to call this, I'd get back an array full of functions, right?
And what do we expect those functions to do? each of them?