[TIL]200805 Scope, Closure

Chaegyeom·2020년 8월 29일
0

TIL

목록 보기
9/77
post-thumbnail

Scope

Scope란 번역해보자면 '범위'라는 뜻을 가지고 있다. 변수에 접근할 수 있는지에 대한 규칙이자 코드의 유효범위라고 볼 수 있다. 즉, 스코프는 변수 접근 규칙에 따른 유효 범위이다.

  • 변수는 어떠한 환경 내에서만 사용 가능하며, 프로그래밍 언어는 각각의 변수 접근 규칙을 가지고 있다.
  • 스코프란 변수와 그 값이, 어디서부터 어디까지 유효한지를 판단하는 범위
  • JavaScript는 기본적으로, 함수가 선언되는(lexical)동시에 자신만의 Scope를 가진다

Global Scope & Local Scope

스코프에는 2가지 종류가 있는데, 전역 스코프(global scope)와 지역 스코프(local scope)이다.

전역 스코프(global scope)

let callBy = 'Hey'

로컬스코프와 글로벌 스코프의 차이

  • 안쪽 스코프에서 바깥 변수/함수를 접근하는 것은 가능
  • 바깥쪽 스코프에서 안쪽 변수/함수를 접근하는 것은 불가능

    지역 스코프(local scope)

    function callByName() {
     let name = 'Hae';
     return callBy + ' ' + name;
    }
- 스코프는 중첩이 가능하다
  함수 안에 함수를 넣을 수 있다
- 글로벌 스코프는 최상단의 스코프로 전역변수는 어디서든 접근이 가능하다
- 지역변수는 함수 내에서 전역변수보다 더 높은 우선순위를 가진다
let callBy = 'Hey' // 전역변수
function callByName() {
  let name = 'Hae'; // 지역변수
  return callBy + ' ' + name;
}

callByName(); // => 'Hey Hae'
name; // => ReferenceError > name은 지역함수내에 있는 지역변수이므로 전역스코프에서 접근할 수 없다.

Function Scope & Block Scope

  • Block : 중괄호({})로 시작하고 끝나는 단위

  • Javascript는 기본적으로, 함수단위로 자신만의 Scope를 가진다.(var키워드가 그렇다)

  • 그러나, Block단위로 Scope를 구분하게 되면 예측하기 쉬운코드를 작성할 수 있다.(그래서 요즘은 let키워드를 통한 선언을 많이 한다.)

for(var i = 0; i<5; i++) {
  console.log(i); // => i가 5번 찍힌다.
}
console.log('final i:', i); // 5
//for문 안의 var키워드를 통해 선언된 i는 block범위를 벗어나도 같은 function scope	에서는 사용이 가능한다.
for(let i = 0; i<5; i++) {
  console.log(i); // => i가 5번 찍힌다.
}
console.log('final i:', i) // =>ReferenceError
// For 문 안의 let키워드를 통해 선언된 i는 블럭을 벗어나는 순간 사용할 수 없기 때문에  블럭(중괄호)밖의 i는 변수를 찾을수 없게 된다
  • const 키워드(constant)
    - 값이 변하지 않는 변수, 즉 상수를 정의할 때 사용하는 키워드
    - Let 키워드와 동일하게 block scope를 따릅니다
    - 값을 재정의하려고 하면 TypeError를 냅니다

변수 정리

letconstvar
유효범위Block ScopeBlock ScopeFunction Scope
값 재정의가능불가능가능
재선언불가능불가능가능

전역변수와 Window객체

  • Window 객체는 전역범위를 대표한다
  • 함수와 var를 통한 선언은 window객체에 담기지만 let으로 선언한 경우에는 window객체에 담기지 않는다
  • 글로벌 스코프(Global Scope)에서 선언된 함수, 그리고 var키워드를 이용해 선언된 변수는 window객체와 연결된다.

Closure(클로저)

MDN에서는 Closure를 다음과 같이 정의하고 있다.

A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function’s scope from an inner function.

무슨 얘기인지 잘 모르겠으니 한국어로 번역된 MDN을 보자

클로저는 함수와 함수가 선언된 어휘적 환경의 조합이다.

무슨소리인지 더 모르겠다. 역시 이래서 원서를 읽으라고 하는 건지도 모르겠다.

영어 MDN을 해석해보고 다른 곳에서 더 찾아본 결과

외부함수의 변수에 접근할 수 있는 내부함수(또는, 이런 작동원리)

라고 한다.


클로저함수는 지역변수, 외부함수의 변수, 전역변수 모두에 접근이 가능하다

클로저를 이용한 유용한 예제

  1. 커링 함수(Currying Function)
  • 함수 하나가 n개의 인자를 받는 대신, n개의 함수를 만들어 각각 인자를 받게 하는 방법
function adder(x) {
  return function(y) {
    return x + y;
  }
}

adder(2)(3); // 5

let add100 = adder(100);
add100(2); // 102 => x의 값을 고정해놓고 재사용 할 수 있다.
add100(10); // 110

let add5 = adder(5);
add5(2); //7
  1. 외부 함수의 변수가 저장되어 템플릿 함수처럼 사용할 수 있다.
function htmlMaker(tag) {
  let strtTag = '<' + tag + '>';
  let endTag = '</' + tag + '>';
  return function(content) {
    return startTag + content + endTag
  }
}

let divMaker = htmlMaker('div');
divMaker('Hae') // <div>Hae</div>
divMaker('Bae') // <div>Bae</div>

let h1Maker = htmlMaker('h1');
h1Maker('headline'); // <h1>headline</h1>
  1. 클로저 모듈 패턴
  • 변수를 스코프 안쪽에 가두어 함수 밖으로 노출시키지 않는 방법
function makeCounter() {
  let privateCounter = 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

// 두 카운터에 각기 다른 privateCounter를 다루면서, privateCounter를 밖으로 노출시키지 않는다.

사실 클로저 모듈패턴은 아직 정확하게 이해하지 못했다. 앞으로 점점 자바스크립트를 배워가면서 이해하게 될 것이라 생각된다.

profile
주니어 개발자가 되고싶은

0개의 댓글