TIR: 완벽가이드 1. 코어자바스크립트(2) 변수 - 220615

Lumpen·2022년 6월 15일
0

TIR

목록 보기
6/40

프로퍼티로서의 변수

전역 변수를 선언한다는 것은 전역 객체의 프로퍼티를 정의하는 것이다
var 사용 시 생성된 프로퍼티는 수정이 불가능하고, delete 연산자로 소멸시킬 수 없다는 뜻

var를 사용하지 않고 할당한 변수는 전역 객체의 평범하고 수정 가능한 프로퍼티로 들어가고 삭제도 가능하다

전역 변수가 전역 객체의 프로퍼티라는것은 DCMAScript 명세에 규정되어 있다
지역 변수에는 그런 규정은 없지만 변수를 각 함수 호출과 연관된
객체의 프로퍼티로 생각해도 된다
ES3 명세에는 이러한 객체를 '호출객체'로 부르고
ES5 명세에는 '선언적 환경 기록'으로 부른다
자바스크립트는 this 키워드로 전역 객체를 참조할 수 있지만 지역 변수가 저장된 객체를 참조할 방법은 제공하지 않는다

렉시컬 스코프 (유효 범위 체인)

자바스크립트는 언어적으로 유효범위를 가지고 있는 언어다
유효범위란 정의된 변수를 사용 가능한 소스 코드의 집합으로 생각할 수 있다
전역 변수는 전역적으로 유효
지역 변수는 변수가 선언된 함수 전체, 중첩된 함수 내에서도 유효
자바스크립트의 모든 코드 더미는 유효범위 체인을 가지고 있다
범위 안에 있는 변수를 정의하는 객체의 체인, 리스트다

변수 해석

변수 x의 값을 얻어야 할때 처음 체인에서 x를 찾고,
해당 객체에 x인 프로퍼티가 있으면 그대로 사용
첫번째 객체가 x를 가지고 있지 않다면 다음 객체에서, 두번째 객체도 가지고 있지 않다면 다음 객체에서
x가 유효범위 안에 있지 않다면 ReferenceError가 발생

최상위 자바스크립트 코드의 경우 유효범위 체인은 '전역 객체'만으로 이루어진다
중첩되지 않은 함수의 유효 범위 체인은 두 개의 객체로 이루어진다
하나는 함수 매개변수와 지역 변수를 정의하는 객체,
다른 하나는 전역 객체
중첩된 함수에서 유효범위 체인은 세 개 이상의 객체를 갖는다
객체의 유효범위 체인이 어떻게 생성되는지 반드시 이해해야 함
함수가 정의 될 때, 함수는 유효범위 체인을 저장한다
-> 함수 선언시 유효범위가 결정됨 (정적 스코프 - 렉시컬 스코프)
함수가 호출될 때, 해당 함수의 지역 변수를 저장하기 위해
새로운 객체를 하나 생성 후, 해당 객체를 기존에 저장된 유효 범위 체인에 추가한다
중첩 함수의 경우 외부에서 함수를 호출할 때마다 중첩된 함수가 매번 선언된다 때문에 함수를 호출할 때마다 유효범위 체인이 조금씩 달라진다
함수는 항상 동일하지만 유효범위 체인은 호출 시 마다 달라지게 된다

유효범위 체인의 개념은 with문과 클로저를 이해하는데도 중요함

스코프, 클로저

변수, 스코프, 클로저 개좋은 글

ES5의 식별자 생명 주기는 함수 레벨 스코프를 지원하며, ES6+부터는 식별자의 생명 주기의 제어를 더 잘 관리하기 위해 블록 레벨 스코프의 개념이 추가되었다.

자바스크립트 ES6+는 함수 레벨 스코프(ES5)와 블록 레벨 스코프(ES6+)의 렉시컬 스코프(정적 스코프) 규칙을 가진다.
-> 저번에 책에서 블록 레벨 스코프 없다고 되어있던 것 틀림

ES6의 let, const 키워드는 블록 레벨 스코프 변수를 만들어준다. aaa 변수가 if문 블록에서 선언되었으므로, 해당 블록 밖에서는 잘못된 참조 에러가 발생한다. const, let 키워드로 선언한 변수는 모든 코드 블록(함수, if문, for문, while문, try~catch문 등)을 지역 스코프로 인정한다.

호이스팅

자바스크립트의 모든 선언에는 호이스팅이 일어난다.
-> 변수는 선언문, 할당문에 관계 없는듯 합니다 const의 경우 선언문이 없기 때문에 그렇게 생각
-> 함수는 선언문에서만 호이스팅이 일어남

그런데 let, const, class를 이용한 선언문을 호이스팅이 발생하지 않는 것처럼 동작한다.
var 키워드로 선언된 변수와는 달리 let 키워드로 선언된 변수를 선언문 이전에 참조하면 참조 에러(ReferenceError)가 발생한다.
이는 let 키워드로 선언된 변수는 스코프의 시작에서 변수의 선언까지 *일시적 사각지대(Temporal Dead Zone; TDZ)에 빠지기 때문이다. -> TDZ는 스코프의 시작 지점부터 초기화 시작 지점까지의 사각지대 구간을 뜻한다.
비유하자면 선언 단계와 초기화 단계 사이에 잠시 머물러 있는 것이라고 할 수 있다

여기서 중요한 지점은 이 호이스팅이라는 용어가 ‘선언이 먼저 메모리에 저장되었다.’는 것을 의미하기 때문에 즉, ‘선언이 끌어올려진다’는 의미이기 때문에 모든 선언은 호이스팅이 일어난다는 말은 참이된다.
즉, 호이스팅이 파일의 맨 위로 끌어올려진 것 같은 현상을 의미할 때 선언문 이전에 참조해서 에러를 발생시킨다고 호이스팅이 일어나지 않은 것은 아니라는 의미이다.
왜냐하면 정말 선언은 끌어올려진 것이 맞다. (표현하면 그렇고 정확히는 선언이 코드 실행 전에 메모리에 저장되었다는 의미이다.)
그런데 왜 오류가 나는가 하면 var 키워드는 선언과 함께 undefined로 초기화되어 메모리에 저장되는데 let과 const는 초기화되지 않은 상태로 선언만 메모리에 저장되기 때문이다.
초기화 되지 않으면 변수를 참조할 수 없다. 그래서 참조 에러를 일으키는 것이다.
let과 const에도 호이스팅이 일어나기 때문에 에러를 일으키는 것이다.

중첩된 함수에서 유효 범위가 달라진다

중첩된 함수를 반환하는 함수

  ■ 예제
/* makefunc함수는 호출될 때마다 함수를 반환.

  함수 안의 유효 범위는 함수가 호출될 때마다 달라진다. */

function makefunc(x)

{

  return function() { return x; }

}

 

// makefunc함수를 수차례 호출. 결과를 배열에 저장.

var arr = [ makefunc(0), makefunc(1), makefunc(2) ];

 

/* 모든 함수의 몸체는 동일하지만 유효범위가 다르다.

   따라서 각 호출은 서로 다른 값을 반환. */

alert(arr[0]());  // output : 0

alert(arr[1]());  // output : 1

alert(arr[2]());  // output : 2
  ■ 특징

    - 바깥 쪽 함수가 호출될 때 반환되는 함수의 자바스크립트 코드는 항상 동일하지만 반환되는 각 함수의 유효 범위는 

      조금씩 차이가 난다. 

    - 바깥 쪽 함수에 대한 전달인자 값이 각 호출 때마다 달라지기 때문이다.  

      (즉, 바깥 쪽 함수를 호출할 때마다 유효 범위 체인 안의 호출 객체가 달라진다.) 

    - 함수 호출 시 서로 다른 반환값을 초래하는 요소는 함수가 정의될 당시의 유효 범위 때문이다.

  ■ 중첩된 함수의 호출 객체

    - 중첩된 함수가 생성되면 이 함수의 정의는 호출 객체를 가리킨다.

      (함수가 정의된 유효 범위의 맨 앞에 호출 객체가 존재)

    - 중첩된 함수가 바깥 함수의 내부에서만 사용된다면 이 함수에 대한 유일한 참조는 호출 객체 내부에만 존재.

  ■ 중첩된 함수에 대한 참조를 전역 유효 범위 안에 저장

    - 중첩된 함수에 대한 외부 참조가 존재.

    - 중첩된 함수는 바깥 함수의 호출 객체를 가리키는 참조를 계속 간직한다.

    - 한 번 호출된 바깥 함수를 위하여 생성된 호출 객체는 계속해서 살아남고 함수 전달인자와 지역 변수의 이름과  

      값들은 이 호출 객체 안에서 유지. 
profile
떠돌이 생활을 하는. 실업자는 아니지만, 부랑 생활을 하는

0개의 댓글