DEV Community

Dahye Ji
Dahye Ji

Posted on

JavaScript Basic - Hoisting, IIFE, Filter(), Map(), Map, Set...

Hoisting***

JavaScript Hoisting refers to the process whereby the interpreter appears to move the declaration of functions, variables or classes to the top of their scope, prior to execution of the code.

var a = "first";
var b = "second";
var c = "third";
var d;
console.log(a);
console.log(b);
console.log(c);
console.log(d);
// first
// second
// third
// undefined
Enter fullscreen mode Exit fullscreen mode

'd' is undefined because there is no value assigned to it here.

var a = "first";
var b = "second";
var c = "third";
console.log(a);
console.log(b);
console.log(c);
console.log(d);
var d = "fourth";
// first
// second
// third
// undefined
Enter fullscreen mode Exit fullscreen mode

However here, 'd' has a value assigned to it but it's still undefined. WHY? It's because it only takes declarations and take them to the top of the document.

var a = "first";
var b = "second";
var c = "third";

d = "fourth"

console.log(d);

var d;

// fourth
Enter fullscreen mode Exit fullscreen mode

Here, it console.log 'd' because when we declared variable, it gets hoisted to the top and because we also assigned a value to it before console.log() happens.
So it sees this as a valid code. This is what we call 'Hoisting'
Declarations in JS get hoisted. (Declaration happens first then saved in the memory after).

+ Let's see what happens with function as well.

console.log(add1(10, 20));
console.log(add2(10, 20));

// The function below goes all the way up because of hoisting!

function add1(x, y) {
  return x + y;
}
Enter fullscreen mode Exit fullscreen mode

Because it's read from top to bottom in JavaScript, you might think the console.log() won't be executed. Because it's written before the function. However, the function add1(x,y) goes to the top because of hoisting and console.log() will be executed.
Named function in the same way as a variable gets taken out of the code and get placed to the top or hoisted to the top of the file. So we can use it at any point inside our code.

console.log(multiply(10, 20));
let multiply = function (a, b) {
  return a * b;
};

// Uncaught ReferenceError: multiply is not defined (error)
Enter fullscreen mode Exit fullscreen mode

But if you use function expression like above code (assign function to variable), only the variable will go to the top because of hoisting(read what I wrote above. It takes variables to the top only not the values). So, the function won't go up.(won't hoist the function in this case because it's assigned to variable as a value). Therefore, the console.log() won't be executed. It's the same with arrow function.

console.log(multiply(10, 20));

const multiply = (a, b) => a * b;
Enter fullscreen mode Exit fullscreen mode

This also gets an error because arrow function works like function expression above.
Video that helps to understand Hoisting
About hosting on MDN

How we avoid hoisting?

Don't create variables out in the root of file as long as it's unnecessary and instead, put them inside function if you are going to use the variables inside the function.

Then, how do we avoid function being hoisted?

So it's where it comes 'anonymous function'. Anonymous function doesn't have a name attached to it. So, the way we do it is by creating variable and assigning it to the function.


const sayA = function() {
  let a = 20;
  return a;
} 

console.log(sayA());
Enter fullscreen mode Exit fullscreen mode
console.log(sayA());

const sayA = function() {
  let a = 20;
  return a;
} 
// if you move the function to below, like this then you can check that it gets an error. because the browser doesn't know if there is a function because it's not named function.
Enter fullscreen mode Exit fullscreen mode

IIFE(Immediately Invoked Function Expression)

IIFE is a function that runs as soon as it is defined(like its name).
Syntax

(function () {
  statements
})();
Enter fullscreen mode Exit fullscreen mode

You just have to wrap the function with '()' -parentheses to create IIFE.

Pros using IIFE

  • Self(auto)-invocation (It runs as soon as it's defined)
  • It's function scope. (function or variable defined inside IIFE, cannot be accessed outside the IIFE block)
  • It's memory efficient.
// Anonymous IIFE.
(function () {
    let a = 1;
    let b = 2;
    return a+ b;
}, ());
// It a code that starts the first.
// Usually use this when there is code that needs to be start as soon as it renders.

// Named IIFE
(function foo() {
  let a = 3;
  let b = 5;
  return a * b;
}());

// This does NOT work
foo(); // ReferenceError: foo is not defined
Enter fullscreen mode Exit fullscreen mode

Call by value / Call by reference

When you call function and pass argument to it, it doesn't pass the original value itself but pass copied version of the value as function. Thus, when the value get changed within the function, it doesn't affect the original value.

let x = [10, 20, 30];

function test(a) {
  // 'a' is parameter
  // when 'a' received the value, it gets the copy of address of x value
  // 'a' points [10, 20, 30]
  a[0] = 10000;
}
test(x); // 'x' is argument
x;
//[(10000, 20, 30)];


let x = 10;
function test(a) { // 'x' points 10
  a = 10000; // but 'a' points 10000
}
test(x); // x is argument
x;
// 10
// 'x' points 10 so it returns 10

///
Enter fullscreen mode Exit fullscreen mode

Video for better understanding: JavaScript Value vs Reference Types

map()

map() method creates a new array populated with the results of calling a provided function on every element in the calling array.
Example

let array = [1, 4, 9, 16];
let valueOne = array.map((x) => x * 2);

array;
//[ 1, 4, 9, 16]
valueOne;
// [2, 8, 18, 32]


function square(x) {
  return x ** 2;
}
valueTwo = array.map(square);
// [1, 16, 81, 256]
array;
// [1, 4, 9, 16]
valueTwo;
// [1, 16, 81, 256]
Enter fullscreen mode Exit fullscreen mode

Using map() with object

let data = [
  {
    class: 1,
    number: 1,
    name: "Chloe",
    "test result": 91,
  },
  {
    class: 1,
    number: 2,
    name: "James",
    "test result": 70,
  },
  {
    class: 1,
    number: 3,
    name: "Kate",
    "test result": 64,
  },
  {
    class: 1,
    number: 4,
    name: "Mark",
    "test result": 89,
  },
  {
    class: 1,
    number: 5,
    name: "Jenny",
    "test result": 100,
  },
];

data.map((num) => num["test result"]);
// [91, 70, 64, 89, 100]
// 0: 91
// 1: 70
// 2: 64
// 3: 89
// 4: 100

let sum = 0;
data.map((x) => x["test result"]).forEach((y) => (sum += y));
sum;
// 414
Enter fullscreen mode Exit fullscreen mode

Using map() with array

let array = [1, 2, 3, 4];
let result = [];
let data = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9],
];

let newData = data.map((a) => a.map((n) => n * 2));
newData;
// [Array(3), Array(3), Array(3)]
// 0: [2, 4, 6]
// 1: [8, 10, 12]
// 2: [14, 16, 18]
Enter fullscreen mode Exit fullscreen mode

More about map()

filter()

filter() method creates a new array with all elements that pass the test implemented by the provided function.

const words = [
  "good",
  "nice",
  "magnificent",
  "awesome",
  "spectacular",
  "amazing",
  "cool",
];

const result = words.filter((word) => word.length > 7);
console.log(result);
// ['magnificent', 'spectacular']


let x = [2, 6, 5, 4, 8, 5, 4, 3, 2, 9];
// numbers that are greater than 5 from array x
const result = x.filter((num) => num > 5);
result;
// [6, 8, 9]
Enter fullscreen mode Exit fullscreen mode

Method chaining

Method chaining is the mechanism of calling a method on another method of the same object. This ensures a cleaner and readable code. Method chaining uses this keyword in the object's class to access its methods. In javascript, the this keyword refers to the current object in which it is called

'1001001010'.replace('1', '#').replace('0', ' ')
// '# 01001010'
'1001001010'.replace(/1/g, '#').replace(/0/g, ' ')
// '#  #  # # 's
Enter fullscreen mode Exit fullscreen mode
// let array = [1, 4, 9, 16]

// method chaining inside map()
// square root: Math.sqrt()
array.map(Math.sqrt);
// [1, 2, 3, 4]


array.map(Math.sqrt).map((x) => x ** 3);
//(4) [1, 8, 27, 64]
array;
// (4) [1, 4, 9, 16]
array.map((x) => x ** 0.5);
// (4) [1, 2, 3, 4]
Enter fullscreen mode Exit fullscreen mode

method chaining practice

let userInfo = [
  {
    id: "jjang",
    pw:
      "5dbfbded05f34de754e8f265448be17934580556b488c0461fd12d211a87aaa5",
    gender: "male",
    phone: "010-5004-0000",
    email: "hojun1@gmail.com",
    "joined date": "2020-12-02",
    location: "125.242.161.149",
  },
  {
    id: "jjang2",
    pw:
      "5dbfbded05f34de754e8f265448be17934580556b488c0461fd12d211a87aaa5",
    gender: "male",
    phone: "010-5004-0000",
    email: "hojun2@gmail.com",
    "joined date": "2021-12-02",
    location: "125.242.161.149",
  },
  {
    id: "jjang3",
    pw:
      "5dbfbded05f34de754e8f265448be17934580556b488c0461fd12d211a87aaa5",
    gender: "female",
    phone: "010-5004-0000",
    email: "hojun3@gmail.com",
    "joined date": "2021-12-02",
    location: "125.242.161.149",
  },
  {
    id: "jjang4",
    pw:
      "5dbfbded05f34de754e8f265448be17934580556b488c0461fd12d211a87aaa5",
    gender: "female",
    phone: "010-5004-0000",
    email: "hojun4@gmail.com",
    "joined date": "2020-12-02",
    location: "125.242.161.149",
  },
];

// filter male only
userInfo.filter((el) => el.gender === "male");

// filter user who's male and joined in 2021
userInfo.filter(
  (el) => el.gender === "male" && el["joined date"].split("-")[0] === "2021");

// filter who's id is "jjang"
// userInfo.filter((el) => el.id === "jjang");
userInfo.find((user) => user.아이디 === "jjang");
// find stops when it finds the one looking for (good for memory efficiency)
Enter fullscreen mode Exit fullscreen mode

** When id or password doesn't match
Do not let the user know "ID doesn't match" or "PW doesn't match". Because if you do, then you are letting someone know which one isn't matching. This is not good for security.
Instead, do it like "ID or PW don't match" so the person who sees this doesn't know which one isn't matching exactly.(This is for security reason)

more practice

let blogs = [
  {
    id: 1,
    title: "title1",
    content: "content1",
    section: "daily life",
  },
  {
    id: 2,
    title: "title2",
    content: "content2",
    section: "hobby",
  },
  {
    id: 3,
    title: "title3",
    content: "content3",
    section: "web development",
  },
  {
    id: 4,
    title: "title4",
    content: "content4",
    section: "web development",
  },
];

let sectionName = "web development";
let data = "web development" ? blogs.filter((el) => el.section === sectionName) : blogs;

data;
// [{…}, {…}]
// 0: {id: 3, title: 'title3', content: 'content3', section: 'web development'}
// 1: {id: 4, title: 'title4', content: 'content4', section: 'web development'}

// when there is error or can't find, you should show a message saying you can't find or return all the contents
Enter fullscreen mode Exit fullscreen mode

Object

let obj = {
  keyA: "value of A",
  keyB: "value of B",
};

Object.entries(obj);
// [Array(2), Array(2)]
// 0: (2) ['keyA', 'value of A']
// 1: (2) ['keyB', 'value of B']

Object.keys(obj);
// ['keyA', 'keyB']
// 0: "keyA"
// 1: "keyB"

Object.values(obj);
// ['value of A', 'value of B']
// 0: "value of A"
// 1: "value of B"

Object.is(0, -0);
// false

for (let i of obj) {
  console.log(i);
}
// Uncaught TypeError: obj is not iterable.
// You cannot iterate object in JS. You can do that only with for...in

for (let i in obj) {
  console.log(i);
}
// keyA
// keyB
Enter fullscreen mode Exit fullscreen mode

Object.entries()
Object.keys()
Object.values()

Map & Set

The Map() constructor creates Map objects.

Till now, we’ve learned about the following complex data structures:

  • Objects are used for storing keyed collections.
  • Arrays are used for storing ordered collections.

Map

Map is a collection of keyed data items, just like an Object. But the main difference is that _**Map allows keys of any type.

Methods of Map

  • new Map(): creates the map.
  • map.set(key, value): stores the value by the key.
  • map.get(key): returns the value by the key, undefined if key doesn’t exist in map.
  • map.has(key): returns true if the key exists, false otherwise.
  • map.delete(key): removes the value by the key.
  • map.clear(): removes everything from the map.
  • map.size: returns the current element count.(length)
let map = new Map();

map.set('1', 'str1');   // a string key
map.set(1, 'num1');     // a numeric key
map.set(true, 'bool1'); // a boolean key

// remember the regular Object? it would convert keys to string
// Map keeps the type, so these two are different:
console.log(map.get(1)); // 'num1' // using index
console.log(map.get('1')); // 'str1' // using key

console.log(map.size); // 3
Enter fullscreen mode Exit fullscreen mode

As you can see above, unlike objects, keys are not converted to strings. Any type of key is possible.

NOTE: map[key] isn’t the right way to use a Map
Although map[key] also works, e.g. we can set map[key] = 2, this is treating map as a plain JavaScript object, so it implies all corresponding limitations (only string/symbol keys and so on).
So we should use map methods: set, get and so on.

Iteration over Map

For looping over a map, there are 3 methods:

  • map.keys() – returns an iterable for keys,
  • map.values() – returns an iterable for values,
  • map.entries() – returns an iterable for entries [key, value], it’s used by default in for..of.

Set

A Set is a special type collection – "set of values" (without keys), where each value may occur only once.

Method of Set

  • new Set(iterable): creates the set, and if an iterable - object is provided (usually an array), copies values from it into the set.
  • set.add(value): adds a value, returns the set itself.
  • set.delete(value): removes the value, returns true if value existed at the moment of the call, otherwise false.
  • set.has(value): returns true if the value exists in the set, otherwise false.
  • set.clear(): removes everything from the set.
  • set.size: is the elements count.

The main feature is that repeated calls of set.add(value) with the same value don’t do anything. That’s the reason why each value appears in a Set only once.

For example, we have visitors coming, and we’d like to remember everyone. But repeated visits should not lead to duplicates. A visitor must be "counted" only once.
Set is just the right thing for that:

let set = new Set();

let hana = { name: "Hana" };
let lilly = { name: "Lilly" };
let hailey = { name: "Hailey" };

// visits, some users come multiple times
set.add(hana);
set.add(lilly);
set.add(hailey);
set.add(lilly);
set.add(hailey);
//Set(3) {{…}, {…}, {…}}

// set keeps only unique values
console.log(set.size);
// 3

for (let user of set) {
 console.log(user.name);
}
// Hana
// Lilly
// Hailey 
Enter fullscreen mode Exit fullscreen mode

Iteration over Set

We can loop over a set either with for..of or using forEach

let set = new Set(["blue", "black", "white"]);

for (let value of set) console.log(value);
// blue
// black
// white

// the same with forEach:
set.forEach((value, valueAgain, set) => {
  console.log(value);
});
// blue
// black
// white
Enter fullscreen mode Exit fullscreen mode

practice map

let m = new Map();

m.set("first", "one");
m.set("second", "two");
m.set("third", "three");

m; 
// Map(3) {'first' => 'one', 'second' => 'two', 'third' => 'three'}
// [[Entries]]
// 0: {"first" => "one"}
// 1: {"second" => "two"}
// 2: {"third" => "three"}

m.get("first");
// 'one'
m.get("second");
// 'two'
m.get("third");
// 'three'

m.keys();
// MapIterator {'first', 'second', 'third'}
// [[Entries]]
// 0: "first"
// 1: "second"
// 2: "third"

m.set("first", "one!!");
// Map(3) {'first' => 'one!!', 'second' => 'two', 'third' => 'three'}
// [[Entries]]
// 0: {"first" => "one!!"}
// 1: {"second" => "two"}
// 2: {"third" => "three"}

m.values();
// MapIterator {'one!!', 'two', 'three'}
// [[Entries]]
// 0: "one!!"
// 1: "two"
// 2: "three"

let n = new Number(5);
n;
//Number {5}
n = 5;
n;
// 5

let a = new Array([1, 2, 3]);

a;
// [Array(3)]

typeof a;
// 'object' // let a = new Array([1, 2, 3]);
typeof n;
// 'number' // assigned number, n = 5;

typeof m;
// 'object' // let m = new Map();

m.has("first");
// true
m.delete("first");
// true
m;
// Map(2) {'second' => 'two', 'third' => 'three'}

m;
// Map(2) {'second' => 'two', 'third' => 'three'}
for (let i of m) {
  console.log(i);
}
//(2) ['second', 'two']
//(2) ['third', 'three']

// You can't iterate object with for of but m is object created with new Map() and it's iterable as you can see above.


let obj1 =  {
  keyA: "value of A",
  keyB: "value of B",
};
Object.entries(obj1);

Object.length(obj1)
// Uncaught TypeError: Object.length is not a function

Object.keys(obj1).length;
// 2

m.size; // it's to find out the length 
// 2

m.entries();
// MapIterator {'second' => 'two', 'third' => 'three'}

let temp = new Map([
  [1, 10],
  [2, 20],
  [3, 30],
  [4, 40],
]);

temp;
// Map(4) {1 => 10, 2 => 20, 3 => 30, 4 => 40}

temp.size;
// 4 

let temp = new Map(Object.entries(obj1));

temp;
// Map(2) {'keyA' => 'value of A', 'keyB' => 'value of B'}
temp.size;
// 2

let mm = new Map();
mm.set("one", 1);
// Map(1) {'one' => 1}
mm.set("two", { one: 1, two: 2 });
// Map(2) {'one' => 1, 'two' => {…}}
// [[Entries]]
// 0: {"one" => 1}
// 1: {"two" => Object}

mm;
// Map(2) {'one' => 1, 'two' => {…}}
// [[Entries]]
// 0: {"one" => 1}
// key: "one"
// value: 1
// 1: {"two" => Object}
// key: "two"
// value:
// one: 1
// two: 2

mm.set([1, 2, 3], "three");
// Map(3) {'one' => 1, 'two' => {…}, Array(3) => 'three'}
// [[Entries]]
// 0: {"one" => 1}
// 1: {"two" => Object}
// 2: {Array(3) => "three"}

Enter fullscreen mode Exit fullscreen mode

mm.set

practice set

let s = new Set("abcdeeeeeeeee");
console.log(s);
// {'a', 'b', 'c', 'd', 'e'}
console.log(s.size);
// 3 5

s.add("f");
// Set(6) {'a', 'b', 'c', 'd', 'e', …}[[Entries]]0: "a"1: "b"2: "c"3: "d"4: "e"5: "f" size: 6[[Prototype]]: Set
// Set is not an ordered abstract data structure.

s.delete("b");
//true
s;
// Set(5) {'a', 'c', 'd', 'e', 'f'}
s.has("c");
// true
s.size;
// 5
s.forEach((i) => console.log(i));
// a
// c
// d
// e
// f

let a = new Set("abc");
let b = new Set("cde");

a;
// Set(3) {'a', 'b', 'c'}
b;
// Set(3) {'c', 'd', 'e'}

// Intersection
[...a].filter((value) => b.has(value));
// ['c']
a | b;
// 0
a & b;
// 0
a || b;
// Set(3) {'a', 'b', 'c'}[[Entries]]0: "a"1: "b"2: "c"size: 3[[Prototype]]: Set
a && b;
// Set(3) {'c', 'd', 'e'}
a + b;
// '[object Set][object Set]'

let union = new Set([...a].concat(...b));

union;
// Set(5) {'a', 'b', 'c', 'd', 'e'}
[...a, ...b];
// (6) ['a', 'b', 'c', 'c', 'd', 'e']
Enter fullscreen mode Exit fullscreen mode

more about Map & Set

Spread

let numbers = [0, 1, 2];
let newNum = 12;
numbers = [...numbers, newNum];

numbers;
// [0, 1, 2, 12]
Enter fullscreen mode Exit fullscreen mode

Rest

function checkRest(x, y, z) { 
 console.log(x, y, z);
}

let numbersArr = [0, 1, 2];

checkRest(numbersArr);
// [0, 1, 2]
checkRest(...numbersArr);
// 0, 1, 2


function restFunc(v, w, x, y, z) {
    console.log(v,w,x,y,z);
 }

let arr = [0, 1];
restFunc(-1, ...arr, 2, ...[3]);
// -1 0 1 2 3
Enter fullscreen mode Exit fullscreen mode

more about spread & rest

Discussion (0)