Javascript - 호이스팅, 스코프, 클로저

goodjam92·2023년 4월 14일
0

Javascript

목록 보기
4/4
post-thumbnail

서론

실행 컨텍스트 글을 작성하며 제대로 설명하지 못하였던 개념인 Environment Record 내부에서 수행되는 호이스팅, 스코프 그리고 클로저에 대해 알아보도록 하겠습니다.

호이스팅 (hoisting)

호이스팅이란 변수와 함수의 메모리 공간을 선언 전에 미리 할당하는 것을 의미합니다. 쉽게 말하면 코드는 위에서 아래로 실행 되기에 코드 실행 전에 식별자 정보를 수집하는 동작으로 인해 식별자들을 코드의 최상단으로 올려준다라고 생각하시면 됩니다. 그럼 호이스팅의 과정을 알아보기 위해 변수와 함수에서의 동작을 확인해보겠습니다.

변수 호이스팅

  • var
console.log(전역변수); // undefined

var 전역변수 = 100;

cosoel.log(전역변수); // 100

var의 경우에는 호이스팅 되었을 때 변수 명과 함께 값이 초기화되어 undefined로 저장됩니다. 그래서 선언문 전에 변수를 호출하면 undefined인 것을 볼 수 있습니다. 이후 선언문 다음에 호출하면 제대로 된 값이 나오는 것을 볼 수 있습니다.

  • const, let
console.log(나변수); // reference Error 

let 나변수 = 1;
console.log(나변수); // 1

const, let의 경우에는 변수명에 대한 호이스팅이 수행되지만, 값을 초기화하지 않습니다. 그렇기에 선언문 이전에 호출하면 에러를 띄우고, 선언문 이후에 호출해야 변수 값이 나오는 것을 볼 수 있습니다.

함수 호이스팅

  • 함수 선언문
함수();	// "난 함수다" 

function 함수 () {
  console.log("난 함수다");
}

함수 선언문의 자바스크립트 엔진이 함수 선언과 동시에 완성된 함수 객체를 생성해서 환경 레코드에 기록합니다. 그렇기에 함수 선언문 이전에 함수를 호출해도 함수가 실행됩니다.

  • 함수 표현식
함수1(); // undefined => Type Error

var 함수1 = () => {
  console.log("함수 1이다");
}

함수2(); // Reference Error

const 함수2 = () => {
  console.log("함수 2이다");
}

함수2(); // "함수 2이다"

함수 표현식의 경우에 var 변수에 담고있는 함수는 호이스팅 단계에서 undefined로 초기화되어 호출 될 수 없기에 에러가 발생합니다. 그리고 const 변수에 담고있는 함수는 변수 호이스팅과 동일하여 선언문 이전에는 값이 없기에 에러가 발생하고 선언문 이후에 값이 호출됩니다.

스코프 (scope)

현재 실행되는 컨텍스트를 말하며 식별자에 대한 유효범위를 의미합니다. 만약 식별자가 해당 스코프 내에 존재하지 않는다면 상위 스코프인 직전 컨텍스트에 접근하여 식별자가 있는지 확인하게 되는데 이를 스코프 체인(scope chain) 이라고 합니다. 그리고 이를 가능하게 하는 것은 바로 Lexical Environmentouter Enviroment Reference 입니다.

let a = 1;

function 외부함수() {
  function 내부함수() {
	  console.log(a); // undefined (1)
      let a = 5;
    
    function 내부안의내부함수() {
      console.log(a); // 5 (2)
      console.log(b); // Error : b is not defined (3)
    }
    내부안에내부함수();
  }
  내부함수();
  
  console.log(a); // 1 (4)
}

외부함수();

위 코드의 console.log()들에 대해 순서대로 하나씩 알아보겠습니다.

  • (1) console.log(a) : 내부함수의 a가 함수 이전에 선언되었는데 console.log에는 undefined로 나옵니다. undefined는 내부함수의 a변수를 가리킨다고 보시면 됩니다.

  • (2) console.log(a) : 내부안의내부함수의 console.log(a)는 현재 실행 컨텍스트에 a 식별자가 없기에 직전 컨텍스트인 내부함수에서 식별자를 찾게됩니다. 그 결과로 5가 호출됩니다.

  • (3) console.log(b) : 현재 실행 컨텍스트에 b가 없기에 내부함수에 접근하여 식별자를 찾습니다. 이곳에도 b 식별자를 찾을 수 없기에 외부함수에 접근하고 결국 식별자를 찾지 못해 없다는 결론을 내리고 error를 발생시킵니다.

  • (4) console.log(a) : 외부함수의 실행 컨텍스트에는 a가 없기에 직전 컨텍스트에서 선언 된 a의 값을 호출합니다.

실행 컨텍스트가 추가가 될 때마다 outer environment Reference로 직전 컨텍스트를 참조하게 됩니다. 이 outer를 통해 하위 스코프에서 상위 스코프로의 접근 즉 직전 컨텍스트에 대해 접근을 하게 됩니다. 이로 인해 현재 컨텍스트에서 변수를 발견하지 못하거나 call stack에 동일한 변수명이 있다면 자바스크립트 엔진이 outer를 활용해 안쪽에서 부터 바깥쪽으로 탐색해가며 식별자 결정을 하게됩니다.
이 때 찾던 식별자를 발견하게 되면 더 이상 상위 스코프로의 탐색을 진행 하지않고, 식별자 결정이 됩니다.

변수 섀도잉

  • 동일한 식별자로 인해 상위 스코프에서 선언된 식별자의 값이 가려지는 현상

클로저 (closure)

클로저는 외부 변수를 기억하고 이 외부 변수에 접근할 수 있는 함수를 의미한다.
(모던 JavaScript 튜토리얼에서..)

다시 말해 내부 함수가 외부 함수의 context에 접근할 수 있는 것을 의미합니다.

function 외부함수() {
  const name = "goodjam";
  function 내부함수() {
   	console.log(name);
  }
  return 내부함수();
}

const a = 외부함수(); // "goodjam"

클로저는 내부함수와 밀접한 관계를 가지고 있습니다. 내부함수는 외부함수의 지역 변수에 접근할 수 있습니다. 외부 함수가 실행이 종료되어 소멸된 이후에도 내부함수가 외부함수의 변수에 접근할 수 있습니다. 이러한 메커니즘을 클로저라고 합니다.

function 친구저장(name) {
  return {
    이름보기 : function () {
    	return console.log(name);
    },
    이름수정 : function (changeName) {
    	return name = changeName;
    }
  }
}

const 고등학교친구 = 친구저장("홍길동");
const 대학교친구 = 친구저장("김철수");

console.log(고등학교친구.이름보기()); // "홍길동"
console.log(대학교친구.이름보기());  // "김철수"

고등학교친구.이름수정("홍길순"); 
console.log(고등학교친구.이름보기()); // "홍길순"
console.log(대학교친구.이름보기());  // "김철수"

위의 코드처럼 친구저장함수의 name 매개변수에 접근하는 return의 내장 함수들이 있는데 친구저장함수가 변수에 선언되어 실행되고, 종료되어도 내부 함수에서는 계속 외부함수의 값을 접근할 수 있습니다. 그리고 동일한 외부 함수에서 선언된 내부함수, 메소드는 외부함수의 지역변수를 공유합니다.
고등학교친구, 대학교친구는 동일한 외부함수를 공유하고있지만 결과는 서로 다르게 나옵니다. 그 이유는 외부함수가 실행될 때마다 새로운 지역변수를 포함하는 클로저가 생성되기에 둘은 서로 다른 독립된 객체가되기 떄문입니다.

정리

  • 호이스팅이란 변수와 함수의 메모리 공간을 선언 전에 미리 확보하는 것을 말한다.
  • var는 호이스팅 시 변수명 선언과 값이 초기화 되고, constlet은 변수명 선언만 된다.
  • 함수 선언식은 호이스팅 시 함수선언과 동시에 완전한 객체가 저장되고, 함수 표현식은 const, let변수와 동일하게 처리된다.
  • 스코프는 현재 실행되는 컨텍스틀 말하며 식별자의 유효 범위를 의미한다.
  • 식별자가 현재 컨텍스트에 존재하지 않으면 outer를 통해 직전 컨텍스트에 접근하여 식별자를 탐색한다. 이를 스코프체인이라고 한다.
  • 클로저란 외부의 변수를 내부 함수에서 접근할 수 있는 것을 말한다.
  • 외부 함수가 종료되어도 외부 함수의 지역 변수에 내부 함수들이 접근할 수 있다.

.
.
.
.
.

참고사이트
자바스크립트를 모르는 친구의 고개를 끄덕이게 하기 (feat.실행 컨텍스트)
10분테코톡-하루의 실행컨텍스트
생활코딩-클로저
MDN - 클로저

profile
습관을 들이도록 노력하자!

0개의 댓글