Back to Home

Becoming Javascript Queen

July 21, 2015

How to master JavaScript

After two unsuccessful javascript interviews, I decide to take some time and study in depth. Like it or not, JavaScript developer positions are much more than Ruby developer positions and I just can't ignore powerful libraries and their scalability! So What is JavaScript?

JS is a single threaded lagnauge. JS is a dynamic, weakly typed, prototype-based language with first-class functions. If you think about it, DOM is JavaScript representation of HTML and browser.

First, let's see how JS memory and variable works.

In this example, JS created the variable sum as a function and skip the function body. Next, It will create a, b and assigned to number and an object. When it try to create the variable c, it will invoke the function sum and then reads the function body. a and b becomes x and y and then it will return the result, which is 2 + 3 = 5

Let's pretend there is a new variable d. If you call d = sum(a,b), what happen?

It will still return 5. Because the system will create new variable d apart from c and assigned to sum(a,b) like they don't know nothing about c.

As you can guess, if you call c(); it will returns 7. It is because it will bring c value and then execute again.

Let's observe weired behavoir of javascript double and triple equal rules

'==' and '===' :The Performance Difference

21 == '21' //true undefined == null //true undefined === null //false 21 === 21 //true {} === {} //false - because they only compare the memory address of the two different objects. NaN === NaN //false true == {valueOf: function() {retur "1"}} //true - because they are trying to convert bullean value to ingeter 1. So it becomes a question of 1 == '1'

So when we using ==, this will increse look up time dramatically if they have to match different type of things. For instance, if they want to see if true and the function returns '1' are equal(==), then it will first see if true can be validated as something else like integer and then convert to 1. And then it will see if this will be match with the string of '1'. And then finally it will determine 1 and '1' are same thing. So this is about the performance issue, not anything else.

Note: I don't think this is super important or anything but 'Method' is function belongs to a object(so it is property of an object), function is just general scope of function

There is also term called 'Native Array Method' which is like 'unshift' and 'push' in array. Function on array comes out of box of Javascript.

*Special key word 'arguments' var add = function(a,b) { console.log(arguments); }; add(3, 4, 5) // [3,4,5] in array like format

Scope scope scope!!!

var extra = 'chocolate'; var blender = function(fruit) { var x = fruit ; var y = 'yogart'; var delic = function() { var ingred = 'milk'; console.log( fruit + ' and '+ y + ' and ' + extra) }; console.log("The x is " + ingred); delic(); } blender('strawberry'); //this will throw ReferenceError: ingred is not defined

Conclusion: Parent can't access children's variables(ingred) but children can access parent's variables(x, y).

function 'delic' can't be execute outside of the parent function 'blender'. The reason is that delic is a private function of blender.

var outerCounter = 10; var fn = function () { outerCounter = outerCounter + 1; ACTUAL = outerCounter; }; fn(); expect(ACTUAL === 11).to.be.true; console.log(outerCounter) // 11 fn(); expect(ACTUAL === 12).to.be.true; console.log(outerCounter) // 12

Because outerCounter is a global variable, it can be reset from anywhere. Compare with the following exercise

var fn = function () { var innerCounter = innerCounter || 10; innerCounter = innerCounter + 1; ACTUAL = innerCounter; }; fn(); expect(ACTUAL === 11).to.be.true; fn(); expect(ACTUAL === 11).to.be.true;

A new variable scope is created for every call to a function.

Closure

var closureAlert = function() { var x = 0; var alerter = function() { alert(++x); } return alerter; } myclosureAlert = closureAlert(); myclosureAlert(); // myclosureAlert is a 'closure'. It holds the reference to the environment.

One thing really important to understand is why we use 'closure'. A 'closure' is to store the function inside of the function so it can be called later. So in general, the parent function will 'return' the child function (but it will NOT invoke the function) which can be stored in variable outside of the parent function. Thus it can be invoked later.

Let's look at one funny example of how closure works in memory.

So what a[0]();, a[0]();, a[0](); will return? The answer is 3. Let me walk through this.

  1. The for loop will create 3 different functions. a[0], a[1], a[3]. That's all what the for loop does. Nothing else.
  2. At the begining, variable i will start as 0. and then when it create 3 function, it will change to 1 and 2.
  3. When it becomes 3, for loop will stop. Because for loop supposed to take i less than 3. But i remained as 3.
  4. So when a[0]() or any other two functions called, it alerts '3'.

Prototype Inheritance. What does 'NEW' keyword do?

var Person = function(name) { this.name = name; }; //object constructor Person.prototype.speak = function() { console.log("Hello")};

The object constructor is there so we can construct similar type of objects. For instance, with Person object constructor, we can create many people like this

james = new Person("James"); sarah = new Person("Sarah");

Not a big deal. We know this very well. But what does 'new' keyword do under the hood?

Here are the list of things happen when a 'new' keyword was called

  1. Create a new object like james and sarah
  2. Set sarah and james's __proto__ to constructors prototype
  3. Invoke prototype with functions like arguments like name
  4. return an object with all functions that the constructor has like speak, name etc.
var NEW = function(newobj, args) { var sarah = {}; sarah.__proto__ = newobj.prototype newobj.apply(sarah, args); return sarah; }; var sarah = NEW(Person, ['name']) //so our 'NEW' function will create object sarah sarah.speak(); //speak function will be inherited from newobj 'Person'

Call and Apply

Call and Apply are used to invoke function. Call and Apply call a function with a given this value and arguments provided individually.

The difference between call and apply is that call takes individual argument and apply takes array of arguments

First, using call to chain constructors for an object

var Product = function(name, price) { this.name = name; this.price = price; }; var Food = function(name, price) { Product.call(this, name, price); this.category = 'food'; }; Food.prototype = Object.create(Product.prototype); //Create new object and copy the Product.prototype Food.prototype.constructor = Food; //Reset the constructor from Product to Food (single inheritance)

Second, using call to invoke an anonymous function

var animals = [ { species: 'Lion', name: 'King' }, { species: 'Whale', name: 'Fail' } ]; for (var i=0; i "<" animals.length; i++){ (function(i) { this.print = function() { console.log('#' + i + ' ' + this.species + ': ' + this.name); } this.print(); }).call(animals[i], i); }; //#1 Lion: King //#2 Wale: Fail

The first argument of the call is always for 'this'. What we are calling the function on. The second argument is whatever we need to pass on. In this case, 'i'. Otherwise, the print function will not know 'i' and print 'undefined'.