ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 4장) 변수와 스코프, 메모리
    Java Script/프론트앤드 개발자를 위한 자바스크립트 2020. 6. 26. 21:51
    반응형

    변수와 스코프, 메모리

    JS는 느슨한 변수 타입 ~> 변수는 특정 시간 특정 값을 가리키는 문자 그대로 이름일 뿐

    • 원시 값과 참조 값

      • 원시 값 : 단순 데이터 (스택 메모리에 저장)
      • 참조 값 : 여러 값으로 구성되는 객체를 가리킴(힙 메모리에 저장)
      • 변수에 값을 할당 ~> JS 엔진이 원시 데이턴지 참조 데이턴지 판단
      • JS는 메모리 위치에 직접 접근하는 것을 허용 X ~> 객체를 조작할 때는 객체 자체가 아닌 객체에 대한 참조를 조작하는 것
    • 동적 프로퍼티

      • 참조 값을 다룰 때는 언제든 프로퍼티와 메서드를 추가/삭제 가능

        var person = new Object();
        person.name = "Zin0";
        alert(person.name);    // Zin0
        
        var name = "Zin0";
        name.age = 28;
        alert(name.age);    // undefined

        원시 값에는 프로퍼티가 없지만 추가하려해도 에러 X.
        다만, 동적으로 프로퍼티를 추가할 수 있는 값은 참조 값 뿐이다.

    • 값 복사

      • 원시 값을 다른 변수로 복사할 때는 저장된 값을 새로 생성해서 새로운 변수에 복사
        var num1 = 5;
        var num2 = num1;
        위의 두 변수는 5라는 값을 각각 갖는다. (다른 메모리 위치)
      • 참조 값을 복사할 때는, Heap 공간에 저장된 객체를 가리키는 포인터 값을 복사해줌.
        var obj1 = new Object();
        var obj2 = obj1;
        obj1.name = "Zin0";
        alert(obj2.name);    // Zin0    
        같은 객체를 가리키기 때문에, obj2에서 해당 프로퍼티에 접근하면, Zin0 값을 가져온다.
    • 매개변수 전달

      • ECMAScript의 함수 매개변수는 모두 값으로 전달

      • 매개변수의 값은 지역 변수에 복사된다. ~> 이름 붙은 매개변수로 복사되면서, ECMAScript에서는 arguments 객체의 한 자리를 차지한다. 원시 값과 참조 값에 따라 값을 복사함.

        function addTen(num) {
            num +=10;
            return num;
        }
        
        var count = 20;
        var result = addTen(count);
        alert(count); // 20
        alert(result); // 30
        function setName(obj) {
            obj.name = "Zin0"
        }
        var person = new Object();
        setName(person);
        alert(person.name); // Zin0
        function setName(obj) {
            obj.name = "Zin0";
            obj = new Object();
            obj.name = "JY";
        }
        var person = new Object();
        setname(person);
        alert(person.name);    // Zin0    
      • 매개 변수가 원시 값일 경우에는 값을 복사하여 지역변수에 저장하고 return함으로 변수값에 변화가 없음.

      • 참조 값의 경우 매개변수가 참조 형태로 전달된 것이 아니라, Heap 공간에 저장된 객체를 가리키는 포인터 값을 복사해 준 것이다. ECMAScript의 함수 매개변수는 지역 변수와 다를 것 없다고 생각하면 편하다.

    • 타입 판별

      • typeof 연산자는 변수가 원시 타입인지 파악하기 최상.
        • 값이 객체거나 null이면 "object"를 반환
        • 함수에 사용하면, "function"을 반환
      • instanceof 연산자는 주어진 참조 타입의 인스턴스인지 파악
        alert(person instanceof Object); // person 변수가 Object의 인스턴스인가?
        alert(colors instanceof Array); // colors 변수가 Array의 인스턴스인가?
        alert(pattern instanceof RegExp); // pattern 변수가 RegExp의 인스턴스인가?
        • 원시 값은 Object의 인스턴스가 아니기 때문에 항상 false 값을 리턴
    • 실행 컨텍스트와 스코프

      • 실행 컨텍스트(Execution Context) - '컨텍스트'라고 부름.

      • 실행 컨텍스트는 다른 데이터에 접근할 수 있는지, 어떻게 행동하는지를 규정한다.

      • 변수 객체가 연결되어 있으며, 해당 컨텍스트에서 정의된 모든 변수와 함수는 이 객체에 존재.

      • 코드에서 직접 접근할 수는 없지만, 이면에서 데이터를 다룰 때 이 객체를 이용

      • 전역 컨텍스트 - 가장 바깥쪽에 존재하는 실행 컨텍스트

        • ECMAScript 구현 황경에 따라 이름이 달라진다.
        • 웹 브라우저에서는 window라 부르고, 이 window 객체에 전역 변수와 함수가 모두 window 객체의 프로퍼티 및 메서드로 생성됨.
        • 실행 컨텍스트는 포함된 코드가 모두 실행될 때 파괴 (Life Cycle)
          • 내부에서 정의된 변수와 함수도 함께 파괴됨
          • 전역 컨텍스트는 애플리케이션이 종료될 때 파괴된다.
      • 함수 호출 ~> 독자적인 실행 컨텍스트 생성 ~> 스택에 쌓임.

        • 함수 실행이 끝나면 컨텍스트를 스택에서 꺼내고 컨트롤을 이전 컨텍스트에 반환
      • 컨텍스트에서 코드를 실행하면, 변수 객체에 스코프 체인이 만들어 진다.

        • 실행 컨텍스트가 접근할 수 있는 모든 변수와 함수에 순서를 정의

        • 스코프 체인의 앞쪽은 항상 코드가 실행되는 컨텍스트의 변수 객체

        • 컨텍스트가 함수라면 활성화 객체를 변수 객체로 사용

        • 활성화 객체는 변수 하나로 시작(전역 컨텍스트에 존재X)

          • 전역 컨텍스트에 도달할 때 까지, 하위 컨텍스트부터 부모 컨텍스트로 계속 이동
        • 식별자를 찾을 때는 스코프 체인을 따라가면서 식별자 이름 검색
          ~> 찾을 수 없을 때는 에러 발생
          var color = "blue";

          function changeColor() {

            var anotherColor = "red";
          
            function swapColors() {
                var tempColor = anotherColor;
                anotherColor = color;
                color = tempColor;
                // color, anotherColor, tempColor 모두 접근 가능
            }
            // color, anotherColor 접근 가능, tempColor는 불가능
            swapColors();

          }
          // color만 접근 가능
          changeColor();

      • 내부 컨텍스트는 스코프 체인을 통해 외부 컨텍스트 전체에 접근 가능하지만, 외부 컨텍스트는 내부 컨텍스트에 대해 전혀 알 수 없다.

    • 스코프 체인 확장

      • 실행 컨텍스트에는 전역 컨텍스트와 함수 컨텍스트 두 타입이 존재(eval() 호출 시, 생성되는 세 번째 타입이 있기는 하다.)

      • 스코프 체인을 확장하는 방법

        • 스코프 체인 앞 부분에 임시로 변수 객체를 만들며, 코드 실행 후에 사라진다.

          • try-catch 문의 catch 블록 - 에러 객체를 선언하는 변수 객체가 생성

          • with 문 - 객체가 스코프 체인에 추가
            function buildUrl() {

            var qs = "?debug=true";
            
            with(location) {
                var url = href + qs;
            }
            return url;

            }
            ▲ with 문이 location 객체에 적용됨 ~> location 객체가 스코프 체인에 추가됨.

        • href 변수는 location.href를 참조, qs는 buildUrl()함수 컨텍스트의 변수 객체에 들어있다. 따라서 with문 내부에서 선언한 변수 url은 함수 컨텍스트로 편입 ~> 함수 값으로 반환 가능

    • JS에는 블록 레벨 스코프가 없다

      if(true) {
          var color = "blue";
      }
      alert(color); // blue
      
      for(var i=0; i<10; i++) {
          doSth(1);
      }
      alert(i);    //10
      • JS에서는 변수를 선언할 때 현재 실행 컨텍스트에 추가한다. 따라서 블록 레벨 안의 변수를 이용 가능

      • 변수 선언

        • var를 사용해 선언한 변수는 자동으로 가장 가까운 컨텍스트에 추가된다.
          • 함수 내부 -> 함수 로컬 컨텍스트
          • with문 내부 -> 함수 컨텍스트
            function add(num1, num2) {
            var sum = num1 + num2;
            return sum;
            }
            var result = add(10,20); //30
            alert(sum); // error
      • 식별자 검색

        • 컨텍스트 안에서 식별자 참조 -> 스코프 체인 앞에서부터 식별자를 탐색

        • 식별자 이름을 찾으면 검색 멈추고 변수를 설정

        • 전역 컨텍스트의 변수 객체에서도 못찾으면, 정의되지 않을 것으로 판단.
          var color = "blue";
          function getColor() {

            return color;

          }
          alert(getColor()); // blue

          function getColor2() {

            val color = "red";
            return color;

          }
          alert(getColor()); // red


    • 가비지 콜렉션
      • 코드 실행 중에 메모리를 관리함 ~> 필요한 메모리 자동 할당, 더 이상 사용 X 메모리는 자동 회수.
      • 코드 실행 중에 특정 시점에서 메모리를 회수하도록 지정할 수 있음
      • 더이상 사용하지 않는 변수를 식별하는 기준은 두 가지 방법을 흔히 사용한다.
    • 1 - 표시하고 지우기
      • JS에서 가장 널리 쓰이는 가비지 컬렉션 방법
      • 변수가 특정 컨텍스트 안에서 사용할 것으로 정의 ~> 그 컨텍스트 안에 있는 것으로 표시
        • 컨텍스트 안에 존재하는 변수의 메모리는 해제하면 안된다. 실행 중에 사용될 가능성이 있기 때문에. 변수가 컨텍스트 밖으로 나가면 컨텍스트 밖에 있는 것으로 표시
      • 가비지 컬렉터가 작동하면 메모리에 저장된 변수 전체에 표시를 남긴다.
      • 그 다음 컨텍스트에 있는 변수와, 컨텍스트에 있는 변수가 참조하는 변수에서 표시를 지운다.
      • 위의 두 과정을 거친 다음에도 표시가 남아있는 변수는 삭제해도 안전함.
    • 2 - 참조 카운팅
      • 널리 쓰이지는 않는다.
      • 각 값이 얼마나 많이 참조됐는지 추적
      • 변수 선언하고 참조 값이 할당되면 참조 카운트는 1
        • 다른 변수가 같은 값을 참조하면 카운트 증가
        • 해당 값을 참조하는 변수에 다른 값 할당 ~> 원래 값의 참조 카운트가 줄어든다.
        • 참조 카운트가 0이 되면 해당 값에 접근할 방법 X, 메모리 회수해도 안전
      • 순환 참조
        • 객체 A와 B가 서로를 참조하는 경우
        • 표시하고 지우는 방식은 함수 실행이 끝날 때, 두 변수가 모두 스코프를 벗어남으로 문제X
        • 참조 카운팅은 서로 참조 카운트 2를 가지기 때문에, 함수 실행 이후에도 두 변수 존재.
        • IE 8을 포함한 이전 버전은 BOM과 DOM 객체들이 c++의 COM(구성 요소 객체 모델)로 구현됨. COM 객체는 가비지 콜렉션에 참조 카운팅 방식 이용. ~> 순환 참조 문제
        • IE 9는 BOM과 DOM 객체를 JS 객체로 만들어서 해결
    • 성능
      • 가비지 컬렉션을 실행하는 타이밍이 중요함 (IE가 가비지 컬렉터를 너무 자주 실행해서 성능 문제를 일으킨다..)
    • 메모리 관리
      • 가비지 컬렉션을 지원하는 프로그래밍 환경에서는 메모리 관리에 신경쓰지 않아도 된다.
      • 하지만, JS라는 환경에서 메모리 관리와 가비지 콜렉션은 다름.
      • 웹브라우저에서 사용할 수 있는 메모리는 일반적인 데스크톱 애플리케이션 가용 메모리에 비해 매우 적기 때문. ~> JS가 시스템 메모리를 전부 사용해서 OS 다운되는 현상 방지 위해
      • 따라서 필요없어진 데이터는 null을 할당하여 참조를 제거하는 편이 좋다.
        • 주로 전역 변수 및 전역 객체의 프로퍼티에 적용
        • 지역 변수는 컨텍스트를 빠져나가는 순간 자동으로 참조 제거
    반응형

    댓글

Designed by Tistory.