코린이의 기록

[ES6] 스코프 본문

javascript,HTML,CSS

[ES6] 스코프

코린이예요 2019. 6. 20. 19:22
반응형

전역 스코프

전역변수를 사용하는 방식

let name = "Irena";
let age = 25;

function greet() {
    console.log(`Hello, ${name}!`);
}

function getBirthYear() {
    return new Date().getFullYear() - age;
}

greet(); 
console.log(getBirthYear());

Result

Hello, Irena!

1994

 => name 값을 외부에서 의도적으로 바꿀 수 있다는 문제가 있음. 

let user = {
    name : "Irena",
    age : 25
};

function greet() {
    console.log(`Hello, ${user.name}!`);
}
function getBirthYear() {
    return new Date().getFullYear() - user.age;
}

greet(); 
console.log(getBirthYear());

단일 객체로 정의하는것이 낫다.

name과 age를 없애고 user를 써서 전역 식별자를 하나 줄였지만 greet(), getBirthYear()은 여전히 전역 user에 의존한다. 이 객체 역시 어디서든 수정할 수 있다. 

 

대안으로 전역 스코프에 의존하지 않게 만드는 방법인데... 위와 차이점을 잘 모르겠다.

function greet(name) {
    console.log(`Hello, ${user.name}!`);
}
function getBirthYear(name) {
    return new Date().getFullYear() - user.age;
}

greet(); 
console.log(getBirthYear());

블록 스코프

중괄호로 묶은 것. 블록의 스코프에서만 보이는 식별자를 의미한다. 

코드 블록({…})내에서 유효한 스코프 

단, ES6에서 도입한 let과 const를 사용해야 가능한 스코프이다. Javascript는 기본적으로 비블록레벨스코프를 가진다. 그게 무슨말이냐면, C언어 같은 경우는 블록안에 있는 지역 변수는 외부에서 사용할 수 없었는데 Javascript는 그렇지 않다. 

c언어

int main(void) {
  // block-level scope
  if (1) {
    int x = 5;
    printf("x = %d\n", x);
  }
  printf("x = %d\n", x); // use of undeclared identifier 'x'
  return 0;
}

7번째 줄에서 x는 선언되지 않은 식별자 에러가 발생한다.

javascript

console.log('before block');
{
    console.log('inside block');
    var x = 3;
    console.log(x);
}
console.log(`outside block; x=${x}`);

javascript에서는 정상적으로 outside block; x=3 이 출력된다.이를 ES6에서 새로 도입한 let, const를 사용해서 블록레벨스코프로 만들어보자. 

console.log('before block');
{
    console.log('inside block');
    const x = 3;
    console.log(x);
}
//console.log(`outside block; x=${x}`);

Result

before block

3

7번째 줄에서 x를 찍으려고 해보지만 x는 블록안에서 정의된 값이므로 ReferenceError가 발생한다. 왜냐하면 x는 블록 내에서만 접근 가능한 객체이기 때문이다.

 

 

클로저 

함수가 특정 스코프에 접근할 수 있도록 의도적으로 함수를 그 스코프에서 정의하는 것. 쉽게말해서 함수내부에서 함수를 선언하면 클로저를 생성한것이다. 

아래 예시를 보면서 이해해보자

클로저 Example 1

let globalFunc;
{
    let blockVar = 'a';
    globalFunc = function() {
        console.log(blockVar);
    }
}
globalFunc();

Result 

a

 

globalFun이 블록 안에서 정의되었다. 이 함수 밖에 있는 blockVar를 사용하고 있는데, 내부 함수에서 자신을 포함하고 있는 외부함수(or 블록)에서 정의한 변수에 접근할 수 있다. 가장 상위에 있는 스코프의 전역변수는 globalFunc이지만, globalFunc 함수의 전역변수는 그 상위에 있는 blockVar이 전역변수가 된다. 

클로저 Example 2

function outerFunc() {
    var x = 10;
    var innerFunc = function () { console.log(x); };
    innerFunc();
  }
  
  outerFunc();

Result

10

innerFunc에서 x 변수 접근이 가능하다. 

 

결론은, 스코프 안에서 함수를 정의하면 해당 스코프는 메모리에서 조금 더 오래 유지된다. 

클로저를 Javascript에서 사용하는 예제 

 

1씩 증가하는 카운터 만들기

Case 1. 전역 변수를 썼을때.

  var counter = 0;

    function increase() {
      return ++counter;
    }

increase();

이 경우, counter 전역변수를 외부에서 변경할 수 있기 때문에 상당히 위험한 코드이다. 

 

Case 2. 지역 변수를 썼을 때

 function increase() {
      var counter = 0;
      return ++counter;
}
increase();

이 방식은 increase 함수가 호출될 때마다 변수를 0으로 초기화시키기 때문에 counter라고 할 수 없다. 계속 값이 1이기 때문.

 

Case 3. 클로저를 이용할때

var increase = (function () {
      var counter = 0;
      // 클로저를 반환
      return function () {
        return ++counter;
      };
}());

increase 함수에서 내부함수를 만들어서 counter를 증가시키고 있다. 이는 counter변수를 외부에서 변경할 수 없으므로 안전한 코드라고 할 수 있다.

 

 

클로저의 대한 이해 : https://poiemaweb.com/js-closure

 

 

Reference : Learning JavaScrpit 이선 브라운 지음

반응형

'javascript,HTML,CSS' 카테고리의 다른 글

[javascript] Set onclick on the <tbody tr> except for specific <td>  (0) 2019.07.05
[ES6] 객체와 객체지향 프로그래밍  (0) 2019.06.30
[ES6] 표현식 & 연산자  (0) 2019.06.17
[HTML] 한자 넣기  (0) 2019.06.14
[ES6] 객체  (0) 2019.06.14
Comments