스코프

Verba volant, scripta manent·2021년 1월 15일
0

JavaScript

목록 보기
6/20
post-thumbnail

Scope

1. Scope의 정의

Scope는 "변수의 접근할 수 있는 범위"로 사용된다.

변수의 접근할 수 있는 범위 라는 내용이 이해가 가지 않을 수도 있다.

아래 예시를 살펴보자.

ex)
let greeting = 'Hello';
function greetSomeone() {
  let firstName = 'Josh';
  return greeting + ' ' + firstName;
}

greetSomeone();
firstName;

여기서 greetSomeone() 과 firstName을 실행해보자.

firstName을 실행해보면 에러 메시지가 뜰까?

그 이유는 변수 firstName은 function greetSomeone() 안에서만 사용할 수 있기 때문이다.

그래서 Scope를 배워야할 중요성이 여기서 생긴다.

2. Scope의 종류

1) Global Scope (전역 스코프)

모든 곳에서 해당 변수에 접근할 수 있다는 의미다.

ex)
let greeting = 'Hello';
function greetSomeone() {
  let firstName = 'Josh';
  return greeting + ' ' + firstName;
}

greetSomeone(); // 'Hello Josh'
firstName; // ReferenceError

이 예제에서 전역 스코프는

let greeting = 'Hello';

greetSomeone(); // 'Hello Josh'
firstName; // ReferenceError

이다.

특정 지역에 묶여있지 않고 바깥에 있는 코드는 전역 스코프라고 할수 있다.
전역 스코프에 선언된 변수는 어디서든지 그 값을 참조할 수 있다.

2) Local Scope (지역 스코프)

해당 지역에서만 접근할 수 있기 때문에 범위를 벗어난 다른 지역에서는 접근할 수 없다는 의미다.

ex)
let greeting = 'Hello';
function greetSomeone() {
  let firstName = 'Josh';
  return greeting + ' ' + firstName;
}

greetSomeone(); // 'Hello Josh'
firstName; // ReferenceError

이 예제에서 지역 스코프는

function greetSomeone() {
  let firstName = 'Josh';
  return greeting + ' ' + firstName;
}

이다.
function greetSomeone()을 따로 선언함으로써 자신들만의 지역 스코프가 생성되었다.

지역 스코프 안쪽에서 선언된 변수는 바깥 지역에 영향을 주지 못한다.

3. Scope의 특징

  • Scope는 변수 접근 규칙에 따른 유효 범위를 말한다.

  • 변수는 특정 환경 내에서만 사용 가능하며, 프로그래밍 언어는 각각의 변수 접근 규칙을 가지고 있다.

  • Scope는 변수와 그 값이, 정확히 어디서부터 어디까지 유효한지를 판단하는 범위다.

  • JavaScript 에서는 기본적으로, 함수가 선언되는(lexical) 동시에 자신만의 Scope(범위)를 가지게 된다.

4. Local Scope vs Global Scope

  • 안쪽 Scope에서 바깥 변수/함수를 접근하는 것은 가능하나
    바깥쪽 Scope에서 안쪽 변수/함수를 접근하는 것은 불가능하다.

  • Scope 는 중첩이 가능하다.
    (함수 안에 함수를 넣을 수 있다.)

  • 전역 스코프는 최상단의 Scope로 어디서든 접근이 가능하다.

  • 지역 변수는 해당 함수 내에서 전역 변수보다 더 높은 우선순위를 가진다.★★

ex1)
let name = "Richard";

function showName() {
  let name = "Jack"; // 전역 변수
  // showName 함수 안에서만 접근 가능
  console.log(name);
}

console.log(name);
showName();
console.log(name);

이 문제를 풀어보면

console.log(name);은 Richard,
showName();은 Jack,
console.log(name);은 Richard가 나온다.

왜 그런 결과가 나올까?
let name = "Richard"; 는 전역 변수이다.
let name = "Jack"; 는 showName 함수 안에서만 접근 가능한 지역 변수이다.

따라서 처음 console.log(name); 을 입력하면 전역 변수인 "Richard"를 참조한다.
그 다음, showName(); 을 입력하면 showName 함수 안에서만 접근이 가능한 "Jack"을 참조한다.
마지막으로 console.log(name); 을 입력하면 전역 변수인 "Richard"를 참조하게 되는 것이다.

이번엔 전역 변수에 선언이 없는 경우의 문제를 풀어보자.

ex2)
let name = "Richard";

function showName() {
  name = "Jack"; // 전역 변수
  // 선언(let)이 없기 때문에, 바깥 scope에 있는 name이라는 변수를 가져온다.
  console.log(name);
}

console.log(name);
showName();
console.log(name);

여기서 주의해야할 점은 전역 변수는 name = "Jack"; 으로 선언(let)이 없다는 것이다.

이 문제를 풀어보면

이번에는 console.log(name);은 Richard,
showName();은 Jack,
console.log(name);은 Jack이 나온다.

왜 Richard로 나왔던 console.log(name);의 결과가 showName()을 실행 후 Jack으로 나왔을까?

방금 전역 변수인 name = "Jack"이 선언(let)이 없다고 했다
처음 console.log(name);을 실행하면 맨 처음 선언한 let name = "Richard"; 전역 변수를 참조하지만 지역 스코프인 function showName() 에 가면 선언(let)이 없기 때문에, 바깥 scope에 있는 name이라는 변수를 가져와 "Richard"가 "Jack"으로 재할당된다.
그래서 let name = "Richard";에서 let name = "Jack";으로 바뀌어 showName();을 실행 후의 console.log(name);을 실행하면 Jack이 되는 것이다.

5. Function Scope vs Block Scope

Block이란 중괄호로 시작하고, 끝나는 단위를 뜻한다.

ex) 
if (true) {
 console.log('i am in the block');
}

for (let i=0; i<10; i++) {
  console.log(i);
}

{
  console.log('it works');
}

라고 할때, block은 아래의 노란 부분이다.

if (true) {
console.log('i am in the block');
}

for (let i=0; i<10; i++) {
console.log(i);
}

{
console.log('it works');
}

아래의 문제를 풀어보자.

(문제1)
for(let i=0; i<5; i++) {
console.log(i);
}
console.log('final i:', i);

를 실행하면

console.log('final i:', i);는 ReferenceError가 뜬다.
왜 그럴까?

위 문제에서 block은 노란 부분이다.
for(let i=0; i<5; i++) {
console.log(i);
}
console.log('final i:', i);

위 block에서 console.log(i);를 실행하면 다섯번 iteration이 되어 0 1 2 3 4 가 나온다.

하지만 console.log('final i:', i);를 실행하면 block범위를 벗어나게 되므로 변수를 사용할 수 없게 되어 ReferenceError가 나오는 것이다.

이처럼 block안의 변수는 block 범위를 벗어나는 즉시 사용할 수 없다.

그럼 같은 문제로 선언을 let 대신 var로 바꾸어 실행해보자.

(문제2)
for(var i=0; i<5; i++) {
console.log(i);
}
console.log('final i:', i);

위 block에서 console.log(i);를 실행하면 역시 다섯번 iteration이 되어 0 1 2 3 4 가 나온다.
하지만 console.log('final i:', i);를 실행하면 let으로 선언해서 나온 결과와는 달리 5가 나온다. 왜 그럴까?

var는 let과는 다르게 block 범위를 벗어나도 같은 function scope 에서는 사용이 가능하다는 특이한 점이 있다.

여기서 한가지 의문이 들기 마련이다.
변수를 선언하는 let과 var는 어떻게 다른가?

1) 개발자 콘솔로 확인하는 var 키워드와 let 키워드

아래 캡쳐 부분을 통해 각각의 작동원리를 알 수 있다.

var 키워드를 실행했을때



let 키워드를 실행했을때




2) const 키워드

const 키워드란 값이 변하지 않는 변수, 즉 상수를 정의할 때 사용한다.

  • let 키워드와 동일하게 Block Scope를 따른다.

  • 값을 재정의하려고 하면 TypeError를 낸다.

let 키워드 vs const 키워드 vs var 키워드

let const var
유효 범위 Block Scope Block Scope Function Scope
값 재정의 가능 불가능 가능
재선언 불가능 불가능 가능

6. 전역 변수와 window 객체

<window의 정의>

전역 범위를 대표하는 객체

<window의 특징>
전역 변수에서 선언된 함수, 그리고 var 키워드를 이용해 선언된 변수는 window 객체와 연결한다.

ex)
var myName = "Paul";
console.log(window.myName); // Paul

function foo() {
  console.log('bar');
}

console.log(foo === window.foo); // true

★전역 범위에 너무 많은 변수를 선언하지 않도록 주의하라!★

7. 선언 없이 초기화된 전역 변수

절대로, 선언 키워드(var, let, const) 없이 변수를 초기화하지 마라!

function showAge() {
  // age는 전역 변수로 취급된다
  age = 90;
  console.log(age);
}
showAge(); // 90
console.log(age); // 90
profile
말은 사라지지만 기록은 남는다

0개의 댓글