시작하기 전

어찌해야 간결하고 이해하기 쉽게 작성할지 고민이 많았었습니다.
다시 About Javascript 카테고리 포스트 관련하여 공부한 내용 중 정리가 된 부분들을 작성해보도록 하겠습니다.

실행 컨텍스트/스코프에 대해 배우기 전

아래 요소에 대해서 먼저 설명을 합니다. 이부분을 이해하고 다음 실행 컨텍스트/스코프 섹션을 들으시면 이해에 도움이 되실겁니다.

  • 원시값
  • 참조값
  • 동적 프로퍼티
  • 값 복사
  • 매개변수
  • 타입판별

원시값과 참조값

변수에는 원시값과 참조값을 담을 수 있습니다.
원시값은 Undefined, Null, 불리언, 숫자, 문자열인 값입니다.
참조값은 메모리에 저장된 객체입니다.

자바스크립트는 메모리 위치에 직접 접근하는 것을 허용하지 않으며, 객체를 조작한다는 것은 객체 자체가 아니라 해당 객체에 대한 참조를 조작하는 것 입니다.

원시값과 참조 값의 차이

원시값과 참조값은 사용하는데 있어서 차이점이 존재합니다.
지금 숙지하지 않는다면 나중에 반드시 곤란할 수 밖에 없는 요소이기 때문에, 꼭 숙지하고 가야할 부분입니다.

1. 동적 프로퍼티

참조값은 동적 프로퍼티를 추가 할 수 있습니다.
참조값을 사용하여 동적 프로퍼티를 추가하는 예제입니다.

var person = new Object();
person.name = "Kendrick"; //객체에 name이라는 프로퍼티를 추가 합니다.
alert(person.name); // name이라는 프로퍼티가 생성되어 정상적으로 "Kendrick"이 출력됩니다.

위에서는 프로퍼티가 정상적으로 추가 되고, 조회도 잘 됩니다.
아래는 원시값을 사용하여 프로퍼티를 추가해보는 예제입니다.

var name = "Kendrick";   //문자열을 생성합니다. 문자열은 원시값입니다.
name.age = 27;     // 프로퍼티를 추가합니다.
alert(name.age)     //하지만 원시값은 프로퍼티를 추가하지 못하기 때문에 undefined가 출력됩니다.

위 예제와 같이 원시값은 동적 프로퍼티를 추가할 수 없습니다.
저~ 위에서도 설명했지만 원시값이란 Undefined, Null, 불리언, 숫자, 문자열을 말합니다.

2. 값 복사

원시값과 참조값은 값을 복사할 때도 다른 모습을 보입니다.

원시값은 다른 변수로 복사할 때 현재 저장된 값을 새로 생성한 다음 새로운 변수에 복사를 합니다.

var text1 = 'hello';
var text2 = text1;
text1 = 'world';
console.log(text2); // hello 출력

text2는 분명 text1을 바라보고 있을 텐데.. hello가 출력되네요
var text2 = text1 의 동작을 할때 text2가 text1을 바라보지 않고 원시값 자체를 복사 했기 때문입니다.

원시값 복사 참고 그림

참조값은 다릅니다. 참조값은 생성되면 Heap에 저장이 됩니다.
생성한 값은 (Heap에 저장되어 있는) 경로를 가지고 있습니다.
참조값을 다른 변수로 복사할 때 (Heap에 저장되어 있는)객체 자체가 Heap에 저장되어 잇는 곳에 대한 주소를 복사 합니다.
예제를 보겠습니다.

var textObj1 = new Object();
var textObj2 = textObj1;
textObj1['hello'] = 'world';
console.log(textObj2['hello']); //world 출력

원시값 예제와 동일하게 복사 하였습니다.
그 후 원본(textObj1)에 동적 프로퍼티를 생성 해줬습니다.
원시값과 달리 복사 할 때 값을 새로 생성하지 않고 Heap에 저장되어 있는 곳에 대한 주소를 복사 합니다. 그 때문에 textObj2는 textObj1과 동일한 경로를 바라보고 있습니다.
즉 textObj2는 textObj1에서 등록한 프로퍼티를 사용할 수 있게 되었습니다.

참조값 복사 참고 그림



3. 매개변수 전달

함수의 매개변수는 모두 값으로 전달됩니다.
이때 중요한 특징으로 매개변수의 값은 복사됩니다.
이런 특징은 2. 값 복사에서 다뤘던 부분하고 일치합니다.
원시값과 참조값이 매개변수 값으로 사용 되었을 때,
원시값의 경우 값이 복사 되어 들어갑니다. 하지만 참조값의 경우 heap에 저장된 경로가 복사되기 때문에 위에 값 복사 부분과 일치합니다

매개변수는 값으로 전달되며 이 값은 복사 된다는 것 꼭 기억합시다.

4. 타입 판별

자바스크립트는 동적 언어이기 때문에 변수에 대한 타입체크가 필요할 때가 있습니다.
하지만 원시 타입과 참조 타입별로 판별에 사용하는 연산자가 다릅니다.

원시값은 typeof 함수를 사용하면 알수 있으며
참조값은 instanceof 연산자를 사용하면 알 수 있습니다.

typeof는 원시값을 (string인지 number 등 인지)정확히 판별할 수 있지만, 참조 값에대해서는 별 쓸모가 없습니다.
typeof로 참조값을 판별하면 undefined가 떨어집니다

instanceof는 변수가 주어진 참조 타입의 인스턴스 일때 true를 반환합니다.
변수가 Object인지 Array인지 RegExp인지 확인 할 수 있지만 마찬가지로 참조값을 판별하면 false가 떨어집니다.

실행 컨텍스트와 스코프

실행 컨텍스트와 스코프에 대해서 알아보도록 하겠습니다.

실행 컨텍스트

컨텍스트라고도 부르며 중요한 개념입니다.

전역 컨텍스트와 함수 컨텍스트 두 가지 타입이 존재합니다.

함수 컨텍스트

함수를 호출하면 독자적인 실행 컨텍스트가 생성됩니다.
실행 컨텍스트는 변수객체가 연결되어 있습니다.
이 변수 객체는 현재 실행 컨텍스트의 정의된 모든 변수와 함수를 저장하고 있습니다.

function name(){
     var test_value = "helloworld"; 
}

위 name 함수의 실행 컨텍스트에는 변수 객체가 연결되어 있고, 이 변수 객체는 test_value변수를 저장하고 있습니다.

전역컨텍스트

가장 바깥쪽에 존재하는 실행 컨텍스트입니다. 이 또한 실행 컨텍스트 이며 가장 상위에 있기 때문에 전역 컨텍스트라고 부릅니다.
웹브라우저에서는 이 컨텍스트를 window라고 부릅니다.
실행 컨텍스트에 경우 보통 포함된 코드가 모두 실행될 때 파괴되는데, 전역 컨텍스트는 애플리케이션이 종료될 때, 예를들면 웹페이지에서 나가거나 브라우저를 닫을 때까지 계속 유지됩니다.

스코프

스코프 혹은 Variable Scope는 변수에 접근이 가능한 유효범위를 뜻합니다.
쉽게 말해 변수, 함수, 매개변수의 접근성 및 생존 기간을 말하는 개념입니다.
스코프는 두종류가 있으며 각각 글로벌 스코프와 로컬 스코프라고 합니다.
글로벌 스코프의 경우 전역 컨텍스트의 스코프, 로컬 스코프의 경우 함수 컨텍스트의 스코프라고 생각하면 이해하기 쉽다.

스코프 체인

스코프 체인의 목적은 실행 컨텍스트가 접근할 수 있는 모든 변수와 함수에 순서를 정하는 것입니다.
스코프 체인은 변수 객체를 가지고 있으며, 순서대로 가지고 있습니다.

현재컨텍스트의 활성화 객체 -> 부모 컨텍스트에 활성화 객체 -> 부모 컨텍스트에 활성화 객체 -> ... -> 전역 컨텍스트의 변수 객체

활성화 객체란 해당 컨텍스트에 존재하는 변수와 함수이며, 전역 컨텍스트는 활성화 객체를 가지고 있지 않기때문에 변수객체로 적혀있습니다.
위와같이 순서대로 가지고 있으며 끝에는 전역 컨텍스트의 변수객체를 가지고 있습니다.

아래 예제를 봅시다

var color = "blue";
function changeColor(){
    if (color === "blue"){
          color = "red";
    }else {
          color = "blue";
    }
}

changeColor함수 내에서 color식별자를 찾습니다.
changeColor 컨텍스트에 변수 객체에 해당 식별자가 없으니 다음 단계로 넘어갑니다.
바로 전역 컨텍스트가 나옵니다. 여기서 변수 객체에서 color 식별자를 찾을 수 있었고 이런식으로 식별자를 찾아갑니다
(끝끝내 식별자를 찾을 수 없다면 일반적으로 오류가 발생합니다.)

이런식으로 스코프 체인이 동작하기 때문에 자식이 부모 변수 객체의 존재하는 식별자를 사용할 수 있으나, 부모는 자식의 식별자에 접근할수가 없습니다.

참고로 함수 매개변수도 변수로 간주됩니다.

스코프 체인 확장

실행 컨텍스트에는 전역 컨텍스트와 함수 컨텍스트 두 가지 타입만 있습니다.
하지만 특정 문장으로 스코프 체인 앞 부분에 임시로 변수 객체를 만들 수 있는 방법이 있습니다.
이 해당 변수 객체는 코드 실행이 끝나면 사라집니다. 이렇게 사용되는 경우는 두 가지 입니다.

  • try-catch 문의 catch 블록
  • with 문

두 문장은 모두 스코프 체인앞에 변수 객체를 추가 합니다.

with문은 해당 객체가 스코프 체인에 추가됩니다.
catch문은 에러 객체를 선언하는 변수 객체가 생성됩니다.

function buildUrl() {
    var qs = "?debug=true";

    with(location){
          var url = href + qs;
    }

    return url;
}

기존 location.href 를 사용하기 위하여 스코프 체인에 location객체를 넣어 줬습니다.
즉 다시말해 with문 내의 href는 with문이 추가한 location.href 변수를 참조 하고 있는 것 입니다.
with문의 경우 객체를 사용자 임의로 추가 할수 있으며,
catch문은 기본적으로 에러 객체가 스코프체인에 추가되는 것 입니다.
이런 방식으로 스코프 체인을 확장 할 수 있습니다.

블록 스코프가 없다.

다른언어는 중괄호로 감싼 블록 코드마다 스코프(ECMAScript에서는 실행 컨텍스트라고 하는)가 생성되며 스코프를 나오면 제거됩니다.
하지만 자바스크립트는 변수를 선언할 때 해당 변수를 현재 실행 컨텍스트에 추가 합니다.
아래 예제를 봅시다.

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

위 예제에서 var i 변수를 생성 하였습니다.
i는 전역 컨텍스트 변수객체에 저장됩니다.
그런 이유로 다른언어라면 for문이 끝나고 i가 제거 되겠지만, 자바스크립트는 실행컨텍스트(위코드는 전역컨텍스트)에 변수를 저장하고 있기 때문에 for문이 끝나고 10이라고 출력이 됩니다.

변수 선언

변수 선언시 var를 사용할 경우 자동으로 가장 가까운 컨텍스트에 추가 됩니다.
함수 내부에서는 함수의 로컬 컨텍스트가 가장 가까운 컨텍스트로 지정됩니다.
하지만 var로 변수를 생성 하지 않고 변수를 초기화 하면 전역 컨텍스트(!!)에 추가 됩니다.

마치며

마지막으로 혼란스러울 수 도 있는 분에게 당부하자면 실행컨텍스트와 스코프는 비슷하지만 다른개념입니다.
머리를 식힐 겸, 다음 포스트에서는 가비지 컬렉션에 대해서 간단하게 알아보도록 하겠습니다.


이 포스트는 프론트엔드 개발자를 위한 자바스크립트(인사이트)에서 발췌한 내용이 포함되어 있습니다.
내용 전문이 아니기 때문에 자세하게 알고싶으신 분은 프론트엔드 개발자를 위한 자바스크립트(인사이트) 서적을 참고 하시길 바랍니다.