본문 바로가기

프로그래밍/개발일지

2주차

8일차 (12/23, 수)

  • 축약어(snippets)와 prettier라는 포맷 익스텐션 도구를 알게됨

DOM 조작

  • Document Object Model의 줄임말. 자바스크립트가 HTML 엘리먼트에 접근하고 조작(manipulate)할 수 있게끔 해줌.
  • WEB API를 각 브라우저마다 담고있는데 자바스크립트를 이용해서 이 API에 접근할 수 있음
  • 이벤트 리스너(event listener)란 특정 이벤트가 발생할 때까지 기다리다가 이벤트가 발생하면 반응(react)하는 것이다.
    document.querySelector('.check').addEventListener('click',          )
    • 클릭 이벤트에 반응하는 이벤트 리스너
    • addEventListner의 두번째 인자에는 이벤트 핸들러의 함수(Function)이다. 예를 들어 "클릭을 하면 ~를 실행하라"와 같은 것
    • 왜 두번째 인자( )에는 "함수"여야하는지는 당연하다. 예를들어 "버튼을 클릭했을 때 구구단을 실행하라"와 같은 기능을 만들고 싶을때는 이벤트핸들러가 '클릭'되었을 때 '구구단을 실행하는(do)' 것이 자연스럽기 때문이다.

document.querySelector('.check').addEventListener('click',  function () {
		console.log('Hi! You Clicked Button!');
})

9일차~10일차 (12/24~12/25 휴무)

  • 크리스마스 연휴로 휴무 취함

11일차 (12/26, 토)

  • Udemy에서 숫자 추측 게임을 만들어보는 중

const, let 차이점

  • const는 말 그대로 변하지 않는 상수
  • let은 변할 수 있음. 만약 게임 기회를 5번 준다면 let 변수를 주는게 맞음

Modal Window

  • 부트스트랩 이용할 때는 특정 모달 윈도우를 지원하는 class가 있어서 끙끙댔던 기억이 있는데, 여기서 설명하는 모달 윈도우는 굉장히 간단하다.
  • classList에 있는 add함수와 remove함수를 이용해서 클래스 이름만 추가 또는 제거하면 모달 윈도우가 완성된다.
    [HTML] 
    
    <div class="modal hidden">
    	<button class="close-modal">&times;</button>
    
    [JS]
    modal.classList.remove('hidden');
    
    [CSS]
    .hidden {
      display: none;
    }

키보드 입력을 감지하는 법

document.addEventListner('keydown', function (e) {
	if (e.key==='Escape'){
			// Action
	}
}

12일차 (12/27, 일)

  • Udemy Pig 게임 만들기를 끝내고 Javascript가 백단에서 동작하는 방식에 대한 강좌를 들음

  • 모든 브라우저는 저마다의 자바스크립트 엔진을 갖고 있음
    • 구글 크롬은 V8 Engine
    • 애플 사파리는 JavaScriptCore
    • MS 익스플로어는 Chakra
  • 자바스크립트 엔진에는 CALL STACKHEAP 이라는 공간을 가지고 있음
    • CALL STACK : 코드가 실제로 실행되는 공간 (스택 구조)
    • HEAP : Object들이 저장되는 메모리 공간
  • 머신코드로 해석되는 과정에 앞서 컴퓨터구조에 대해 잠깐 설명이 필요
    • Compilation : 전체 코드가 머신코드로 한번에 컨버팅되며 컴퓨터가 실행가능한 바이너리 파일로 쓰인 것
    • Interpretation : line by line으로 코드가 실행됨. 당연히 한줄씩 실행되기 때문에 Compilation 방식보다 느림
    • Just-in-time(JIT) compilation : 기술이 많이 발전했기 때문에 최근 모던 자바스크립트는 JIT 방식을 사용함. 전체 코드를 한번에 컴파일하고, 즉시 실행함

자바스크립트 코드가 실행되는 과정

  1. Parsing : 코드를 읽는 단계, AST(Abstract Syntax Tree) 라고 해서 각 라인의 코드에 의미있는 부분들을 트리(데이터구조) 형태로 구조화 함. 이 단계에서는 코드에서 syntax error가 있는지를 같이 점검함.
  1. Compilation : Paring에서 생성된 AST를 머신 코드로 '컴파일' 함.
  1. Execution : 컴파일 된 코드가 Call Stack 공간에서 실행됨
  1. Optimization : 'Execution' 중에 최적화되지 못한 부분을 다시 re-compile 함. 이때 Execution은 중단되지 않음

위 모든 과정들은 메인쓰레드에서 분리된 엔진 내 특별한 쓰레드에서 수행됨.

Runtime in the Browser

자바스크립트 엔진은 브라우저 내 WEB APIs을 이용하여 window object들에 접근할 수 있게된다. WEB APIs도 런타임의 일부라고 봐야 한다. 여기서 런타임이란 하나의 박스와 같아서 우리가 필요로하는 자바스크립트 관련한 것들을 넣어둔다.

또 런타임에는 Callback queue도 포함된다. 여기서 콜백 큐란 실행 준비가 된(reday to be excuted) 모든 콜백 기능들을 포함하는 데이터 구조이다. 예를 들어 특정 이벤트에 반응하는 이벤트 핸들러가 그러하다. (이벤트 핸들러를 콜백 function이라고 부르기도 한다)

Execution Context란?

  • 자바스크립트가 실행되기 위한 환경이라고 이해하면 됨. 지역 변수라던가, 함수에 전달되는 인자라던가 코드가 실행되기 위해 필요한 정보들이 저장됨
  • 자바스크립트 코드가 얼마나 크건간에 하나의 global exectuion context만 있음.
  • top-level code에서는 담고 있는 내용이 많이 없음. 단순히 머신코드를 받아 CPU가 처리하는 과정임.

Execution Context in Details

  1. Variable Environment
  1. Scope Chain
    • Lexical scoping : 함수를 어디에서 호출하는지에 따라 범위가 결정되는 것이 아니라 함수를 어디에 선언했는지에 따라 결정된다. (↔ dynamic scoping)
      var number = 1;
      function a() {
        var number = 10;
        b();
      }
      function b() {
        console.log(number);
      }
      a(); // ?
      b(); // ?
      결과
      
      1; // a() 결과
      1; // b() 결과
      • 위 예시는 함수의 '선언'에 따라 상위 스코프가 결정된다는 예시

3. this keyword

  • Global Context안에 'x'가 unknown 상태인데 왜냐하면 처음에는 어떠한 함수도 실행되지 않은 상태이기 때문

왼쪽 코드가 짧기 때문에 위 수순에서 정리되지만 수 백개의 함수가 있다고 생각해보자.

13일차 (12/28, 월)

Scope의 3가지 종류

  • Global Scope
  • Function Scope : local scope라고도 불림. 함수 안에서만 접근 가능
  • Block Scope
    • ES6에 적용된 const, let만 block scope에 해당됨
    • 위 예제에서 var는 block scope가 아니라 function scope에 포함됨

Variables Environment : hoisting과 TDZ

Hoisting : "Variables lifted to the top of their scope"

  • hoisting의 목적은 선언(declaration) 전에 변수(variable)와 함수(functions)를 이용하기 위함
  • 어떤 환경이 만들어지느냐? 모든 선언(var, let, const, function)을 가장 위로 끌어올린다. 코드의 실제 위치를 위로 끌어올리는 것이 아니라 자바스크립트 엔진이 변수 환경(variables environment)을 알고 있음을 알려주는 '가상의 개념'이다.
    if (true){
    	var name = "LEE";
    }
    console.log(name);
    • 생각하기로는 "LEE"가 출력이 될 것 같지 않지만 실제로는 출력이 된다!
    • 변수가 if 함수 내에 정의되어있지만, 변수의 선언함수 최상위로 호이스트 되었다.

Hoist 예제

console.log(me); >>> undefined
console.log(job); >>> ReferenceError : Cannot access 'job' before initialization
console.log(year);

var me = 'Jonas';
let job = 'teacher'
const year = 1991;

TDZ : Temporal Dead Zone, let & const

  • ES6에 도입된 TDZ는 한글로 직역하면 사각지대를 의미한다.
  • 변수를 선언하기 전에 사용하는 것은 심각한 에러를 유발하기 쉬운데, 스코프 시작 지점~초기화 시작 지점까지를 TDZ 두어 에러를 좀 더 포착할 수 있고 에러를 만드는 코드를 더 쉽게 피할 수 있도록 만들어준다.
  • TDZ 설명 과정에 앞서 변수의 생성과정 3단계를 인지하고 있어야한다. 변수는 선언(declaration) → 초기화(initialization) → 할당(assignment) 단계를 거친다.
    • var는 변수 선언과 동시에 초기화를 동시에 진행 → Undefined Error
      ## 변수 hoisting
      
      value; // => Undefined
      var value;
      
      ## var 함수 hoisting
      console.log(addArrow); // TypeError : addArrow is not a function
      
      var addArrow = (a,b) => a+b;
      
      ## const 함수 hoisting
      console.log(addArror); // Reference Error : cannot access 'addExpr' before initialization
      
      var addArrow = (a,b) => a+b;
      
    • let, const는 변수 선언과 초기화가 분리되어 진행 → Reference Error
      console.log(`Jonas is a ${job}`); >>> ReferenceError
      const job = 'teacher';

엉뚱한 결과를 낳을 수도 있는 var

if (!numProducts) deleteShoppingCart();

var numProducts = 10;

function delteShoppingCart(){
	console.log('All Products deleted'); // deleteShoppingCart()
}
  • 만약 카트에 10개의 품목을 담았는데 hoisting의 원리를 모르고 위와 같이 작성하면 쇼핑 카트를 모두 삭제하는(!) 일이 발생할 수 있음
  • 위 예제는 var 변수의 특성 때문인데 declaratin(선언)과 initialization(초기화)가 같이 된다는 특성을 가지고 있음. 그래서 undefined 상태로 hoisting이 되어버리면서 if문이 !(undefined) → true로 실행되면서 쇼핑카트 목록이 삭제되는 불상사가 발생함

  • 강좌에서는 var 변수 선언을 하지 말고 const, let을 사용을 권장함
  • 클린 코드 작성을 위해서는 각 스코프 최상단에 변수를 선언하라고 권장함
  • 번외로 var 변수 선언은 global window object에서 property로 생성되나 let과 const는 그렇지 않음
    var abc = 1;
    let def = 2;
    const ghi = 3;
    
    console.log(abc===window.abc); // true
    console.log(def===window.def); // false
    console.log(ghi===window.ghi); // false

This

  • this 키워드는 모든 함수의 exectuion context에서 생성되는 특별한 변수다.
function check() {
  console.log(this === window);
}

check(); // true
  • use strict 에서는 this는 window 객체를 가리키지 않는다.
'use strict';

const check = check => {
  console.log(this === window);
};
check();
  • Arrow Function에서는 use strict 와 상관없이 this는 window 객체이다.

Regular Function과 Arrow Function 내 this 바인딩

'use strict';


// obj 내 regular function일 경우
var obj = {
  names: 'LEE',
  text: '님 안녕하세요',
  print: function () {
    console.log(this === window);  // false
    console.log(this === obj1);    // true
  }, 
};
obj.print();

// obj 내 arrow function일 경우
var obj2 = {
  names: 'LEE',
  text: '님 안녕하세요',
  print: () => {
    console.log(this === window); // true
    console.log(this === obj2); // false
    console.log(this === undefined); // false
    console.log(this.names === undefined); // true
  },
};
obj.print();

console.log(this); // Window

const calcAge = function (birthYear) {
  console.log(2037 - birthYear);
  console.log(this); // undefined
};
calcAge(1991);

const calcAgArrow = birthYear => {
  console.log(2037 - birthYear);
  console.log(this); // Window
};
calcAgArrow(1980);
  • Arrow Function으로 만들었을 땐 this가 Window를 가르킨다.
const jonas = {
  year: 1991,
  calcAge: function () {
    console.log(this);
		console.log(2037 - this.year);
  },
};

jonas.calcAge();

const matilda = {
  year: 2017,
};

matilda.calcAge = jonas.calcAge; /// {year : 1991,calcAge: f}
Ⓐ matilda.calcAge(); // 20

const f = jonas.calcAge; 
Ⓑ console.log(f) // f() {console.log(this); console.log(2037-this.year);
Ⓒ f(); // undefined
  • Ⓐ : matilda 오브젝트는 calcAge() 함수를 정의하지 않았음에도 불구 20이라는 값을 출력한다.
    • 여기서 jonas 오브젝트 안에 있는 this가 그 오브젝트에 귀속되는 개념이 아니라는걸 암시한다. 즉, this는 dynamic하게 오브젝트를 가리킨다.
  • Ⓑ : jonas 의 calcAge 함수를 출력한다.
  • Ⓒ : undefined가 출력된다. f 함수의 주인(owner)이 없기 때문이다

Arrow Functions에서 this는 사용할 수 없다!

예제 1 : Arrow Function에서의 this

const jonas = {
  firstName: 'Jonas',
  year: 1991,
  calcAge: function () {
    console.log(this);
    console.log(2037 - this.year);
  },
Ⓐ greet: () => console.log(`Hey ${this.firstName}`),
};

jonas.greet(); // Hey undefined
  • Ⓐ : greet 메서드의 parent scope는 global scope(=window object). 당연히 window에는 firstName가 없으므로 undefined로 출력된다.
    • 위 상황에서 var 변수 선언을 쓴다면 위험할 수도 있는데, var는 window object에 등록되기 때문이다. 만약 var firstName = "Tomas" 가 선언되어 있었다면 엉뚱한 값을 내놓았을 것
    • 따라서 강좌에서는 arrow보다는 regular function을 이용하라고 권장한다.

예제 2 : Regular function인데 this를 사용할 수 없는 경우

const jonas = {
  firstName: 'Jonas',
  year: 1991,
  calcAge: function () {
    console.log(2037 - this.year);

    Ⓐ const isMillenial = function () {
      console.log(this.year >= 1981 && this.year <= 1996); // 'year' of undefined
    };
    isMillenial();
  },
  greet: () => console.log(`Hey ${this.firstName}`),
};

jonas.greet();
jonas.calcAge();
  • 위 문제를 해결하기 위해서는 두가지 방법이 있음
    // 1. outside에 this를 설정해주기
    
    	 const self = this;
    
     Ⓐ const isMillenial = function () {
          console.log(self.year >= 1981 && self.year <= 1996); 
     };
    
    // 2. Arrow Function을 활용하기 (권장)
    	
     Ⓐ const isMillenial = () => {
          console.log(this);
          console.log(this.year >= 1981 && this.year <= 1996);
        };

Primitive vs Reference Values

  • Reference Value : Object, Array, Function 등..
    • Reference Value는 Heap에 생성됨
    • 객체를 생성하면 Heap에 있는 메모리 주소를 직접 가리키는게 아니라 Call Stack에 Heap 주소를 담는 메모리를 할당함
    • Reference Value는 const로 생성한다고 해서 값이 안 변하는게(immutable) 아님. 따라서 const를 선언할 때는 이 점을 인지하고 있어야 함.

Destructing Assignment (구조분해 할당)

  • 복잡한 데이터 구조를 작은 구조로 나누는 것
const arr = [2,3,4];

// 기존 방법
const a = arr[0];
const b = arr[1];
const c = arr[2];

// Destructing
const [x,y,z] = arr;
console.log(x,y,z); // 2, 3, 4

// Switching variables

const restaurant = {
  name: 'Classico Italiano',
  location: 'Via Angelo Tavanti 23, Firenze, Italy',
  categories: ['Italian', 'Pizzeria', 'Vegetarian', 'Organic'],
  starterMenu: ['Focaccia', 'Bruschetta', 'Garlic Bread', 'Caprese Salad'],
  mainMenu: ['Pizza', 'Pasta', 'Risotto'],

let [main, , secondary] = restaurant.categories;
console.log(main, secondary);

[main, secondary] = [secondary, main];
console.log(main, secondary);

14일차 (12/29, 화)

Spread Operator

'use strict';

const restaurant = {
  name: 'Classico Italiano',
  location: 'Via Angelo Tavanti 23, Firenze, Italy',
  categories: ['Italian', 'Pizzeria', 'Vegetarian', 'Organic'],
  starterMenu: ['Focaccia', 'Bruschetta', 'Garlic Bread', 'Caprese Salad'],
  mainMenu: ['Pizza', 'Pasta', 'Risotto'],

  openingHours: {
    thu: {
      open: 12,
      close: 22,
    },
    fri: {
      open: 11,
      close: 23,
    },
    sat: {
      open: 0, // Open 24 hours
      close: 24,
    },
  },
  order: function (starterIndex, mainIndex) {
    return [this.starterMenu[starterIndex], this.mainMenu[mainIndex]];
  },

  orderDelivery: function ({ starterIndex, mainIndex, time, address }) {
    // 앞 두개는 기존 obj, 뒤 두개는 아래에서 만든 obj
    console.log(
      `Order Received ${this.starterMenu[starterIndex]} and ${this.mainMenu[mainIndex]} will be delivered to ${address} at ${time}`
    );
  },
};

restaurant.orderDelivery({
  time: '23:30',
  address: 'Via del Sole, 21',
  mainIndex: 2,
  starterIndex: 2,
});

// Switching variables
// let [main, , secondary] = restaurant.categories;
// console.log(main, secondary);

// [main, secondary] = [secondary, main];
// console.log(main, secondary);

const { name, openingHours, categories } = restaurant;
console.log(name, openingHours, categories);

const {
  name: restaurantName,
  openingHours: hours,
  categories: tags,
} = restaurant;

console.log(restaurantName, hours, tags);

// Set Default Values (menu는 빈 array값으로 두고, startMenu를 starters라고 부르고 싶을 때)
const { menu = [], starterMenu: starters = [] } = restaurant;
console.log(menu, starters);

// Mutating valuables
let a = 111;
let b = 999;
const obj = { a: 23, b: 7, c: 14 };

//(X) {a, b} = obj; // Syntax Error  중괄호를 쓰게되면 JS는 코드 블록을 예상함
({ a, b } = obj);
console.log(a, b);

// Nested Object (object 안에 object가 있는 경우)
const {
  fri: { open: o, close: c },
} = openingHours; // 금요일 오픈시간 : o, 금요일 마감시간 : c
console.log(open, close); // 축약하지 않았을 경우
console.log(o, c); // o, c로 축약했을 경우

Rest Pattern

  • spread operator과 반대라고 생각하면 됨. 새로운 array를 만들어거나 함수에 여러 값을 전달할 때 사용함
'use strict';

const restaurant = {
  name: 'Classico Italiano',
  location: 'Via Angelo Tavanti 23, Firenze, Italy',
  categories: ['Italian', 'Pizzeria', 'Vegetarian', 'Organic'],
  starterMenu: ['Focaccia', 'Bruschetta', 'Garlic Bread', 'Caprese Salad'],
  mainMenu: ['Pizza', 'Pasta', 'Risotto'],

  openingHours: {
    thu: {
      open: 12,
      close: 22,
    },
    fri: {
      open: 11,
      close: 23,
    },
    sat: {
      open: 0, // Open 24 hours
      close: 24,
    },
  },
  order: function (starterIndex, mainIndex) {
    return [this.starterMenu[starterIndex], this.mainMenu[mainIndex]];
  },

  orderPasta: function (ing1, ing2, ing3) {
    console.log(`Here is your delicous past with ${ing1} ${ing2} and ${ing3}`);
  },

  orderPizza: function (mainIngredient, ...otherIngredients) {
    console.log(`메인재료 : `, mainIngredient);
    console.log(`서브재료 : `, otherIngredients);
  },
};

// 1. Destructing

//// Spread, because on RIGHT side of
const arr = [1, 2, ...[3, 4]];

// REST, beacuse on LEFT side of =
const [a, b, ...others] = [1, 2, 3, 4, 5];
console.log(a, b, others);
const [pizza, , risotto, ...otherFood] = [
  ...restaurant.mainMenu,
  ...restaurant.starterMenu,
];

console.log(pizza, risotto, otherFood);

//// Objects
const { sat, ...weekdays } = restaurant.openingHours; // 평일 오픈시간만 출력하는 테크닉
console.log(weekdays);

// 2. Functions
const add = function (...numbers) {
  let sum = 0;
  for (let i = 0; i < numbers.length; i++) {
    sum += numbers[i];
  }
  console.log(sum);
};

// 여러 개의 인자를 받는 함수만들기
add(2, 3);
add(5, 3, 7, 2);
add(5, 3, 7, 2, 3, 2, 1, 4); // argument가 늘어나도 add 함수는 쓰고싶어!

const x = [5, 13, 27]; // 배열을 spread를 이용해 합하는 방법
add(...x);

restaurant.orderPizza('mushrooms', 'onion', 'olives', 'spinach'); // 메인재료는 mushroom, 서브재료는 그 이하
restaurant.orderPizza('mushrooms'); // 메인재료는 mushroom, 서브재료는 없음

Short Circuiting (&& and ||)

// Use ANY data type, return ANY data type : short-circuiting
console.log(3 || 'Jonas'); // 출력 : 3, 첫번째 value가 truly value면 첫번째 값을 반환함
console.log('' || 'Jonas'); // 출력 : Jonas;
console.log(true || 0); // 출력 : true
console.log(undefined || null); // 출력 : null (undefined = falsy value)
console.log(undefined || 0 || '' || 'Hello' || 23 || null); // 출력 : Hello

// Practice

restaurant.numGuests = 0;
const guests1 = restaurant.numGuests ? restaurant.numGuests : 10;
console.log('guests1 : ', guests1);

const guests2 = restaurant.numGuests || 10;
console.log('guests2 : ', guests2);

console.log('--- AND ---');
console.log(0 && 'Jonas'); // 출력 : 0
console.log(7 && 'Jonas'); // 출력 : Jonas
console.log('Hello' && 23 && null && 'jonas'); // 출력 : null

if (restaurant.orderPizza) {
  restaurant.orderPizza('mushrooms', 'spinach');
}
restaurant.orderPizza && restaurant.orderPizza('mushrooms', 'spinach'); // 위 if문으로 간단하게 서술할 수 있음

The Nullish Coalescing Operator (??)

  • 왼쪽 피연산자가 null 또는 undefined일 때 오른쪽 피연산자를 반환하고, 그렇지 않으면 왼쪽 피연산자를 반환하는 논리 연산자이다

'프로그래밍 > 개발일지' 카테고리의 다른 글

1주차  (2) 2020.12.21