- Anthony Alicea, follow him on Twitter
- Javascript Basics
- JavaScript: Understanding the Weird Parts in 35ish minutes - Introduction
- JavaScript: Understanding the Weird Parts in 35ish minutes - Part 1/6
- JavaScript: Understanding the Weird Parts in 35ish minutes - Part 2/6
- Willingness to learn
- 35 min
In javascript, objects and functions are very much related
- Object and the dot
- The object can have primitive types connected to it's “property”
- An object can have an object connected to it as a child “property”
- An object can also contain functions “method” So an object can have properties and methods and these sit in the memory. The following example shows how to access these methods and properties.
Example 1
var person = new Object();
//person will get reference of the firstname in memory ie string property (primitive)
person[“firstname”] = “Tony”;
//accessing the property
var firstNameProperty = “firstname”;
console.log(person[firstNameProperty]);
//accessing property with dot operator
console.log(person.firstname);
//creating object inside object
person.address = new Object();
//using dot operator to add properties
person.address.street = “111 Main st.”;
var person = {}; //object literal same as new Object(); but this is much faster
var Tony = {
firstname:’Tony’
};
function greet (person){
console.log(“Hi ” + person.firstname);
}
greet(Tony);
//Create an object on the fly
greet({firstname:’Tony’,lastname:’Doe’});
#BIGWORD Namespace : A container for variables and functions, Typically to keep variables and functions with the same name separate.
var greet = “hello”;
var greet = “hola”;
console.log(greet);
//create an object, that would work as a container for our methods and properties
var english = {};
var spanish = {};
//now following greet will not collide with each other
english.greet = “hello”;
spanish.greet = “hola”;
console.log(english);
JSON - Javascript Object Notation, It’s inspired by object literals
Example 1
//Object Literal
var objectLiteral = {
firstName : “Mary”,
isAProgrammer : true
}
In previous years data was sent over the internet in various formats and the format landed upon a while is XML. Problem was download time, Because it was using lots of bandwidth if you are dealing with lot of data so Object literal format was used to pass data in JSON string format. The only difference is properties are wrapped in quotes in JSON
//JSON - Subset of object literal syntax
{
“firstName”: “Mary”,
“isAProgrammer”: true
}
#BIGWORD First Class Function (FCF) - Everything you can do with other types you can do with functions. Assign them to variables, pass them around, create them on the fly.
- Functions reside in memory
- It’s a special type of object because it has all the features of the normal object as well as some special properties
- We can attach primitive, object, functions to a function
Two special properties of functions are Name - optional,can be anonymous Code - The actual lines of code you have written is set in this property. The code you write is one of the property you are adding one to it. This property is invocable
Example 1
function greet(){
console.log(“hi”);
}
//As we know functions are the objects, we can add a property to it
greet.language = “english”;
console.log(greet);
Output - we get only text the function we wrote
function greet(){
console.log(“hi”);
}
console.log(greet.language); //english
#BIGWORD Expression - A unit of code that results in a value, It doesn’t have to save to a variable
//Function expression
a = 3
returns 3
//Function statement
function greet(){
console.log(“hi”);
}
Example 1 - As we know functions are objects in js. Here we are creating a function on the fly and setting it equal to a variable.
var anonymousGreet = function(){
console.log(‘hi’);
}
// Here we still get function object. JS engine creates one and in this case, NAME property is anonymous but you can access it with a reference i.e variable name and CODE i.e invocable is console.log(“hi”);
// We can call this function using the following statement.
anonymousGreet();
Example 2
greet(); // OUTPUT : hi
function greet(){
console.log(“hi”);
}
anonymousGreet(); // OUTPUT : Uncaught TypeError, Undefined is not a function
// REASON : Because of function expressions are not hoisted.
var anonymousGreet = function(){
console.log(“hi”);
}
Example 3
greet(); // OUTPUT : hi
function greet(){
console.log(“hi”);
}
var anonymousGreet = function(){
console.log(“hi”);
}
anonymousGreet(); // OUTPUT : hi
function log(a){
console.log(a);
}
log(3); //creating a value on the fly -- OUTPUT : 3
log(“hello”); //creating a string on the fly -- OUTPUT : hello
log({
greeting: ‘hi’
}); //creating an object on the fly -- OUTPUT : {greeting:”hi”}
log(function(){
console.log(‘hi’);
});//creating anonymous function on the fly -- OUTPUT : function(){ console.log(‘hi’);}
Example 4 - Executing function created on the fly , Concept of first class functions where you can pass function around as a parameter, use them like variables called functional programming.
function log(a){
a()
}
log(function(){
console.log(“hi”);
});///OUTPUT : hi
Pass by value
If a variable is primitive and it’s equated to other variables then a copy of the primitive value is created.
a = 3 // primitive value, It will sit in one address location in memory
b = a // copy of primitive value will be assigned to b, It will sit in other address location in memory
Pass by reference (objects)
If we set a variable equals to an object we get a location an address in memory that knows where that object lives and that’s how we reference it but when other b = a or we pass a to a function that new variable b instead of getting a new location in memory simply points to the location in memory a does, No new object is created, no copy of the object is created instead 2 names point to the same memory location This is called by reference
Example 1
//by value (primitives)
var a = 3;
var b;
b=a;
a = 2;
console.log(a); //2
console.log(b); //3
New a will be 3 and b will be 3 sitting on different memory spots. That means we can change a and it will not affect b they are on their own.
//by reference (all objects (including functions))
var c = { greeting:’hi’};
var d;
d=c;
Now they are not copies of each other
c.greeting = ‘hello’;//mutate
#BIGWORD mutate - To change something “immutable can’t be changed”
console.log(c); // {greeting : “hello”}
console.log(d); // {greeting : “hello”}
//by reference (even as parameters)
function changeGreeting(obj){
obj.greeting = ‘hola’; //mutate
}
changeGreeting(d);
console.log(c); // {greeting : ‘hola’}
console.log(d); // {greeting : ‘hola’}
//equals operator sets up new memory space (new address)
c = {greeting : ‘howdy’}
console.log(c); // {greeting : ‘howdy’}
console.log(d); // {greeting : ‘hola’}
//this
Example 1
console.log(this); //this is immediately available even at the global execution context, window object (global object)
Example 2
function a(){
console.log(this); //window object
//we can attach new variable to the window object
this.newVariable = ‘hello’;
}
a();
console.log(newVariable);
Explanation - when you create a function the this
keyword is still going to point the global object
Similarly,
Example 3
//var b gets a functional express by creating an anonymous function
var b = function a(){
console.log(this);
}
b(); //still window object
Example 4
var c = {
name : ‘The c object’,
log : function(){
console.log(this); //the object in which the method is sitting inside of
}
}
c.log(); // Object {name : “The c object” ,log: function(){ console.log(this)}}
Example 5
var c = {
name : ‘The c object’,
log : function(){
this.name = ‘Updated c object’; //mutate - change the object properties
console.log(this); //the object in which the method is sitting inside of
}
}
c.log(); // Object {name : “Updated c object” ,log: function(){ this.name = ‘Updated c object’;console.log(this)}}
Example 6
var c = {
name : ‘The c object’,
log : function(){
this.name = ‘Updated c object’; //mutate - change the object properties
console.log(this); //the object in which the method is sitting inside of
var setname = function(newname){
this.name = newname;
}
// didn’t work on c but added new property to window object
setname(‘Updated again!, The c object’);
console.log(this);
}
}
c.log(); // Object {name : “Updated c object” ,log: function(){ this.name = ‘Updated c object’;console.log(this);var setname = function(newname){this.name = newname;} setname(‘Updated again!, The c object’);console.log(this);}}
A very common pattern can b used in this case to avoid errors in code because of “this”
var c = {
name : ‘The c object’,
log : function(){
var self = this; //self will be pointing at the same location in memory as this
self.name = ‘Updated c object’; //mutate - change the object properties
console.log(self); //the object in which the method is sitting inside of
var setname = function(newname){
self.name = newname; //js will look for self in scope chain
}
//will work now
setname(‘Updated again!, The c object’);
console.log(self);
}
}
c.log(); //Object {name : “Updated again!, The c object” ,log: function(){ this.name = ‘Updated c object’;console.log(this);var setname = function(newname){this.name = newname;} setname(‘Updated again!, The c object’);console.log(this);}}
An array can be declared as
var arr = new Array();
var arr = [1,2,3];
JS arrays are dynamically typed.
var arr = [
1,
false,
{
name : ‘Tony’,
address : ‘22 Main st’
},
//it’s a function expression not statement inside array declaration
function(name){
var greeting = “hello”;
console.log(greeting + name);
},
“hello”
];
JS array can hold collections of anything. Invoking function in an array with-param in array
arr[3](arr[2].name);
#BIGWORD Arguments - The parameters you pass to a function, javascript gives you a keyword contains a list of all the values of all the parameters that you pass to a function.
Example 1
function greet(firstname, lastname, language){
console.log(firstname);
console.log(lastname);
console.log(language);
}
//this function can be invoked without being any params passed to it
greet();
OUTPUT
undefined
undefined
undefined
greet(‘John’);
OUTPUT
John
undefined
undefined
greet(‘John’,’Doe’);
OUTPUT
John
Doe
undefined
greet(‘John’,’Doe’,’es’);
OUTPUT
John
Doe
es
Example 2
//setting up a default value
function greet(firstname, lastname, language){
language = language || ’en’;
console.log(firstname);
console.log(lastname);
console.log(language);
}
greet(‘John’,’Doe’);
OUTPUT
John
Doe
en
New Keyword #ARGUMENTS
Example 3
function greet(firstname, lastname, language){
if(arguments.length === ‘0’){
console.log(‘Missing parameters’);
return;
}
language = language || ’en’;
console.log(firstname);
console.log(lastname);
console.log(language);
console.log(arguments);
}
greet(‘John’,’Doe’);
OUTPUT
John
Doe
en
[‘John’,’Doe’] // containing all the values of params we have passed
Arguments - looks like an array, act like an array but not exactly the javascript array. It doesn’t have all the features of the javascript array
SPREAD
// using ...name (ES6)
// wrapping up all other arguments in ...other param
function greet(firstname, lastname, language, ...other){
if(arguments.length === ‘0’){
console.log(‘Missing parameters’);
return;
}
language = language || ’en’;
console.log(firstname);
console.log(lastname);
console.log(language);
console.log(arguments);
}
greet(‘John’,’Doe’,’es’,’111 main st’,’new york’);
Function Overloading means we can have a function of the same name with the different number of params. This thing is not available in js because functions are objects.
function greet ( firstname,lastname,language ) {
language = language || ‘en’; //default param
if(language == ‘en’){
console.log(“Hello ” + firstname + “ ” + lastname);
}
if(language == ‘es’){
console.log(“Hola ” + firstname + “ ” + lastname);
}
}
greet(‘John’,’Doe’,’en’); //Hello John Doe
greet(‘John’,’Doe’,’es’); //Hola John Doe
Code doesn’t directly run on a computer there is an intermediate program b/w code and the computer that translates your code into something that a computer could understand. The js engine for eg does this and it has different aspects to it one of being a syntax parser which reads the code and determines what it does
Semicolons are optional in core-js The syntax parser in the js engine injects automatically the semicolon at the end of the required syntax.
Example 1
function getPerson(){
return
{
firstname:’Tony’
}
}
console.log(getPerson()); //gives error as it adds semicolon after return
Following will work
function getPerson(){
return {
firstname:’Tony’
}
}
#BIGWORD Whitespace - Invisible characters that create literal ‘space’ in your written code, Carriage returns, tabs, spaces
Example 1
var
//first name of the person
firstname,
//last name of the person
lastname,
//the language
//can be ‘en’ or ‘es’
language;
var person = {
//the first name
firstname: ‘John’,
//the last name
//(always required)
lastname: ‘Doe’
}
console.log(person);
Example 1
//function statement
function greet(name){
console.log(name)
}
//invoke it with following line
geet();
//function expression , creating function object on the fly
var greeFunc = function(name){
console.log(name);
}
//invoke it with following line
greetFunc();
//IIFE - Immediately invoked functions expression
Example 1
var greeting = function(name){
return name;
}(‘John’);
console.log(greeting); // OUTPUT - John
Example 2
3; //this is a valid js expression, it will not throw error message
Example 3
“I’m a string” //this is also a valid js expression, it will not throw an error message
Example 4
{
name : ‘john’
}
Example 5
//This will throw an error - unexpected token parenthesis
//REASON - Syntax parser expects it to be function statement
function (name){
return (name);
}
Example 6
//This will not throw any error, everything inside parenthesis is assumed as an expression so this will be treated as a function expression. Here you are creating function object on the fly
(function (name){
return (name);
});
Example 7
//A classic example of IIFE
//Writing a function expression and executing it on the fly
(function (name){
console.log(“Inside IIFE” + name);
}(‘John’))
//OUTPUT : Inside IIFE John
Example 1
//IIFE
(function (name){
var greeting = “Inside IIFE ”;
console.log(greeting + name);
}(‘John’))
//OUTPUT: Inside IIFE John
Explanation - Creating function on the fly and executing it, When the code is executed It creates a global execution context and nothing is in it as we don’t have any global variables or function statements to be hoisted. Then it hits immediately invoked function expression. Then it creates that function object in memory. Then a new execution context is created for that anonymous function then code is run line by line in that function. All variables will be declared in that new execution context
What is a closure? A closure is an inner function that has access to the outer (enclosing) function’s variables—scope chain. The closure has three scope chains: it has access to its own scope (variables defined between its curly brackets), it has access to the outer function’s variables, and it has access to the global variables.
The inner function has access not only to the outer function’s variables but also to the outer function’s parameters. Note that the inner function cannot call the outer function’s arguments to object, however, even though it can call the outer function’s parameters directly.
Closure from other resources:
Example 1
function greet(whattosay){
return function(name){
console.log(whattosay + ‘ ’ + name);
}
}
//We are invoking a function that returns a function so that we can invoke the returned function
greet(‘Hi’)(‘Tony’); // OUTPUT : Hi Tony
Example 2
function greet(whattosay){
return function(name){
console.log(whattosay + ‘ ’ + name);
}
}
//sayHi will be the function which was returned by calling greet
var sayHi = greet(‘Hi’);
sayHi(‘Tony’) //OUTPUT : Hi Tony;
Now how does sayHi
function knows the whattosay
variable?
Explanation -
When this code starts we have global execution context then we invokes greet function and created new execution context of greet()
and that variable which was passed to it whattosay
is sitting in its variable environment, it returns a new function object and creates a function on the fly and returns it. After that return the greet execution context is popped off the stack.
Under normal circumstances, js engine clears out the memory space using garbage collection but at the moment that execution context finishes that memory space is still there for (whattosay)
variable, It’s still hanging around now we move on the global execution context then we invoke the function sayHi()
. It’s an anonymous function and that creates a new execution context and we have name variable to it so it ends up in memory.
When we hit a line console.log(whattosay + ‘ ’ + name);
when this code is invoked js engine sees whattosay
variable it goes up the scope chain, it goes to outer lexical environment, in this case, to look for the variable since it couldn’t find it inside the function itself. sayHi
has reference to the variable whattosay
which is available in the memory space of its outer environment. This phenomenon is called closure.
Closures are simply the feature of the javascript programming language. We don’t have to worry if it’s out environment is still running. Js engine handles the reference to variables in the lexical out environment.
The js engine creates the closure we are just taking advantage of it.
Example 1
function buildFunctions(){
var arr = [];
for(var i =0 ;i <3;i++){
//creating the function, not executing
arr.push(
function(){
console.log(i);
})
}
return arr;
}
var fs = buildFunctions();
//executing the functions
fs[0]();//3
fs[1]();//3
fs[2]();//3
Why?
Global execution context is always there buildFunctions()
, fs
When buildFunctions
get invoked we get arr [f0,f1,f2]
and i 3
After this buildFunctions execution context is popped off the stack
fs[0]()
execution context is created there is no variable i inside the scope so it goes up the scope chain if finds i = 3
hangging around then fs[1]()
and then fs[2]()
also has same outer environment reference
Overcoming this problem
function buildFunctions2(){
var arr = [];
for(var i =0 ;i <3;i++){
//the only way to get execution context of i in function is by executing the function
//creating the function, not executing
arr.push(
//IIFE - immediately invoke function expression
(function(j){
console.log(j);
}(i))
)
}
return arr;
}
function buildFunctions2(){
var arr = [];
for(var i =0 ;i <3;i++){
arr.push(((j) => {
return () => console.log(j)
})(i))
}
return arr;
}
var fs2 = buildFunctions2();
//executing the functions
fs2[0]();//1
fs2[1]();//2
fs2[2]();//3
The factory is a function which returns and makes other things for app
Example 1
function makeGreeting(language){
return function(firstname, lastname){
if(language==’en’){
console.log(“Hello” + firstname + lastname);
}
if(language==’es’){
console.log(“Hola” + firstname + lastname);
}
}
}
var greetEnglish = makeGreeting(‘en’);
var greetSpanish = makeGreeting(‘es’);
greetEnglish(‘John’,’Doe’);
greetSpanish(‘John’,’Doe’);
Example 1
function sayHiLater(){
var greeting = ‘Hi!’;
setTimeout(function(){
console.log(greeting);
},3000)
}
sayHiLater(); //It will wait 3000 milliseconds and then prints hi
#BIGWORD - Callback function - A function you give to another function, to be run when the other function is finished. So the function you call (ie invoke) ‘call back’ by calling the function you gave it when it finishes.
Example 2 - Callback function
function tellMeWhenDone(callback){
var a = 1000;
var b = 2000;
callback(); //the callback, it runs the function i give it.
}
tellMeWhenDone(function(){
alert(‘I am done’);
});
Example 1
var person = {
firstname: ‘John’,
lastname: ‘Doe’,
getFullName: function(){
var fullname = this.firstname + this.lastname;
return fullname;
}
}
var logName = function(lang1,lang2){
console.log(‘logged’ + this.getFullName();
}
logName() // this will fail.
var logPersonName = logName.bind(person);
logPersonName();
Example 2
var person = {
firstname: ‘John’,
lastname: ‘Doe’,
getFullName: function(){
var fullname = this.firstname + this.lastname;
return fullname;
}
}
var logName = function(lang1,lang2){
console.log(‘logged’ + this.getFullName();
}.bind(person);
logName() // this will work.
#.bind creates copy of the function we are calling it on
Example 3 - Call is used to call the function
var person = {
firstname: ‘John’,
lastname: ‘Doe’,
getFullName: function(){
var fullname = this.firstname + this.lastname;
return fullname;
}
}
var logName = function(lang1,lang2){
console.log(‘logged’ + this.getFullName();
}
logName() // this will fail.
// calling with reference object and params
logName.call(person,’en’.’es’);
Example 4 - apply - does the same exact thing as a call but it takes an array as a parameter
logName.appy(person,[’en’.’es’]);
Example 5 - Creating it on the fly and calling it using apply
(function(lang1,lang2){
console.log(‘logged’ + this.getFullName();
}).apply(person,[‘en’,’es’]);
We can use call, apply in function borrowing in objects
function currying - binding (creating new copy of the function)
function multipy(a,b){
return a*b;
}
//bind with params, bind is not executing the function
//giving params sets permanent values with bind when copy of function is made
//var a = 2 in following function
var multipyByTwo = multipy.bind(this,2)
multipyByTwo(3); // OUTPUT - 6
#BIGWORD Function Currying - Creating a copy of the function with some preset parameters - Very useful in mathematical situations
First Class Functions - Functions that behaves like objects, passed as parameters, return them from functions
Example 1 - Creating new array out of given array
var arr1 = [1,2,3];
console.log(arr1);
var arr2 = [];
for(var i=0;i<arr1.length;i++){
arr2.push(arr1[i]*2);
}
console.log(arr2);
Example 2 - Creating new array out of given array using functional programming
function mapForEach(arr, fn){
var newArr = [];
for(var i=0; i<arr.length; i++){
newArr.push(fn(arr[i]);
}
return newArr;
}
var arr1=[1,2,3];
//multiply by 2 and copying values in array
var arr2 = mapForEach(arr1,function(item){
return item*2;
});
//OUTPUT
arr2 = [2,4,6];
//check if item is greater than 2 and creating new array of boolean items
var arr3 = mapForEach(arr1,function(item){
return item>2;
});
//OUTPUT
arr3 = [false, false, true];
Example 3
var checkPastLimit = function(limiter, item){
return limiter < item;
}
//created copy of checkPastLimit on the fly and passing limiter = 1
var arr4 = mapForEach(arr1, checkPastLimit.bind(this, 1));
console.log(arr4);
//OUTPUT
arr4 = [false,true,true];
//passing only limit to a function and returning checkPastLimit
var checkPastLimitSimplified = function (limiter){
return function(limter, item){
return limiter < item;
}.bind(this,limiter); //using bind to set first param pre-set
};
var arr5 = mapForEach(arr1,checkPastLimitSimplified(1));
//OUTPUT
arr5 = [false,true,true];
//Refer - underscorejs.org , lodash.com
If you want a gist of the actual document, subscribe to the newsletter.
Unlike life, keyboards do have shortcuts, press COMMAND+D
to make this an easily accessible resource by bookmarking it.