WebFund 2015W Lecture 8

From Soma-notes
Jump to navigation Jump to search

Topics & Readings

  • CodeAcademy:
    • jQuery, JavaScript, HTML & CSS tracks
    • We should have finished the latter 3’s material by now (jQuery can wait, for now)
  • Books:
    • JavaScript The Good Parts:
      • We should be reading/having already read Chapters 3 (Objects) and 4 (Functions).
      • Chapter 2 is a useful overview of syntax.
      • We should be looking now at Chapter 5 (Inheritance).
    • Learning Node:
      • Anil won’t directly talk about, because a lot of the material in this book is more advanced/unnecessary (e.g.: low level networking, sockets). Treat it more as a reference.
      • It goes into depth in a very methodical way into topics that we don’t have time to dive deeply into during lecture (such as modules, Express details...)

It is important to *know your resources*; there’s value in knowing whether a resource you have contains the information you’re looking for, and where to look for it. The most successful way to master this material is to learn from both lectures *and* resources.

Video

The video from the lecture given on January 28, 2015 is available now as an MP4.

Notes

Variables and scope

Why are we talking about JavaScript puzzles? We need to do more than just write code and read your own code; you must be able to read other people’s code. How do you improve on this skill? By practicing.

We’ll create a new file called “lec-puzzles.js”. We’ll start with a function:

f = function(a) {
     debugger;
     return a + 5;
}

Note your program will terminate after running it if you don’t call the function. Once we’re running it and in the debugger, why would we want to call repl? We do it to be able to look at the variables in scope.

f = function(a) {
     debugger;
     var b = a + 5;
     console.log(“b = “ + b);
     return a + 5;
}

x = f(6);
console.log(“x = “ + x);

At a breakpoint, you can only look at variables that have been defined up to that point. Outside of repl: to move lines ahead, type “n”. To continue until the next breakpoint (or terminate program if none), type “c”. The “<“ in the debugger lets you know that the particular line came from the program’s output.

What if we initialize b = 2 before the function declaration? Now, when typing repl at the breakpoint you’d expect typing “b” to see 2. But, we can see that it is still undefined. That’s because of the keyword “var” in b’s local declaration inside f. To test this theory, we remove the “var” to just have “b = a + 5” and rerun. Now we see b has the value 2, because we’re pulling in the value set from the file scope. But, by typing “var b” after we console.log() b’s value, we see that at the breakpoint, b’s value goes back to being undefined, *because* of the keyword “var”. To summarize: there is no point putting “var” anywhere other than the top of your function, because if you put it at some midpoint of the function, the variable is considered to be declared as soon as the function is entered, however it hasn’t yet been defined. Trying to access the variable before it is defined produces a weird behaviour.

Now let’s declare a function, g, inside f.

b = 2

f = function(a) {
     g = function(c) {
          return c + b;
     }

     debugger;
     var b = g(a) + 5;
     console.log(“b = “ + b);
     return a + 5;
}

x = f(6);
console.log(“x = “ + x);

Let’s think through this: the scope of g is nested inside the scope of f; so the reference to b is the one defined in the scope of f (with value undefined), not the one at file scope (with value 2). So b = NaN because what gets run first is the right hand side, g(a) + 5, where in g, it tries to add the passed in 6 to the undefined b, which gives NaN, and then again trying to sum NaN + 5 which results in NaN.

Note: JavaScript doesn’t have block scope! So there are no variables *local to a loop*

What if we try to print in the filescope the value of g(3)? It will return NaN, and more noticeably it won’t break! Why? Because we didn’t use the keyword “var”, the nested function g is actually file scope! Lesson to be learned: use “var”, without exception.

Once "var" is added in front of g, we can no longer call g directly, but if we modify f to return g, then what will happen? We get NaN’s for b and x(6). But if we remove the “var” for the b declared in f, then 6 gets passed in as a, and b is equal to g(6) + 5 which is 8 + 5 = 13. What happens with x(6) is that since b = 13, and x(6) is now the function g, by passing in 6 we get 13 + 6 = 19.

Note: you CAN do a function declaration as follows:

function h(x) {
     console.log(x)
}

But we prefer the other way because we want to install and constantly reinforce the idea that *functions are data values*.

Back to our example, with some modifications explained below:

var b = 2;

f = function(a) {
     var b = 1;

     g = function(c) {
          return c + b;
     }

     b = g(a) + 5;
     return g;
}

x = f(6);
y = f(6);
console.log(“x(6) = “ + x(6));
console.log(“y(6) = “ + y(6));

Explanation of what’s going on with “x = f(6)”: Let’s declare below it “y = f(6)” Run it, and see that the output of x(6) and y(6) are equivalent. See that if we comment out “var b = 1”, we get *different* values. y(6) > x(6) because we’re using the same b. That’s why we care about the “var”; we’ll ensure consistent behaviour by using variables local to the function.

Objects and prototypes

Now let’s try something new:

var car = {doors: 4, colour: “blue”, fuel: “gas”};
var sportscar = Object.create(car)
debugger;

Running this and using repl, we’ll see car.doors gives 4, and surprisingly sportscar.doors gives 4. This is because the declaration of sportscar was using inheritance. Now if we modify in the repl, you see that you can override the inherited attributes by setting sportscar.doors = 2, and typing sportscar.doors gives you back 2 (car.doors remains 4).

Now back to the program, if we type:

var car = {doors: 4, colour: “blue”, fuel: “gas”};
var sportscar = Object.create(car)
car.changecolor = function(newcolor) {
     this.color = newcolor;
}
sportscar.changecolor("red”);
debugger;

We can see that sportscar was able to use the function defined in car through inheritance. It is able to access this through the prototype chain.

Should we have used “var” in the line “car.changecolor = function(newcolor) { ..."? No! The scope of changecolor is the object “sportscar”.

Now in the program, below the line where we change sportscar’s colour to red, we add:

var ferrari = sportscar;

In the repl, by changing ferrari’s colour to orange you will see that sportscar’s colour is orange. This is because ferrari and sportscar *are the same object* when you declare ferrari as such. Note that in the repl, if you type “car.fuel = “electricity”, you’ll see “ferrari.fuel” is “electricity”. So the objects that inherit from other classes automatically see the changes from the parent objects.