자바스크립트 기초 - 컨텍스트, 클로저

Minhyeok Kim·2022년 7월 24일
0
post-thumbnail

[Having a review time in order to make it last longer...]

Idea A,
자바스크립트가 내 인생 처음으로 제대로 배우는 '컴퓨터언어'인데, 배우면서 드는 생각은 사람이 만든 컴퓨터가 나한테 "나랑 대화를 하고 싶으면 지능이 높아야해"라고 말하는 것 같다. 사람과 사람이 대화를 할때에는 공유되는 배경지식이니 감정이 있어서 그런지 알잘딱깔센이 되는데, 이건 뭐... 치킨을 먹는데 이게 기름맛이고 이걸 이온도로 튀겼고 시간이 이만큼 지났으니 이 양념을 묻히면 이런맛이나니 이런반응을 보여라 하는데 이게... 대화인가 명령인가 싶다. 그래도 알잘딱깔센하게 명령을 해야 알잘딱깔센한 실행과 답을 주니 내가 나를 공부하고 가다듬을 수밖에ㅋㅋ 에러나면 내 책임...

Idea B,
자바스크립트를 배우면서 많은 예제들을 다루고 있다. 그런 와중에 드는 생각인데, 물론 수식이나 명령들의 작동법? 작동알고리즘? 을 배우기 위해서 고정된 값과 예상값을 가지고 연습하는건 알겠는데 우리가 정작 실제로 만들어야 하는건 정해진게 아닌 유동적으로 움직일수 있는, 여러 상황에서 대응할 수 있는 알고리즘이라서 응용하는데 시간이 걸리는것 같다. 이를 보완한 교육은 없나?? 그냥 많은 예제들을 풀어보면서 응용법을 하나하나 터득해야하는가?? 생각하는 힘을 빠르게 기를 수 있는 지름길이 있었으면 좋곘다.

[10장 실행 컨텍스트]

실행 컨텍스트
자바스크립트의 동작원리를 담고 있는 핵심 개념으로 스코프, 호이스팅, 클로저, 비동기 처리동작방식을 이해해야함

소스코드 타입
전역코드, 함수코드, eval코드, 모듈코드등이 있다.
소스코드들은 코드평가를 거쳐서 실행컨텍스트로 넘어간다.

// 사람이 선언문으로 작성한 내용
function printName(firstname) {
  // 함수 선언문
  var result = inner(); // "선언 및 할당"
  console.log(typeof inner); // "function"
  console.log("name is " + result); // "name is inner value"
}
function inner() {
  // 함수선언문
  return "inner value";
}

// 컴퓨터가 이해하는 순서 (선언문)
printName(); // 함수 호출

function printName(firstname) {
  var reuslt;
  function inner() {
    return "inner value";
  }
  result = inner();
  console.log(typeof inner);
  console.log("name is " + reuslt);
}

printName();

// 함수 표현식으로 사람이 입력한 순서
function printName(firstname) {
  var inner = function () {
    return "inner value";
  };
  var reuslt = inner();
  console.log("name is " + reuslt);
}

printName();

// 컴퓨터가 인식한 순서 (표현식)
function pritName(firstname) {
  var inner;
  var result;

  inner = function () {
    return "inner value";
  };

  result = inner();
  console.log("name is " + result);
}

printName();

실행 컨텍스트
평가와 실행 사이에서 스코프, 식별자, 코드 실행 순서등을 관리하는 역할을 한다.
Execution Context,
(강의 자료 참고)
실행컨텍스트에서 선언이되고 할당이 되는 찰나에 Temporary Dead Zone 이라는게 있고 이 때문에 선언된 변수들이 호이스팅되면서 초기화가 이루어지는 찰나가 생기고 이때 값은 undefined를 반환한다.
실행 컨텍스트의 스택은 Last in First out 을 따라간다.

// 예제 23-3
const z = 1;
function boo() {
  const c = 2;
  function bar() {
    const v = 3;
    console.log(z + c + v);
  }
  bar(); // 함수 bar 을 실행시키는 메세지이지만 함수 boo가 실행되지 않으면 가만히
}
boo(); // 6, 함수 boo 를 실행시키는 메세지
// 함수 boo 에서 함수 bar을 실행시키고
// 함수 bar 에서 console.log를 통해 값을 반환한다.
// 이때 필요한 인수 z c v 는 체인스코핑을 통해 값을 가져온다.

// 실행컨텍스트 스택 부분 review
const x = 1; //  스택쌓기 1번, 변수 x 의 선언

function foo() {
  // 스택쌓기 2번, 함수 foo 의 선언
  // 스택쌓기 3번, 함수 f00 의 실행 이후 변수 y 선언,
  const y = 2;
  console.log(y);

  function boo() {
    // 스택쌓기 4번,함수 boo 의 선언, y 의 출력
    const z = 3;
    console.log(x + y + z);
  }
  boo();
  // 스택쌓기 5번, boo 의 실행, 변수 z 의 선언, xyz 실행
}

foo();
// 스택실행 1번, x 할당
// 스택실행 2번, foo 할당
// 스택실행 3번, foo 실행
// foo 실행 후 내부, y 할당 -> boo 할당 -> y 출력 -> boo 실행
// boo 실행 후 내부, z 할당 -> xyz 출력
// why ? 스택소모 순서대로라면, boo 가 먼저 실행되어야???
// = 6이 먼저나와야 하는데 왜 2가 먼저 ??

렉시컬환경에서의 실행 컨텍스트
렉시컬환경에서는 함수가 정의된 위치에서 binding이 일어난다. 그렇기 때문에 실행컨텍스트도 그때 함수 실행 컨텍스트를 실행시켜서 값들을 참조한다. 함수안에서 블록 레벨 스코프가 생성될 때에는 함수 실행 컨텍스트가 실행되고 있는 와중에 블록 레벨의 실행 컨텍스트가 새롭게 열린다. 블록 레벨 안에 있는 실행 컨텍스트들은 블록 레벨 안에 있는 값들을 바인딩 하며(체인스코핑 포함) 끝나면 다시 실행되고 있던 함수 실행 컨텍스트로 돌아간다.

// 예제 23-4
var q = 1;
const w = 2;

function roo(e) {
  var q = 3;
  const w = 4;

  function rar(t) {
    const k = 5;
    console.log(e + t + q + w + k);
  }
  rar(10); // 함수 rar를 실행시키고 매개변수 t 에 인수 10을 넣는다.
  // 하지만 함수 roo가 호출되기 전까지는 호출되지 않는다.
}
roo(20); // 42, 함수 roo 를 실행시키고 매개변수 e 에 인수 20을 넣는다.
// 함수 roo 는 함수 rar 를 실행시키면서 매개변수 t 에 인수 10을 넣는다.
// 결과적으로 console.log 를 여기서 출력하게 된다.

[11장 클로저]

자바스크립트 엔진은 함수를 어디서 호출했는지가 아니라 함수를 어디에 정의했는지에 따라 상위 스코프를 결정한다. 이를 렉시컬 스코프라고 한다.
Lexical : of or relating to words or the vocabulary of a language as distinguished from its grammar and construction.
=> 대충 문법과 구조에 연관이 있는~

그래서 클로저(clouse)란, 함숭와 그 함수가 선언된 렉시컬 환경과의 조합이다.
실무에선 "외부에서 접근시키지 못하게 은닉화or폐쇠(closur?)시켜" = "스코프 조절해서 변경안되게 해"

oer : 정의된 환경의 스코프체인을 일컫는말로 렉시컬환경에서 함수들은 자신의 oer을 항상 기억한다.

클로저를 어떻게 활용할까?
let num = 0; // 전역변수이기 때문에 누구나 건들 수 있고 변경이 쉽게 가능하다.
let num = 100; // like this

const increase = function () {
  return ++num;
};

console.log(increase());
console.log(increase());
console.log(increase());

그렇다면 클로저로 한번 숨겨보자!
const increase = (function () {
  let num = 0;
  return function () {
    return ++num;
  };
})();

console.log(increase());
console.log(increase());
console.log(increase());

************************
let num = 0; // 전역변수를 사용하기 떄문에 변질될 가능상이 높다.
// like, 누가 갑자기 let num = 100; 이렇게 쓰면 내가 쓰는 데이터가 바뀐다.

const increase = function () {
  return ++num;
};

console.log(increase()); // 1 -> 101
console.log(increase()); // 2 -> 102
console.log(increase()); // 3 -> 103

// 그래서 클로저(은닉)를 하게 되는데 이는 스코프를 통제해서 데이터를
// 안전하게 보호하는게 목적이다.
const outerFunc = function () {
  // 새로운 함수를 작성해서 방어막을 세운다.
  // 이는 내가 사용하는 데이터를 전역스코프에서 지역스코프로 옮기는 역할이다.
  let num = 0; // 중요하게 생각하는 변수
  const increase = function () {
    // 내가 사용하던 함수 그대로 가져간다.
    return ++num;
  };
  return increase;
  // 방어막 함수에서 리턴값을 내가 사용하던 함수로 한다.
  // 여기서 주의할점!, increase()로 리턴하게 되면
  // 함수의 결과값을 반환하게 되므로 내가 원하는 함수작업을 진행시킬 수 없다.
};
// 방어막 함수를 새로 만들었기 때문에,
// 방어막 함수를 호출하고 실행시켜줄 변수를 만든다.
const def = outerFunc();
console.log(def());
// def 의 결과값을 출력한다.
// def 의 결과값이란, 함수 outerFunc 의 return 값이 되고,
// 첫 결과값은 0 ++ 1 == 1이 된다. 이때 num은 1이 더해진 값으로 재할당 된다.
console.log(def());
// 두번째 def 의 결과값을 출력한다.
// 함수 outerFunc의 return 값이 되고,
// 아까 더해진num 값에 1이 추가로 더해진 값이 출력된다.
console.log(def());
// 같은 방식으로 3이 출력된다.

0개의 댓글