08.05 TIL - Scope & Closure

이형우·2020년 8월 5일

Scope & Closure

학습 목표
Scope(범위)

JavaScript의 Scope의 의미와 적용 범위를 이해할 수 있다
JavaScript의 Scope 주요 규칙을 이해할 수 있다
중첩 규칙
block level vs. function level
let, const, var의 차이
전역 변수와 전역 객체의 의미

Closure
Closure의 의미와 Closure가 가지는 Scope Chain을 이해할 수 있다
Closure의 유용하게 쓰이는 몇 가지 코딩 패턴을 이해할 수 있다

매개변수
Parameter의 갯수가 유동적인 함수를 만들 수 있다
ES6에서 사용하는 Rest parameter 및 ES5의 방법인 arguments 키워드를 이용할 수 있다
Default parameter를 사용할 수 있다

객체 지향 JavaScript
객체 지향 프로그래밍의 기본적인 컨셉을 이해할 수 있다
class, instance 등의 용어를 이해할 수 있다
new키워드를 사용해 instance를 생성할 수 있다
ES6 class 키워드를 사용할 수 있다
prototype을 이용해 클래스의 원형을 만드는 방법을 이해할 수 있다

학습 내용
Scope

  • 변수 firstName에 접근할 수 있는 범위가 존재한다.
  • Local scope 안쪽에서 선언된 변수는 밖에서 사용할 수 없다. firstName 의 경우 Local scope 안에서만 접근할 수 있으며, 아래 예시의 경우 Locap scope 밖에서 불렀기 때문에 바깥입장에서는 firstName 이 정의된 적이 없기 때문에 ReferenceError, 즉 참조할 것이 없어서 에러가 났어요! 라는 메시지를 받게 된다.
  • Scope : 변수 접근 규칙에 따른 유효 범위
    -> 변수는 어떠한 환경 내에서만 사용 가능하며, 프로그래밍 언어는 각각의 변수 접근 규칙을 갖고 있다.
    -> 변수와 그 값이, 어디서부터 어디까지 유효한지를 판단하는 범위
    -> 자바스크립트는 기본적으로, 함수가 선언되는(lexical) 동시에 자신만의 scope를 가진다.
    -> 함수 자체가 일종의 범위 역할을 함.

Scope가 가진 규칙들
Rule.1 : Local scope VS Global scope

  • 안쪽 scope 에서 바깥 변수/함수를 접근하는 것은 가능하나,
    바깥쪽 scope 에서 안쪽 변수/함수에 접근하는 것을 불가능하다.
  • Scope 는 중첩이 가능하다
    -> 함수 안에 함수를 넣을 수 있다.
  • Global scope 는 최상단의 Scope로, 여기서 선언된 전역 변수는 어디서든 접근이 가능하다.
  • 지역 변수는 함수 내에서 전역 변수보다 더 놓은 우선순위를 가진다.


Rule.2 : Function Scope VS Block Scope

  • Block : 중괄호로 시작하고, 끝나는 단위

  • var 키워드 VS let 키워드
  • 변수를 정의하는 또다른 키워드 var
    -> Javascript는 기본적으로, 함수 단위로 자신만의 scope 를 가진다 -> var 키워드도 그렇다 (old way)
    -> 그러나, Block 단위로 Scope 를 구분했을 때에 예측하기 쉬운 코드를 작성할 수 있다 -> let 키워드


  • const 키워드
    -> 값이 변하지 않는 변수, 즉 상수를 정의할 때 사용하는 키워드
    -> let 키워드와 동일하게 Block Scope 를 따른다
    -> 값을 재정의하려고 하면 Type Error 를 낸다

정리


Rule.3 : 전역 변수와 window 객체

  • 전역 범위를 대표하는 객체 : window
  • Global Scope에서 선언된 함수, 그리고 var 키워드를 이용해 선언된 변수는 window 객체와 연결 (아래 예시 참조)
  • 전역 범위에 너무 많은 변수를 선언하지 않도록 주의하기!

    Rule.4 : 선언 없이 초기화된 전역 변수
  • 절대로! 선언 키워드 (var, let, const)없이 변수를 초기화 하지 말것. 선언 키워드가 없다면 전역 변수로 취급된다.
  • Strict mode 를 사용하면 이런 실수를 방지할 수 있다.

Closure
외부 함수의 변수에 접근할 수 있는 내부 함수
예제.1)

예제.2) 함수도 리턴할 수 있다.
다음 outerFn(); 실행 시 찍히는 값은?

function outerFn() {
  let outerVar = 'outer';
  console.log(outerVar); //일단 console.log 가 실행되어 outer 가 먼저 찍힘
  
  funtion innerFn() {
    let innerVar = 'inner';
    console.log(innerVar);
  }
  return innerFn; // 이걸 리턴하라고 했으니 리턴되기는 하되, 먼가 실행되지 않은 함수 자체가 리턴된다. 윗 행의 innerFn 함수가 실행된 것으로 착각하지 말것. 함수는 누가 불러줘야 실행 되는데, 아직 불러준 명령이 없으니 그냥 함수만 리턴 되는 것.(함수도 리턴 될 수 있다는 예시임)
}
outerFn(); //?

예제.3) 다음의 경우 각각 콘솔에 어떻게 찍힐까?

  • outerFn()();
  • let innerFn = outerFn();
  • innerFn();
function outerFn() {
    let outerVar = 'outer' ;
    console.log(outerVar);
    
    function innerFn() {
        let innerVar = 'inner';
        console.log(innerVar);
    }
    return innerFn;
}

outerFn()();
outer : 첫 번째 outerFn()로 인해 outer 가 찍힘
inner : 두 번째 ()가 return innerFn 을 만나 innerFn() 효과가 발휘 되면서, 즉 innerFn 함수를 돌려봐라!는 명령어가 되면서 innerFn함수가 작동 되었고 이로인해 inner가 찍힌다.

let innerFn = outerFn();
outer : innerFn이라는 변수에 outerFn()이 할당되었을 뿐, 결과값은 outerFn()의 결과가 찍히게 된다. 단, innerFn을 변수 선언했기 때문에 innerFn을 콘솔창에 찍어보면, 아래 innerFn의 함수가 들어가 있음을 확인할 수 있다.

Function innerFn() {
        let innerVar = 'inner';
        console.log(innerVar);
    }

innerFn() : inner

Closure 란...?
외부 함수의 변수에 접근할 수 있는 내부 함수
(또는, 이러한 작동 원리를 일컫는 용어)

유용한 closure 함수 예제

  • 커링 : 함수 하나가 n개의 인자를 받는 대신, n개의 함수를 만들어 각각 인자를 받게 하는 방법
function adder(x) {
  return function(y) {
    return x + y;
  }
}
adder(2)(3); // 5 -> 2번 연속 실행함으로써 사용할 수 있는 함수
아래같은 경우에 유용하다
let add100 = adder(100); //x의 값을 고정해 놓고 재사용할 수 있다
add100(2); // 102
add100(10); // 110

let add5 = adder(5);
add5(2); //7
  • 외부 함수의 변수가 저장되어 마치 템플릿 함수와 같이 사용 가능
function htmlMaker(tag) {
  let startTag = '<' + tag + '>';
  let endTag = '</' + tag + '>';
  return function(content) {
    return startTag + content + endTag;
  }
}

let divMaker = htmlMaker('div');
divMaker('code'); //<div>code</div>
divMaker('states'); // <div>states</div>

let h1Maker = htmlMaker('h1');
h1Maker('Headline'); //<h1>Headline</h1>
  • 클로저 모듈 패턴 : 변수를 스코프 안쪽에 가두어 함수 밖으로 노출 시키지 않는 방법. 그렇게 해서 큰 함수 틀은 그대로 고정하고, 원하는 결과값을 내게 하기 위해 조심씩 수정해서 사용하면 된다.
function makeCounter() {
  let privatCounter = 0;
  
  return {
    increment: function() {
      privateCounter++;
    },
    decrement: function() {
      privateCounter--;
    },
    getValue: function() {
      return privateCounter;
    }
  }
}
let counter1 = makeCounter();
counter1.increment();
counter1.increment();
counter1.getValue(); // 2

let counter2 = makeCounter();
counter2.increment();
counter2.decrement();
counter2.increment();
counter2.getValue(); // 1
profile
완전 완전 초짜

0개의 댓글