Tag Archives: udemy

arguments object (js)

The Arguments Object

In the Create Phase of the Execution context, we know that variables environment, this, and reference to the outer scope is set up. Additionally, it also sets up the arguments object.

Contains the list of all values that we pass into the function we’re calling.

Arguments just another name for parameters we pass into our functions. Parameters and arguments are interchangeable.

In JS, we are given keyword arguments.
During the creation phase, memory space is set for the function definition. Then it has memory space set for the parameters, and assigned them undefined, just like any other variable names.

JavaScript functions have a built-in object called the arguments object.

The argument object contains an array of the arguments used when the function was called (invoked).

This way you can simply use a function to find (for instance) the highest value in a list of numbers:

..or create a sum function

Arguments are Passed by Value

The parameters, in a function call, are the function’s arguments.

JavaScript arguments are passed by value: The function only gets to know the values, not the argument’s locations.

If a function changes an argument’s value, it does not change the parameter’s original value.

For primitive values changes to arguments are not visible (reflected) outside the function. We copy them in and manipulate them inside the function. However, the outside values stay the same.

output:

a: 10, b: 66

However, for objects, it’s different. Objects are being pointed to by references. And when you pass in an object, the parameter name will reference that object. Hence you’ll have the outside var reference obj1 reference your literal object, as well as the parameter ‘objRef’ reference that object.

In // 1, we take the reference that’s pointing to the literal object and access its property ‘name’ and then change it. This will work because we’re directly manipulating a reference that’s pointing to the object.

However, in // 2, you see that we take this parameter reference and point it to ANOTHER literal object with the property address.

When we log the results, you’ll see that obj1 did not change. This is because we didn’t do anything to the original object that obj1 is pointing to. Rather, we just took the parameter reference ‘objRef’ and pointed it somewhere else.

Prototype and Inheritance in JS (non-class)

https://www.digitalocean.com/community/tutorials/understanding-prototypes-and-inheritance-in-javascript

note:

It is important to note that x.__proto__ is a legacy feature and should not be used in production code, and it is not present in every modern browser. However, we can use it throughout this article for demonstrative purposes.

Use Object.getPrototypeOf(x) instead.

Creating objects through Constructor Functions

Because each character will share many characteristics, such as having a name, and a level, we use a constructor functions to create templates for those common traits of each hero.

Every hero has a name, and a certain level of experience

We have created a constructor function called Hero with two parameters: name and level. Since every character will have a name and a level, it makes sense for each new character to have these properties.

The this keyword will refer to the new instance that is created, so setting this.name to the name parameter ensures the new object will have a name property set.

The hierarchy looks like this:

The Prototype Object

Whenever we create a constructor function, JS will automatically create a literal object that acts as the prototype object.

That literal object’s sole purpose is to contain functions and properties for all instantiations of your constructor functions.

Note that this object (like all objects of JS) derives from Object, and will have its __proto__ pointing to Object’s Prototype Object.

This literal object acts as the prototype object for Hero. It will also have a constructor property pointing back to your constructor function. Thus, making this prototype object the same type as your constructor function.

In our case, we create a constructor function “Hero” with its properties. JS gives us an empty literal object (Let’s call it Prototype Object) in order to act as a single object that keeps properties and functions that are shared among all instances of Hero.

Note that our Hero Prototype Object derives from Object and thus, has its __proto__ referencing Object‘s Prototype Object.

It then creates and assigns a “constructor” property from the Hero Prototype Object to your Hero constructor function. This makes the prototype object type Hero. Finally, it assigns the “prototype” property from your Hero constructor property onto the “Hero Prototype Object”.

Creating an Instance

What this means is that any instantiations of Hero will have its OWN properties and functions from the Constructor function.
hero1 is an instance we just created. It has its own properties of name and level, as indicated from the constructor function Hero.

hence if we were to make three hero objects, hero1, hero2, and hero3, they all have their own unique properties of name, and level.
i.e

hero1 is “Grom”, level 1
hero2 is “Haomarush”, level 2
hero3 is “Thrall”, level 8

In addition, they ALL share the “Hero Prototype object”.

So if the “Hero Prototype object” has a property “nickname” initiated to “Orc”, then they would all have nickname = “Orc”.

Same goes for any functions and other properties that “Hero Prototype Object” may have.

Let’s check on how this works. First we display the prototype of our object hero1. You can do it in 2 ways:


output:

Hero {}
Hero {}

Now repeat with hero2, hero3. You’ll see that their __proto__ all are Hero{}. They all point to the same Prototype Object Hero{}

Adding to the Prototype

Let’s add a property nickname to the prototoype, and see what it looks like:

Now our “Hero Prototype Object” will look like this:

Hero { nickname: ‘orc’ }

This means that when we try to read a property from a Hero object, say from hero1,
– it will try to look for “nickname” in the hero1 instance first.
– When it does not find it, it will go up the hierarchy (i.e hero1.__proto__) into the prototype object, which it will then find.
– If it still does not find it, it will keep going up the prototype chain, hero1.__proto__, hero1.__proto__.__proto__….etc

If you were to change nickname:

You cannot change it from the instantiation

Our hierarchy is like this.

We have our Hero instance, with its own properties of name and level. It has its __proto__ pointing to an object, with property “type”, which is
initialized to orc. It has its constructor back to Hero, and __proto__ references pointing to Object’s Prototype Object.

Simply put, don’t confuse yourself when you try to change a property or function of a prototype object.

You cannot change it through an instantiation like this:

Erroneously, many people may think that it will see if nickname exist in the instantiation, it does not. Then it goes up to the prototype and finds it there. Then it just assigns the new string.

When it comes to setting values, JS will only let you access at the prototype level. It only let’s you read at the object level if the property does not exist at the object level.

If you do decide to assign at the object level, essentially Javascript will interpret it as you wanting to add a property called “type” to your instance, and then initialize it.

Thus, in the hierarchy, you’ll get two property called “type”. One is at the instance level. One is at the prototype level.

If you want to set the prototype object to a new value, you must do so through Object.prototype.YourPropertyName.

Different character classes of heroes

However, each hero has different abilities. Some wield different weapons. All have vastly different skills and abilities.

It wouldn’t make sense to put all the abilities for every class into the Hero constructor, because different classes will have different abilities.

We want to create new constructor functions, but we also want them to be connected to the original Hero.

Let’s take a look at how we can accomplish this with prototype inheritance and constructors.

The Warrior

We use call in order to chain constructors. Essentially, call replaces a function’s this reference to a passed in object.
Hence, we replace Hero’s constructor function’s this reference to point to Warrior’s this. Then, any kind of initialization done in Hero will take place in Warrior. In other words:

  • We first create a constructor function called Warrior. This identifies Warrior.
  • We then pass the “this” reference to Hero via function call. Using Warrior’s “this” reference, we start to construct it like a Hero. Essentially, we are doing chain constructors for Warrior. We want to first initialize Warrior to be a Hero with Hero’s properties and functions.
  • Then, we come back to Warrior, and initialize Warrior’s properties.

note: The JavaScript call() Method

The call() method is a predefined JavaScript function method.

It can be used to invoke (call) a function with an owner object as the first argument (parameter).
In our example, instance person calls fullName. However, fullName’s this is myObject.

With call(), you can use a method belonging to another object.

This example calls the fullName function of person, but is using it on myObject:

…back to our example…

In the extended object Warrior, we initialize Warrior with Hero’s properties via javascript’s call function.

So in the same way, we call Hero’s constructor, but with Warrior’s this.
We pass Warrior’s this into Hero’ constructor

Warrior total properties = Warrior own properties + Hero properties;

We’ll add the attack() method to Warrior because its what they do. We want all instances of Warrior to be able to attack, as they should.

Linking up the Prototype

So the warrior class has properties of the Hero class. But what about the Hero’s functionalities? Let’s say the Hero’s prototype has functionality to be able to run, jump, and swim.

The warrior, being a hero, should automatically have those functionalities as well.

From javascript standpoint, when you create Warriors, it will be setup in a similar fashion like Hero. You’ll get a Warrior Prototype Object. Your Warrior’s prototype reference will point to Warrior Prototype Object. Warrior Prototype Object will add and point “constructor” back to your Warrior.

Since Hero’s prototype properties and methods are not automatically linked when you use call() to chain constructors, our Warrior will use Object.create() to link the prototypes.

Now, given Hero’s hierarchy is like so:

In order to inherit Hero prototype
1) Warrior will first disregard its own prototype object
2) Use Object.create to clone its own Hero Prototype Object.
3) re-point Warrior’s prototype to this clone.

Note:
The Object.create() method creates a new object, using an existing object to provide the newly created object’s __proto__

Object.create

w is simply a standalone object that has its __proto__ pointing to the Hero prototype object. It will be used as Warrior prototype object. But for now, let’s analyze this object:

Thus, as you can see, it does not have prototype property. It simply accesses all the functionalities of objA.


repoint Warrior’s prototype to our Hero Prototype Object clone

We simply want the type of all instances of Warrior to say Warrior {}.
It’s for clarity purposes.

We add an “attack function” to our custom Warrior Prototype Object.

We have class names’s prototype point to a literal object to denote that all
all instances of this class’s __proto__ should point to this literal object
for prototype functionalities. Since Warrior’s prototype now points to Hero Prototype object, this means all instances
of warriors have Hero Prototype Object’s prototype functionalities.

… given Hero Prototype functionality has jump, run, and swim, then our warrior will also be able to use these functionalities.

Now, all instances of Warrior can not only attack, but also jump, run, swim as well!

Double Prototype Level

Let’s create someone who has two types together.

First, we construct a Healer.

okay, so have Healer created. It derives from Hero in properties and prototype functionality.

double class power: Mage

Now let’s create a special class called Mage, where we can heal like a Healer, but also have our own Mage special ability.

Structure

You’ll see that an instance of the Mage will inherit properties and functions of its deriving object Warrior, and its deriving object Hero, and Object.

Furthermore, it will be able to use the whole hierarchy of its own prototype, which includes Warrior, Hero, and Object.


— Structure —

Mage {
name: ‘Thrall’,
level: 99,
weapon: ‘Ragnorak’,
special: ‘summons Bahamut’ }

Mage { constructor: [Function: Mage] }

Healer { constructor: [Function: Healer], heal: [Function] }

Hero { greet: [Function] }

{}

source code example

Accessing via Prototype Chain

Immediately-Invoked Function Expression (iife)

https://www.kirupa.com/html5/immediately_invoked_function_expressions_iife.htm

function variation 1 – declare named function, then execute it

function variation 2 – anonymous function expression, execute it via reference

function variation 3 – Immediately Invoked Function Expression

say you have a function you want to execute like so:

…usually, you’ll have a variable name reference the function, then execute it. Or you give the function a name, and use that name for the execution as shown above.

You can also invoke a function like so:

What we just did is create a reference and point it to the return value of a function expression. Remember, a function without a name is a function expression. A function with a name is a function statement.

If you want to write function() as an expression itself, without a variable reference pointing to it, the compiler will give you an error.

It’s expecting it to be a function statement. It’s looking for a function name. In order to remedy this, you can use parentheses. Also, when you use parenthesis, JS assumes that the inside is an expression. We can create a function expression and immediately invoke it like so:

The general structure for creating an IIFE that takes arguments is as follows:

You can invoke it outside or inside the parentheses, doesn’t matter.

example:

What happens in memory

When the IIFE is immediately invoked, we have an execution context pushed onto the execution stack.
In its Creation Phase, it will put variables into memory.

In our example, greeting variable is put into memory and assigned undefined. By definition, any variable created within a certain execution context belongs there. In other words, the variable is local to the function which is was defined. Furthermore, a let variable is local to the block which it was defined.

Sa you want to manipulate the global variable. Simple, just pass it in.

This pattern makes it intentional for you to affect the global object. And not by accidental.

Review Scope real quick…

…you learned that JavaScript doesn’t have the concept of block scoping. It only has lexical scope.

This means that variables declared inside a block such as an if statement or loop will actually be accessible to the entire enclosing function:

As you can see in this example, the foo variable, despite being stuck inside the if statement, is accessible outside of it because your if is not a scopable block. This ability to have your “supposedly inner” variables promoted and accessible to the entire enclosing function is known as variable hoisting!

When do use an IIFE?

what is so different about an IIFE compared to a function you declare and call immediately like you have always done?

The main difference is that an IIFE leaves behind no evidence of its existence after it has run.

This is largely because IIFEs are anonymous functions that are nameless. This means you can’t track them by examining variables. Because the code you put inside an IIFE is actually inside a function, any variables you declare are local to that function.

IIFE provides you with a very simple way of running code fully inside its own bubble and then disappearing without a trace.

By taking advantage of the local scope IIFE’s create, you can selectively choose what to expose or what not to expose.

Here, we declare an object. However, property secretCode is exposed for public usage.

By taking advantage of the local scope IIFE’s create, you can selectively choose what to expose or what not to expose

This time around, you won’t be able to access secretCode. The reason is that the contents of secretCode are buried inside the IIFE and not accessible publicly.

Usage: To Avoid Polluting the Global Scope

Let’s say you’re implementing your own objects. You have different function interfaces like so…

rtScript.js

The problem is, when you import your file into projects, what if other implementations have the same names as well? It would lead to name collisions.

So in order to solve this problem, we want to hide these function interfaces and just expose a global variable name like so:

What this does is that it hides all the function interfaces and global variables used in your implementation.
All that’s exposed is the rtScript variable.

Hence others can use your implementation like so:

otherCode.js

Usage: capture surrounding state via Closure, and parameter passing

Given we have a function quotatious

It takes an array, and pushes a inner function onto it. Thus, it manipulates the references in quotes and points them to their own respective inner function. Even though the scope have not been created just yet, each inner function references its surround environment. Thus, they all reference the i from the loop and the array names.

In the outer scope, when we declare people to be an array with let’s say 4 elements, and pass it into quotatious, we will see that quotes array inside quotatious will have its references pointing to inner functions.

Each inner function will reference i as well as name.

Then quotes array will be returned by quotations function.
We then execute its element, which is a reference to the inner function.

When we execute the 0th element of peopleFunction, it will execute the inner function. The inner function will create scope, and reference i. But at this point, i is 5 because it shared among all the inner functions in quotatious.

Thus, names[5] returns undefined.

Solution

The trick is to create a shell that holds the correct i for each inner function.
Hence we create an IIFE first. the IIFE will take in the i as a parameter, and copy the value into the parameter index.

names is captured by closure, where as i is captured by parameter passing.

This is the key here. As different i gets passed into the IIFE, the i is kept as that value via the parameter index. Hence any inner function in the IIFE will always be able to reference index as that value.

So when we use quotes to execute the inner function, the inner function will be able to get its own referenced i.

Closures (js)

http://javascriptissexy.com/understand-javascript-closures-with-ease/
https://medium.freecodecamp.org/whats-a-javascript-closure-in-plain-english-please-6a1fc1d2ff1c
http://ryanmorr.com/understanding-scope-and-context-in-javascript/

Udemy Example

When I invoke function greet, I’m gunna get a reference to another function, in which I can invoke again.

at 1), greet was invoked, so it was pushed then popped from the execution stack.

at 2) when you invoked the returned function object, it still can access variable ‘whattosay’. How is this possible?

First we have the Global Execution Context.
When greet function was invoked, greet() Execution Context was pushed.

The variable that was passed to greet() is sitting in that Execution Context.

We all know that every execution context have memory space reserved for variables and what not. When the execution context gets popped, the memory space presumably also leaves. The JS engine would eventually garbage collect it. However, in our case, in the creation phase of greet()’s context, the variables ‘whattosay’ and the return anon function were stored in memory. The return anon function is a function definiton, and as with any function defintiions, it gets stored in memory.

We invoked sayHi, which is the anon function in memory.
sayHi() Execution Context is pushed onto the context stack.
It runs through the function and are able to use variable ‘whattosay’ because it’s referencing the object that’s existing in memory.

This phenomenon is called ‘the Execution Context has closed in on its outer variables’. It has captured them.

Closure in Memory

Let’s explain the same code, but we’re going to use async capabilities, instead of a returned function.
We start with the same code.

In the very beginning, we set up the basic. We invoke the global context.

Global (Creation Phase) – which sets up its this, outer references, greetings definition, all in memory

Global (Execution Phase) – During the execution phase, go line by line and execute each line. We first see the function definition greetings. Its already in memory so we skip it. We step and then invoke greetings function. When invoking the function it creates a context, which we push onto the context stack.

Greetings Context (Creation Phase) – we put variable name into memory and assign it to undefined. We set the ‘this’ reference to the global object, and reference to the outer environment

Greetings Context (Execution Phase) – Now comes the line by line execution. Variable name points to immutable literal string ‘ricky’.
Then it invokes setTimeout function. setTimeout function is an async function so it gets put in memory.

Since the setTimeout function was invoked, it needs to go through creation phase and execution phase.
In the creation phase, it sets up the ‘this’ and outer reference for setTimeout. Then it sees the function definition in the first parameter. It puts that anonymous function in memory.

1) For setTimeout’s execution phase, it starts on the “2 second wait”. Once it starts, we continue on to the next step, which is the end of greetings function.
2) The end of greetings function signals that we pop greetings context from the execution stack.

1) Finally, the two seconds are up.
2) setTimeout executes its callback. It invokes the anon function. Thus, it gets pushed onto the context stack.
3) setTmeout completes and thus, gets removed from memory.

then,

1) The invocation of the anon function goes through its Creation Phase. There’s literally nothing there except a log. However, we see that in the log, we are referencing name variable from the memory.
2) We print the log statement
3) the anon function now finishes is popped off the context stack.

In the end, only the global context is left. In memroy, since nothing else is using name, nor anon function in this context, they will be garbage collected.

What is a closure?

Closure is the act of capturing an object and separating it from its original scope, making it available to the capturing function forever.

First off, you create a closure whenever you define a function, NOT when you execute it.

In other words, a closure is formed when a nested function is defined inside of another function, allowing access to the outer functions variables.

For example,

returning the inner function allows you to maintain references to the local variables, arguments, and inner function declarations of its outer function

This encapsulation allows us to:
1) hide and preserve the execution context from outside scopes
2) while exposing a public interface and thus is subject to further manipulation.

setTimeout example

First Try…

We’re accessing the i from the outside scope’s for loop via parent scope.

The result is:

0
1
2
3
4
5
6
7
8
9

1 second later…

10
10
10
10
10
10

not really what we want…

2nd try

We wrap an IIFE around it so that we save the index variable. Now, for each setTimeout invocation at each index i,
we have successfully saved i into parameter index. Each Execution Context is pushed on with the index variable.

Then the setTimeout inside will be able to access the index variable.

for loop: 0
for loop: 1
for loop: 2
for loop: 3

AFTER 3 SECONDs…

callback: 0
callback: 1
callback: 2

One of the most popular types of closures is what is widely known as the module pattern; it allows you to emulate public, private, and privileged members:

Module Pattern

The module acts as if it were a singleton, executed as soon as the compiler interprets it, hence the opening and closing parenthesis at the end of the function. The only available members outside of the execution context of the closure are your public methods and properties located in the return object (Module.publicMethod for example). However, all private properties and methods will live throughout the life of the application as the execution context is preserved, meaning variables are subject to further interaction via the public methods.

or executed in the context of the window object:

The closure has three scope chains

– it has access to its own scope (local variables)
– it has access to the outer function’s variables (including parameters)
– and it has access to the global variables.

When you put a function in a variable like this, you are assigning the variable func, a reference to the function sayHello, NOT A COPY.
Via definition of a reference, anything you do on the func reference will be done on the original function.

For example, when we add a property to reference func:

our function sayHello will also have it because func is a reference to sayHello.

Lexical Scoping

Describes how a parser resolves variable names when functions are nested. The word “lexical” refers to the fact that lexical scoping uses the location where a variable is declared within the source code to determine where that variable is available.

Nested functions have access to variables declared in their outer scope.

init() creates a local variable called name and a function called displayName().
The displayName() function is an inner function that is defined inside init() and is only available within the body of the init() function.

The displayName() function has no local variables of its own.

However, because inner functions have access to the variables of outer functions, displayName() can access the variable name declared in the parent function, init().

However, the same local variables in displayName() will be used if they exist.

Run the code and notice that the alert() statement within the displayName() function successfully displays the value of the name variable, which is declared in its parent function.

Scope is created when you execute a function

When you call a function like init() you create a scope during the execution of that function. Then that scope goes away.

When you call the function a second time, you create a new different scope during the second execution. Then this second goes away as well.

These two scopes that were created in the example above are different. The variable answer here is not shared between them at all.

Every function scope has a lifetime. They get created and they get discarded right away. The only exception to this fact is the global scope, which does not go away as long as the application is running.

Closures are created when you DEFINE a function

When you define a function, a closure gets created.

Unlike scopes, closures are created when you DEFINE a function, NOT when you execute it. Closures don’t go away after you execute that function.

local scope, parent scope, and global scope are all referenced by the closure. NOT COPIED:

Don’t confuse closures with scopes

In the simple example above, we have three functions and they all get defined and immediately invoked, so they all create scopes and closures.

The scope of function one() is its body. Its closure gives us access to both its scope and the global scope.

The scope of function two() is its body. Its closure gives us access to its scope plus the scope of function one()plus the global scope

And similarly, the closure of function three() gives us access to all scopes in the example. This is why we were able to access all variables in function three().

Another Example

If not global variable v…

Since closures have REFERENCE to variables in scope, they have read/write access

This is why we can change global variables everywhere. All closures give us both read/write access to all global variables.

Closures can share scopes

double and square both share scope parent. This means their closure references variables from parent. Specifically, variable a.
Hence, when we call double the first time, double references var a from parent. It does 10 + 10 = 20. Parent’s var a is now 20.

Then, square references var a from parent. a is now 20 from the call to double due to reference usage. square does 20 * 20 = 400.
Parent’s a is now 400.

We execute double again. double’s closure references a from parent which is 400. It does 400 + 400 = 800. Parent’s var a is now 800.

Another example


output:
3
2

Things can get weird though – People Example

Here we pass an array of objects into a function. We are expecting this function to update property id of our objects.

We use a loop to assign index to the array element’s id property.
The anonymous function created here is stored in heap memory, as well as the var i.

After we run this, the result id are all the same, 103.

Since var i, and all three anon functions are stored in heap memory, when we try to access the ids later in the future, the var i is already 103.
Thus, when you invoke those functions in the future, they will be access i that is 103.

Solution

We need to make sure we store the correct i somehow, and not an i that has already ran through its course to the end.

We do this through two ways:

  • Invoking the function and storing the value right away
  • IIFE
  • “let”
Invoke the function, and storing the value

This is about invoking the function right away, and then assigning the value to people[i][“id”].
As you can see when you call creator(gang), the logs ‘Calculating for index:…’ gets called right away.
Because at each index, the anonymous function gets invoked and then the value is stored.

Thus, whenever you access updatedGang[0].id, you’re simply retrieving the stored value

In this solution, we’re invoking the function RIGHT AWAY, thus creating the scope, and the i is accessed. It will return 0. 0 + 100 = 100. It will then assign the integer 100 to updatedGang[0].id
When i becomes 1, the function executes RIGHT AWAY, creating the scope, and the i is accessed as 1. 1 + 100 = 101. 101 gets assigned to updatedGang[1].id.

… and so on.

using IIFE

As you can see, we now have reference to a function that you can execute in the future. In addition, we also have our own variable ‘index’ to store the i.

The IIFE for index 0 puts the i in its own memory, and stores it.
The IIFE for index 1 puts the i in its own memory, and stores it
…etc

Thus, we can invoke on the fly and still get the correct answer. It is vastly different than our previous example, which is invoke right away, and storing the result.

In other words:

Using IIFE, we first create a function expression and invoke it immediately with a parameter. This creates a context that gets pushed onto our stack context.

Creation Phase:
– We point this reference to global object.
– We create outer reference to scope
– puts parameter ‘index’ in memory
– put anon function j in memory.

Execution Phase:
– We use scope to access people and i. We assign them to anon function which is in memory. This anon function will access parameter ‘index’, which is also stored in memory.

Then this IIFE gets popped from the context stack.
When this loop is done, creator context is popped from the stack.

In the future, when you try to access updatedGang[1].id(), you execute this anon function that is stored in memory. It has index as 1, which is also stored in memory.
Hence that is the reason why we’re able to get the correct value at a later time. We store everything in memory via function definitions and variables.

Using let (block scoped)

We need to use let to make sure we get correct scoping.

A variable declared by let or const has a so-called temporal dead zone (TDZ): When entering its scope, it can’t be accessed (got or set) until execution reaches the declaration.

i in the inner function is not referencing some global var i. It it referencing a block scope variable i, which has been pushed onto the local stack.

it works because, for (let i = 0; i < arr.length ;i ++) defines i as var i for the local block.

in addition:

ref – http://chineseruleof8.com/code/index.php/2017/10/01/let-vs-var-js/

i is only visible in here (and in the for() parentheses)
and there is a SEPARATE i var for each iteration of the loop

like this:

does this:

Hence when we call people[0].id(), we evaluate i, which accesses its local var i as 0. 0 + 100 = 100.
people[1].id(), we evaluate the i, which accesses its local var i as 1. 1 + 100 = 101.
… so on.

Another example