Javascript Hoisting, Scope, Closure

Daen12·2022년 11월 1일

var과 let의 차이, Hoisting

var는 한번 선언된 변수를 다시 선언할 수 있다.

let name = 'Mike';
let name = 'Jane';
console.log(name) //error!

var name = 'Mike';
var name = 'Jane';
console.log(name) //Jane

var는 선언하기 전에 사용 가능하다.
var로 실행한 모든 변수는 최상위로 끌어올려진것 처럼 동작한다.(=호이스팅)
그러나 여기서 콘솔은 undefined가 나오는데, 선언은 호이스팅 되지만 할당은 호이스팅되지 않기 때문. 그래서 error가 아닌 undefined가 나온다.

var name; //선언
console.log(name); //undefined
name = 'Mike'; //할당

호이스팅 : 스코프 내부 어디서든 변수 선언은 최상위에 선언된 것처럼 행동하는 것.

let은 같은 상황에서 에러를 내는데, 이는 Temporal Dead zone과 관련이 있다.

let과 const는 TDZ의 영향을 받는다. 할당을 하기 전에는 사용할 수 없기 때문. 이는 코드를 예측가능하게 하고 잠재적인 버그를 줄일 수 있다.

스코프(Scope)

호이스팅은 스코프 단위로 일어난다.
스코프는 변수이름, 함수 이름, 클래스 이름과 같은 식별자가 본인이 선언된 위치에 따라 다른 코드에서 자신이 참조될 수 있을지 없을지 결정되는 유효범위이다.
변수가 전역에서 선언된다면 전역스코프, 지역에서 선언된다면 지역스코프를 갖게된다.

var은 함수 스코프, let과 const는 블록스코프이다.
블록스코프 : 모든 코드블록에서 선언된 변수는 코드블록 내에서만 유효하며 외부에서는 접근할 수 없다. 여기서 코드블록 = 함수, if문, for문, while문, try/catch문 등
함수스코프 : 함수 내에서 선언된 변수만 그 지역변수가 된다.

const age = 30;
if(age>19){
	var txt = '성인';
}
console.log(txt);//'성인'

위와 같이 var은 if문 밖에서도 사용 가능하지만, let과 const는 중괄호 밖에서 사용 불가능하다. var도 마찬가지로 함수 내에서 선언되면 밖에서 사용 불가능하다.

함수 내부에 함수가 중첩되어있을때 계층적인 구조가 형성되는데, 이렇게 물리적으로 존재하는 스코프 계층을 스코프 체인이라고 한다.

주의할것! 참조할 스코프는 위(부모)로만 올라간다. 즉, 마지막 라인의 y는 전역스코프에서 참조 가능한 값이 없기 때문에 내려가서(자식) 찾는 대신 Reference error가 나온다. (=스코프 체인의 단방향성)

자바스크립트는 정적스코프(=렉시컬 스코프)이다. 함수는 호출됐을때가 아닌, '태어나는 동시에' 본인의 내부슬롯에 상위스코프에 대한 참조를 저장한다.

클로져(Closure)


위 코드에서 outer함수가 ella에 할당되면서 생명주기가 마감되었는데 어떻게 불러올 수 있을까?
마감된 외부함수의 지역변수 x를 inner가 참조 가능한데, 이때 inner를 closure라고 한다.
ella는 inner 함수 객체를 참조하고, inner객체는 내부슬롯의 outer 렉시컬 환경을 참조하기 때문에, garbage collection의 대상이 되지 않는다.

정리 :
1. 한 함수가 상위스코프의 식별자를 참조하고 있고,
2. 본인의 외부함수보다 더 오래 살아있다면
이 중첩함수는 클로져이다.

추가 예제 1.


let과 function에서 선언한 변수들 모두 lexical 환경으로 올라가며 시작한다.

  1. let은 할당되지 않은 상태이므로 초기화(undefined)가 안된 반면, 함수 선언문은 변수와 달리 바로 초기화된다. (그러나 변수에 할당한 함수표현식은 이렇게 되지 않음)
  2. 이제 let one을 만났다. 아직 할당은 안되어 초기값 'undefined'를 갖는다.
  3. 이후 라인을 읽고 one에 숫자 1이 할당된다
  4. 마지막 라인으로 가서 함수를 실행한다. 이로서 새로운 lexical 환경이 만들어진다. 함수가 호출되는 동안 내부 어휘적환경과 전역 어휘적환경 두개를 가진다.
  5. 내부에서 num을 찾았으나, one은 전역환경에서 찾은 후 함수값을 리턴한다.

추가 예제 2.


1. 처음 전역 어휘적 환경에는 makeAdder:function, add3:function이 들어간다.
2. const add3 = makeAdder(3)을 실행하면 x:3이 makeAdder 레벨의 어휘적 환경에 들어간다.
3. add3(2)를 실행하면 익명함수의 어휘적환경에 y:2가 들어간다.
4. add3()함수가 생성된 이후에도 상위함수인 makeAdder의 x에 접근이 가능하다.

추가 예제 3.

전역환경 - makeCounter : function
makeCounter단위 어휘적환경 - num:0
익명함수 어휘적환경 - ' ':function

counter함수가 생성된 이후에도 상위함수인 makeCounter()의 num에 접근이 가능하다. 내부함수에서 외부함수의 num에 접근하여 값을 업데이트 한다.

num = 0 -> 1 -> 2 -> 3 ->...

++
https://joshua1988.github.io/web-development/javascript/function-expressions-vs-declarations/
이거 이해하기!

출처 : https://www.youtube.com/watch?v=PVYjfrgZhtU
https://www.youtube.com/watch?v=4_WLS9Lj6n4

profile
Discipline Beats Talent

0개의 댓글