[JavaScript] 내가 한번 Scope에 접근해 보지!

hyeonbin·2023년 4월 25일
0

JS 계란반 스터디

목록 보기
2/12
post-thumbnail

📃 스코프 (Scope)

📍 스코프란?

참조 대상 식별자를 찾아내기 위한 규칙

  • 사전적 단어로 범위라는 의미다.

  • 식별자 ( 변수, 함수, 클래스 )에 접근할 수 있는 범위가 존재한다.

  • 범위는 중괄호 ( 블록 ) 또는 함수에 의해 나눠진다.
    → 그 범위를 Block ScopeFunction Scope라고 부른다.

  • 자바스크립트는 함수 레벨 스코프를 따른다.
    → 단, ES6에서 도입된 let 키워드를 사용하면 블록 레벨 스코프를 사용할 수 있다.


※ 모든 예제는 var대신 let으로 함 ( 왜냐? var은 쓸모없는 녀석이니깨... 근데 잠깐 등장 )

블록 레벨 스코프 (Block-level scope)

// 입력
let name = 'kim';
if (name) {
	let message = `HiHiHi, ${name}!`;
  	console.log(message);
}

console.log(message);

// 출력
HiHiHi, Kim!
ReferenceError // message is not defined
  • 모든 코드 블록 ( 함수, if문, for문, while문, try/catch문 등 ) 내에서 선언된 변수는 코드 블록 내에서만 유효하며 코드 블록 외부에서는 참조 불가하다.

  • 코드 블록 내의 콘솔은 잘 출력한다. 그러나, let 키워드로 선언된 message 변수는 변수가 선언된 코드블록 외부에서는 접근 불가이기에 에러를 출력한다.


함수 레벨 스코프 (Function-level scope)

// 입력
let name = "kim";

function greeting () {
	let hi = 'hello!'
    console.log(hi + " " + name);
}

greeting();
console.log(hi);

// 출력
hello! kim
ReferenceError // hi is not defined
  • 함수 내에서 선언된 변수는 함수 내에서만 유효하며 함수 외부에서는 참조 불가하다.

  • 함수 블럭에 의해 스코프에 구분이 생기게 되었고 함수 스코프 내부에서 선언된 hi 변수는 함수 외부에서 접근 불가하다.



📍 스코프 규칙

1. 스코프는 중첩이 가능하다.
중첩 스코프는 가장 인접한 지역을 우선하여 참조한다.

2. 내부에서 선언된 변수는 외부 스코프에서 접근 불가하다.
외부 함수 스코프에서 내부 함수 스코프로 접근 불가하다.

3. 외부에서 선언된 변수는 내부 스코프에서 접근 가능하다.
내부 함수 스코프는 외부 함수 스코프에 접근 가능하다.

4. 지역 스코프(Local scope)는 전역 스코프(Global scope)보다 더 높은 우선순위를 가진다.
전역이 아닌 다른 스코프는 전부 지역 스코프다.


전역 스코프 - 지역 스코프 / 전역 변수 - 지역 변수

  • 전역에서 선언된 변수는 전역 스코프를 가지는 전역 변수
  • 지역에서 선언된 변수는 지역 스코프를 가지는 지역 변수

예제 1

let kim = 'hello'; // 전역 변수

function kimkim() {
  let kim = 'bye'; // 지역 변수
  console.log(kim);
}

kimkim();         // ?
console.log(kim); // ?
  • 2개의 kim 변수가 있고, 전역에는 'hello' 지역에는 'bye'일 때 출력되는 값은?

- 서로 다른 스코프에서 정의된 변수들은 변수명이 같더라도 다른 변수로 취급한다.
- 만약 코드블록이 없다면 변수 재선언으로 에러가 발생한다.


예제 2

let kim = 'hello'; // 전역 변수

function kimkim() {
  kim = 'bye'; // 지역 변수
  console.log(kim);
}

kimkim();         // ?
console.log(kim); // ?
  • 만약 2개의 kim 변수 중, 하나의 let 키워드를 제거했을 때 출력되는 값은?

- 전역 변수 kim 변수가 'bye'로 재할당되어 하나의 kim 변수만 존재한다.


function kim() {
    console.log('나는 전역 kim씨');
}

function kimkim() {
    function kim() {
        console.log('나는 지역 kim씨');
    }
    kim();
}

kimkim(); // ?
kim();    // ?
  • 변수 뿐만 아니라 함수도 동일!

- 함수 kimkim()을 호출하면 내부에 지역 스코프를 가지고 있기에 '나는 지역 kim씨'를 출력한다.
- 함수 kim()을 호출하면 전역 스코프이므로 '나는 전역 kim씨'를 출력한다.

=> 이처럼 스코프 규칙을 잘 알아두어야 선언된 변수를 불러올 수 없어 생기는 reference 에러를 방지하고, 고정되어야 하는 변수가 함수 내에서 바뀌게 되는 것을 방지한다.


암묵적 전역

let x = 10; // 전역 변수

function kim () {
  // 선언하지 않은 식별자
  y = 20;
  console.log(x + y);
}

kim(); // 30
  • kim() 함수가 호출되면 자바스크립트 엔진은 변수 y에 값을 할당하기 위해 먼저 스코프 체인을 통해 선언된 변수인지 확인한다.
    스코프 체인 : 각각의 스코프가 어떻게 연결(chain)되고 있는지 보여주는 것
  • y=20;이 실행되면 참조 에러가 발생할 것 처럼 보이지만, 선언하지 않은 식별자가 마치 선언된 변수처럼 동작한다.
  • 왜? 선언하지 않은 식별자에 값을 할당하면 전역 객체의 프로퍼티가 되기 때문이다!
    즉, y=20window.y=20으로 해석해 프로퍼티를 동적 생성한다.
  • 그러나, y는 변수 선언없이 단지 전역 객체의 프로퍼티로 추가되었을 뿐, y는 변수가 아니다. ( 그래서 호이스팅이 발생하지 않음 ↓ )
// 전역 변수 x는 호이스팅이 발생
console.log(x); // undefined
// 전역 변수가 아니라 단지 전역 프로퍼티인 y는 호이스팅이 발생 X
console.log(y); // ReferenceError: y is not defined

let x = 10; // 전역 변수

function kim () {
  // 선언하지 않은 변수
  y = 20;
  console.log(x + y);
}

kim(); // 30

let x = 10; // 전역 변수

function kim () {
  // 선언하지 않은 변수
  y = 20;
  console.log(x + y);
}

kim(); // 30

console.log(window.x); // 10
console.log(window.y); // 20

delete x; // 전역 변수는 삭제안 됨
delete y; // 프로퍼티는 삭제됨

console.log(window.x); // 10
console.log(window.y); // undefined
  • 변수가 아니라 단지 프로퍼티인 y는 delete 연산자로 삭제 가능하다.
  • 전역 변수 x는 프로퍼티이지만 delete 연산자로 삭제 불가능하다.

비 블록 레벨 스코프 (Non block-level scope)

// 입력
if (true) {
  var kim = 5;
}
console.log(kim);

// 출력
5
  • 코드 내에서 변수 kim이 선언되었지만, 자바스크립트는 기본적으로 함수 레벨 스코프를 따르기에 위 예제는 함수 내에서 선언된 것이 아니다.
  • 함수 밖에서 선언된 변수는 if 블록 내에서 선언되었더라도 모두 전역 스코프를 가진다.
  • var 키워드로 선언된 변수가 호이스팅에 의해 함수 레벨 스코프의 최상단으로 끌어올려지기 때문에, 따라서 변수 kim은 전역 변수라서 5를 출력한다.

// 입력
if (true) {
  let kim = 5;
}
console.log(kim);

// 출력
ReferenceError: kim is not defined
  • var과 let 차이

- 코드 내에서 변수 kim이 선언되었고, 자바스크립트는 기본적으로 함수 레벨 스코프를 따르지만 let 키워드는 블록 레벨 스코프가 사용 가능하다.

- 내부에서 선언된 변수는 외부에서 접근 불가니까, 변수 kim은 에러를 출력한다.


최소한의 전역변수 사용

let MYAPP = {};

MYAPP.student = {
  name: 'Lee',
  gender: 'male'
};

console.log(MYAPP.student.name);
  • 전역 변수는 프로그램의 유연성을 악화시킨다.
  • 따라서, 다음과 같이 전역 변수 객체 하나를 만들어 사용한다.

즉시 실행 함수를 이용한 전역변수 사용 억제

(function () {
  let MYAPP = {};

  MYAPP.student = {
    name: 'Lee',
    gender: 'male',
  };

  console.log(MYAPP.student.name); // Lee
})();

console.log(MYAPP.student.name); // ReferenceError: MYAPP is not defined
  • 전역 변수를 만들지 않아서 라이브러리 등에 자주 사용된다.
  • 즉시 실행되고 그 후 전역에서 바로 사라진다.


📍 렉시컬 스코프

  1. 자바스크립트를 비롯한 대부분의 프로그래밍 언어는 렉시컬 스코프(Lexical scope)를 따른다.

  2. 함수를 어디서 호출하는지가 아니라 어디에 선언하였는지에 따라 결정한다.

  3. 다른 말로 정적 스코프(Static scope)라고도 부른다.

  let x = 10;

  function kim() {
    let x = 100;
    lee();
  }

  function lee() {
    console.log(x);
  }

  kim(); // ?
  lee(); // ?
  • 자바스크립트에서는 위와 같은 코드를 작성할 때, 이미 실행 단계에서 코드들의 스코프를 결정한다.
    → 전역 범위에 있는 변수 x
    kim() 함수 안에 있는 변수 x
    lee() 함수 안에 있는 변수 x

  • 위 예제의 실행 결과는 함수 lee()상위 스코프가 무엇인지에 따라 결정한다.
    → 즉, 함수가 어디에서 선언되었는지가 중요하다.

  • 상위 스코프를 결정하는 2가지 방식
    1) 동적 스코프 - 함수를 어디서 호출했는지에 따라 상위 스코프 결정
    → 함수 lee()의 상위 스코프는 함수 kim()과 전역이다.

    2) 렉시컬 스코프 - 함수를 어디서 선언했는지에 따라 상위 스코프 결정
    → 함수 lee()의 스코프는 전역이다.

=> 따라서, lee() 함수가 kim() 함수 안에서 호출된 것과 상관없이 lee() 함수는 전역 범위에 선언되어 있으므로, 전역 범위에 있는 변수 x의 값 10이 두 번 출력

profile
할 수 있다고 믿는 사람은 결국 그렇게 된다 😄😊

0개의 댓글