Notice
Recent Posts
Recent Comments
Link
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
Archives
Today
Total
관리 메뉴

과거의 내가 미래의 나에게

딥다이브 :: 스코프 본문

js 이론

딥다이브 :: 스코프

양바삭 2023. 1. 24. 15:59

「딥다이브 자바스크립트」 정리 - 스코프

※ 아래 내용은 책을 통해 학습한 것을 개인적으로 정리한 것으로 내용이 다소 부정확 할 수 있습니다.

 


 

스코프란?

  • 식별자 참조 가능 유효 범위
    모든 식별자는 자신이 선언된 위치에 의해 자신을 참조할 수 있는 유효 범위가 결정된다. 이 유효 범위를 스코프라고 한다.
  • 식별자 검색 시 사용하는 규칙
    스코프를 통해 js 엔진은 이름이 같은 두 개의 변수 중 어떤 변수를 참조할 지 결정하기도 한다. 즉, 식별자를 검색할 때 사용하는 규칙인 것이다.
  • 네임스페이스
    스코프는 식별자에 범위를 제공하는 영역이다. 변수 이름은 이 영역 내에서만 유일하면 된다. 스코프가 있기에 동일한 변수 이름을 다양한 곳에서 사용할 수 있게된다.
< var 키워드 >
스코프 내에서 식별자는 유일해야 하지만, var 키워드는 스코프 내에서 중복 선언이 허용되어서 변수값이 재할당될 수도 있으니 기억해두자.
var test = 1
var test = 2
console.log(test) //2

 

스코프의 종류

1. 전역 스코프

전역에 변수를 선언하면 그 변수는 어디서든 참조할 수 있게된다. 전역 변수는 모든 스코프의 최상단 스코프이기에 어디서든 접근 가능함으로 쉽게 사용될 수 있다. 하지만 전역변수를 반드시 사용해야할 이유가 없다면 아래와 같은 부작용이 있을수 있기에 전역변수를 사용하는 것은 지양해야 할 것이다.

  1. 모든 코드가 전역 변수를 참조하고 변경할 수 있기에, 변수의 유효 범위가 크면 클수록 코드의 가독성이 나빠지고 의도치 않게 상태가 변경될 수 있는 위험성이 높아진다.
  2. 전역 변수는 웹페이지가 닫힐 때까지 유효하기에 그 생명 주기가 길어 메모리 리소스를 오랜 기간 소비한다.
  3. 전역 변수는 스코프 체인의 최상단에 위치하기에 검색 속도가 가장 느리다.
  4. 파일이 분리되어 있어도 하나의 전역 스코프를 공유하기에 동일한 이름의 변수로 하여금 예상치 못한 결과를 가져올 수 있다

위와 같은 이유로 전역 변수는 지양되어야하는데 그렇다면 전역 변수를 어떻게 억제할 수 있을까?

 

a. 즉시실행함수의 활용
즉시 실행 함수의 내부에다 변수를 선언하고 활용하면 이는 전역 변수를 생성하지 않는다.

 

b. 네임스페이스 객체
전역에 네임스페이스 역할을 담당할 객체를 생성하고 전역 변수처럼 사용하고 싶은 변수를 프로퍼티로 추가하는 방법이다. 그러나 이는 네임스페이스 객체 자체가 전역 변수에 할당되므로 의미는 없어보인다. 왜 있는거야 그럼

var TEST = {};
TEST.name = 'Lee'
TEST.person = {
	age: 20,
	address: 'seoul'
} 

console.log(TEST.name) // Lee

c. 모듈 패턴
클래스를 모방하여 관련 있는 변수와 함수를 모아 즉시 실행 함수로 감싸 하나의 모듈을 만든 것이다. 이를 통해 전역 변수 생성을 억제하기도 하지만 캡슐화까지 구현 가능해진다.

const Counter = (function() {
	var num = 0;
	return {
		increase() {
			return ++num
		}
	}
}())
console.log(Counter.num) // 접근 불가
console.log(Counter.increase) // 1

d. ES6 모듈
ES6 모듈은 파일 자체의 독자적인 모듈 스코프를 제공하기에 더는 전역 변수를 사용할 수 없다. 따라서 var 키워드로 선언한 변수는 더이상 전역 변수가 이니며 window 객체의 프로퍼티도 아니다. script 태그에 type=’module’을 추가하면 해당 js 파일은 모듈로서 동작한다.

 

2. 지역 스코프

지역은 함수 몸체 내부를 가리킨다. 지역 스코프에 선언된 지역 변수는 자신의 지역 스코프와 그 하위 지역의 스코프에서 유효하다.

 

스코프 체인

함수는 중첩해서 정의할 수 있다. 함수를 정의(외부함수)하고 그 안에 또 함수(중첩함수)를 정의할 수 있다는 것이다.

함수가 중첩되면 함수의 지역 스코프도 같이 중첩된다. 중첩 함수의 지역 스코프는 외부 함수의 지역 스코프와 계층적 구조를 갖는다. 이 때 외부 함수의 지역 스코프를 중첩 함수의 상위 스코프라고 한다.

 

이렇게 모든 스코프는 하나의 계층적 구조로 연결되며 모든 지역 스코프의 최상단 스코프는 전역 스코프이다.

 

이와 같은 구조를 스코프 체인이라 한다. 변수를 참조할 때 js 엔진은 스코프 체인을 통해 참조하는 코드의 스코프에서 시작하여 상위 스코프 방향으로 이동하며 변수를 검색한다. 상위에서 하위로 이동하는 일은 없으므로 이는 하위 스코프에서 유효한 변수를 상위 스코프에서 참조할 수 없음을 의미한다.

 

함수 레벨 스코프와 블록 레벨 스코프

함수 레벨 스코프는 함수의 코드 블록만을 기준으로 하여 지역 스코프를 생성한다. 함수 레벨 스코프를 사용하는 대표적인 것은 var 키워드이다.

블록 레벨 스코프는 코드 블록을 기준으로 하여 지역 스코프를 생성한다. ES6에서 도입된 let, const는 이를 따른다.

function test () {
	var i = 10;

	for(var i = 0; i<2; i++ ){
		//...
	}
console.log(i)	
}
// var는 함수 레벨 스코프이기에 for 블록 안에서 선언한 변수를 for 블록 밖에서 불러올 수 있다.
function test () {
	let h = 10;
	
	for(let i = 0; i<2; i++ ){
		//...
	}
	console.log(i)
}
// let은 블록 레벨 스코프이기에 for 블록 안에서 선언한 변수를 for 블록 밖에서 불러올 수 없다. 

 

 동적 스코프와 렉시컬 스코프

동적 스코프는 함수를 호출되는 시점에 상위 스코프가 결정되는 것이다.

렉시컬 스코프(혹은 정적 스코프)는 함수를 정의한 시점에 상위 스코프를 결정한다.

var x = 1;

function foo() {
	var x = 10;
	bar()
}

function bar() {
	console.log(x)
}

foo()
bar()

// bar의 상위 스코프는?
// 동적 스코프: foo 함수와 전역
// 정적 스코프: 전역

js는 렉시컬 스코프를 따르므로 함수를 어디서 정의했는지에 따라 상위 스코프를 결정한다.

함수의 상위 스코프는 함수 정의가 실행될 때 정적으로 결정되기에 함수가 호출된 위치는 상위 스코프에 어떠한 영향도 주지 않고 단지 함수가 정의된 곳이 그 기준이다.

위의 예시에서 함수 선언문으로 정의된 bar 함수는 전역 코드가 실행되기 전에 먼저 평가되어 함수 객체를 생성한다. 이 때, bar 함수 객체는 자신이 정의된 곳의 상위 스코프를 기억하고 호출될 때마다 기억한 상위 스코프를 기준으로 참조한다.

Comments