연습장

15. Scope 스코프 본문

JavaScript

15. Scope 스코프

js0616 2024. 7. 18. 05:17

https://poiemaweb.com/js-scope

 

Scope | PoiemaWeb

스코프는 참조 대상 식별자(identifier, 변수, 함수의 이름과 같이 어떤 대상을 다른 대상과 식별할 수 있는 유일한 이름)를 찾아내기 위한 규칙으로 자바스크립트는 이 규칙대로 식별자를 찾는다.

poiemaweb.com

 

 

1. 스코프란?
변수나 함수가 유효한 범위를 말합니다. 어디서 변수나 함수에 접근할 수 있는지를 결정짓는 범위를 의미합니다.

 

var x = 'global';

function foo () {
  var x = 'function scope';
  console.log(x);
}

foo(); // function scope
console.log(x); // global

 

스코프는 변수나 함수의 이름 충돌을 방지하고, 코드의 유지보수성을 높이며, 프로그램의 효율성을 증가시키는 데 중요한 역할을 합니다.


 

2. 스코프의 구분

 

전역 스코프 (Global Scope)

  • 전역 스코프는 프로그램 전체에서 접근할 수 있는 스코프입니다.
  • 전역 스코프에 선언된 변수나 함수는 어디서든지 접근할 수 있습니다.

 

지역 스코프 (Local Scope)

  • 지역 스코프는 특정한 블록이나 함수 내에서만 접근할 수 있는 스코프입니다.
  • 이는 변수나 함수가 선언된 위치에 따라 결정됩니다.

 

3. 자바스크립트 스코프의 특징

 

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

  • 자바스크립트는 기본적으로 함수 레벨 스코프를 따릅니다. 이는 변수가 선언된 함수 내에서만 유효하며, 함수 외부에서는 접근할 수 없습니다.
  • var 키워드를 사용한 변수 선언
function exampleFunction() {
    var localVar = 10;
    console.log(localVar); // 10
}
exampleFunction();
console.log(localVar); // Error: localVar is not defined

 

 

블록 레벨 스코프 (Block-level Scope)와 let:

  • ECMAScript 6(ES6)에서 let 키워드가 도입되면서 블록 레벨 스코프를 사용할 수 있습니다. let으로 선언된 변수는 해당 블록 내에서만 유효합니다.
  • let과 const 키워드를 사용한 변수 선언
if (true) {
    let blockVar = 20;
    console.log(blockVar); // 20
}
console.log(blockVar); // Error: blockVar is not defined

 


4. 전역 스코프(Global scope)
전역에 변수를 선언하면 이 변수는 어디서든지 참조할 수 있는 전역 스코프를 갖는 전역 변수가 된다. 

var 키워드로 선언한 전역 변수는 전역 객체(Global Object) window의 프로퍼티이다.

 

C언어의 경우 전역 변수를 선언하기 위해서는 의도적으로 main 함수 밖에 변수를 선언하여야 한다.

자바스크립트는 특별한 시작점(Entry point)이 없어서 위 코드와 같이 전역에 변수나 함수를 선언하기 쉽다.

 

 

전역 변수를 남발하는 문제

  • 변수 이름이 중복 가능성
  • 의도치 않은 재할당에 의한 상태 변화
  • 코드를 예측어려움 -> 사용 억제

 

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

 

if (true) {
    var x = 5;
  }
  console.log(x);

 

자바스크립트는 블록 레벨 스코프를 사용하지 않으므로 함수 밖에서 선언된 변수는 코드 블록 내에서 선언되었다할지라도 모두 전역 스코프을 갖게된다.

 


 

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

var a = 10;     // 전역변수

(function () {
  var b = 20;   // 지역변수
})();

console.log(a); // 10
console.log(b); // "b" is not defined

 

자바스크립트는 함수 레벨 스코프를 사용한다. 즉, 함수 내에서 선언된 매개변수와 변수는 함수 외부에서는 유효하지 않다. 따라서 변수 b는 지역 변수이다.

 

var x = 'global';

function foo() {
  var x = 'local';
  console.log(x);

  function bar() {  // 내부함수
    console.log('내부함수:',x); // 내부함수:local
  }

  bar();
}
foo();
console.log(x); // global

 

함수 bar에서 참조하는 변수 x는 함수 foo에서 선언된 지역변수이다. 

이는 실행 컨텍스트의 스코프 체인에 의해 참조 순위에서 전역변수 x가 뒤로 밀렸기 때문이다.

 

 

함수(지역) 영역에서 전역변수를 참조할 수 있으므로 전역변수의 값도 변경할 수 있다.

내부 함수의 경우, 전역변수는 물론 상위 함수에서 선언한 변수에 접근/변경이 가능하다.

var x = 10;

function foo(){
  var x = 100;
  console.log(x); // 100

  function bar(){   // 내부함수
    x = 1000;
    console.log(x); // 1000
  }

  bar();
}
foo();
console.log(x); // 10

 

foo() 함수에 의해 출력되는 x는 foo 의 함수에서 정의된 지역변수 x 의 값 100이다. 

foo() 함수 내부의 bar() 에 의해 foo 의 지역변수 x가 1000으로 재할당되어 1000 이 출력된다.

함수가 종료되어 지역변수 x 는 사라지고 전역변수 x 의 값 10이 출력된다.

 

 

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

 

var foo = function ( ) {

    var a = 3, b = 5;
 
    var bar = function ( ) {
      var b = 7, c = 11;
 
  // 이 시점에서 a는 3, b는 7, c는 11
 
      a += b + c;
 
  // 이 시점에서 a는 21, b는 7, c는 11
 
    };
 
  // 이 시점에서 a는 3, b는 5, c는 not defined
 
    bar( );
 
  // 이 시점에서 a는 21, b는 5
 
  };

  // 이 시점에서는 a , b , c 모두 not defined

 


 

7. 렉시컬 스코프
아래 예제의 실행 결과를 예측해보자.

 

var x = 1;

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

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

foo(); // ?
bar(); // ?

 

foo() 는 지역변수 x 를 만들고 bar() 를 호출하여 console.log(x) 이때 지역변수 x 인 10을 출력

bar() 를 호출시 전역변수 x 인 1을 출력

 

이 아니네.. 

 

 

동적 스코프(Dynamic scope)

  • 함수를 어디서 호출하였는지에 따라 상위 스코프를 결정

 

정적 스코프(Static scope) 또는  렉시컬 스코프(Lexical scope)

  • 함수를 어디서 선언하였는지에 따라 상위 스코프를 결정

 

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

렉시컬 스코프는 함수를 어디서 호출하는지가 아니라 어디에 선언하였는지에 따라 결정된다. 

함수를 어디에서 호출하였는지는 스코프 결정에 아무런 의미를 주지 않는다.

 

foo() 안에서 bar() 를 호출하더라도 지역변수 x 가 아닌 전역변수 x 를 참조하여 1을 출력

 


 

8. 암묵적 전역

var x = 10; // 전역 변수

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

foo(); // 30

 

위 예제의 y는 선언하지 않은 식별자이다. 하지만 참조 에러가 발생하지 않고 선언하지 않은 식별자에 값을 할당하면 전역 객체의 프로퍼티가 된다.

 

  • foo 함수가 호출되면
  • 자바스크립트 엔진은 변수 y에 값을 할당하기 위해 스코프 체인을 통해 선언된 변수인지 확인한다.
  • 이때 변수 y의 선언을 찾을 수 없으므로 참조 에러가 발생해야 하지만
  • 자바스크립트 엔진은 y = 20을 window.y = 20으로 해석하여 프로퍼티를 동적 생성한다.
  • 결국 y는 전역 객체의 프로퍼티가 되어 마치 전역 변수처럼 동작한다.
  • 이러한 현상을 암묵적 전역(implicit global)이라 한다.

 

y는 변수가 아닌 전역 객체의 프로퍼티로 변수 호이스팅이 발생하지 않는다.

y는 delete 연산자로 삭제할 수 있다. 

(전역변수 x 는 삭제되지 않는다)


 

9. 전역 변수 최소화 

 

전역 변수를 남발하는 문제

  • 변수 이름이 중복 가능성
  • 의도치 않은 재할당에 의한 상태 변화
  • 코드를 예측어려움 -> 사용 억제

 

9.1 모듈 패턴 사용:

  • 자바스크립트에서는 모듈 패턴을 사용하여 전역 범위를 최소화할 수 있습니다.
  • 모듈 패턴은 클로저를 이용하여 특정 변수와 함수를 모듈 내에서 캡슐화하고, 외부에서 접근할 수 있는 인터페이스만 노출시키는 방식입니다.
var module = (function() {
    var name = "LEE";

    function sayHello() {
        console.log("Hello module");
    }

    function setName(word){
        name = word
    }

    function getName(){
        console.log(name)
    }

    return {
        setName:setName,
        getName:getName,
        sayHello:sayHello
    };
})();

module.sayHello();
module.setName('kim')
module.getName();

 

this.name = word 가 아니라고 하네.. 

-> 17 this 를 보면  setName 은 내부함수 이므로 this.name 으로 하게 되면 전역을 참조하게 됨

 

 

9.2 Strict 모드 활용:

  • Strict 모드를 사용하면 코드 실행을 보다 엄격하게 처리할 수 있습니다. Strict 모드에서는 암묵적으로 전역 변수를 선언하는 것을 방지하며, 실수를 사전에 방지할 수 있습니다.
  • Strict 모드를 활성화하는 방법은 코드 맨 위에 "use strict"; 를 추가하면 됩니다.
// 전역에 strict mode의 적용하는 것은 바람직하지 않다!
'use strict';

function foo() {
  x = 10; // ReferenceError: x is not defined
}
foo();

 

 

9.3 ES6의 let과 const 사용:

  • ES6에서 도입된 let과 const 키워드를 사용하여 블록 레벨 스코프를 활용할 수 있습니다.
  • 이들을 사용하면 변수가 선언된 블록 내에서만 유효하게 되어, 전역 변수의 사용을 억제할 수 있습니다.
let localVar = 20; // 블록 레벨 변수로 선언
const PI = 3.14;   // 상수로 선언

{
    let blockVar = 30; // 블록 내에서만 유효한 변수
}

 

 

9.4 네임스페이스 객체 활용:

  • 네임스페이스 객체를 사용하여 전역 변수를 최소화할 수 있습니다.
  • 예를 들어, 모든 전역 변수를 하나의 객체 안에 넣고 해당 객체를 통해 접근하는 방식입니다.
var myApp = {};

myApp.globalVar = 10;

myApp.someFunction = function() {
    console.log("Hello, world!");
};

 

(모듈화와 유사한느낌)

 

 

9.5 클래스와 모듈 시스템 사용

  • ES6 이후의 자바스크립트에서는 클래스와 모듈 시스템을 이용하여 전역 범위를 효과적으로 관리할 수 있습니다. 
  • 클래스를 사용하면 각 클래스의 인스턴스 변수가 각각의 객체에 속하게 되어 전역 변수의 남발을 줄일 수 있습니다.
class MyClass {
    constructor() {
        this.instanceVar = 10;
    }

    instanceMethod() {
        console.log("Instance method");
    }
}

const myObj = new MyClass();
myObj.instanceMethod();

 


 

스코프란?

 

자바스크립트의 스코프 특징은?

 

렉시컬 스코프란?

 

암묵적 전역이란?

 

전역변수를 최소화 해야하는 이유와 방법은?

 

 

 

https://chatgpt.com/

'JavaScript' 카테고리의 다른 글

17. this  (0) 2024.07.18
16. Strict mode  (0) 2024.07.18
14. Prototype 프로토타입  (0) 2024.07.17
13. Type Checking 타입 체크  (0) 2024.07.17
12. Function 함수  (0) 2024.07.16