[BB-JS] 4. 변수와 스코프, 메모리

본 글은 자바스크립트 공부를 위해 프론트엔드 개발자를 위한 자바스크립트 프로그래밍를 정리한 글입니다.

들어가며

자바스크립트 변수는 느슨한 타입을 취하므로 변수는 특정 시간에서 특정 값을 가리크는, 문자 그대로 이름일 뿐이다. 변수가 가져야 할 데이터 타입에 대한 규칙이 없으므로 변수의 값과 데이터 타입은 스크립트 실행 중에 바뀔 수 있음

원시 값과 참조 값

  • ECMAScript의 변수는 원시 값과 참조 값 두 가지 타입의 데이터를 저장할 수 있음
    • 원시 값: 단순한 데이터(Undefined, Null, Boolean, Number, String)이며 고정된 크기를 가지고 스택 메모리에 저장
    • 참조 값: 여러 값으로 구성되는 객체(Object)이며 힙 메모리에 저장
  • 메모리 위치에 직접 접근하는 것을 허용하지 않으며, 객체의 메모리 공간을 직접 조작하는 일은 불가능
    • 객체를 조작할 떄는 해당 객체에 대한 참조를 조작
  • 동적 프로퍼티
    • 참조 값을 다룰 때는 언제든 프로퍼티와 메서드를 추가하거나 바꾸고 삭제할 수 있음
    • 프로퍼티를 할당한 뒤부터는 객체가 파괴되거나 프로퍼티를 명시적으로 제거하기 전까지는 접근할 수 있음
    • 원시 값에는 프로퍼티가 없지만 추가하려 해도 에러가 생기진 않으며 추가가 되지도 않음
    • 참조 값만이 동적으로 프로퍼티를 추가할 수 있음
  • 값 복사
    • 원시 값을 다른 변수로 복사할 때는 현재 저장된 값을 새로 생성한 다음 새로운 변수에 복사
    • 참조 값을 변수에서 다른 변수로 복사하면 원래 변수에 들어있던 값이 다른 변수에 복사
    • 참조 변수에 들어있던 값은 객체 자체가 아니라 힙에 저장된 객체를 가리키는 포인터
      • 힙은 애플리케이션이 운영체제로부터 미리 할당받은 메모리 영역(힙은 브라우저가 쓰는 메모리)
      • 애플리케이션이 실행되는 동안 메로리 요구량이 계속 바뀌는데 그 때마다 운영체제에 메모리르 요구하고 반환하길 반복한다면 상당한 성능 저하기 있으므로 일정량의 메모리를 미리 넘겨 받음
    • 참조 변수 복사가 끝나면 두 변수는 정확히 같은 객체를 가리킴
  • 매개변수 전달
    • 함수 매개변수는 모두 값으로 전달
    • 변수 사이에서 값을 복사하는 것과 마찬가지로 함수 외부에 있는 값은 함수 내부의 매개변수에 복사
    • 매개변수를 값 형태로 넘기면 해당 값은 지역 변수에 복사
    • 매개변수를 참조 형태로 넘기면 메모리 상의 값의 위치가 지역 변수에 저장되므로 지역 변수를 변경하면 함수 바깥에도 해당 변경 내용이 반영
    • 함수 내부에서 참조 형태로 넘긴 매개변수에 새로운 값을 할당하면 새로운 값을 바라봄
    • 함수 매개변수는 지역 변수와 동일하다고 생각하면 됨
  • 타입 판별
    • typeof 연산자를 통해 변수가 원시 타입인지 파악할 수 있음
    • String, Number, Boolean, undefined의 경우 정확한 타입을 알 수 있으나, Object이나 null이면 "object"를 반환
    • typeof 연산자는 참조 값에는 쓸모 없음
    • 값이 객체인지 아닌지 여부보다 어떤 타입의 객체인지가 필요하며, instanceof 연산자를 사용할 수 있음
    • instanceof 연산자는 주어진 변수가 참조 타입의 인스턴스인지 여부를 반환
    • 원시 값에 instanceof 연산자를 사용하면 false를 반환

실행 컨텍스트와 스코프

  • 실행 컨텍스트(execution context, EC)는 짧게 컨텍스트라고 부름
  • 변수나 함수의 실행 컨텍스트는 다른 데이터에 접근 할 수 있는지, 어떻게 행동하는지를 규정
  • 각 실행 컨텍스트에는 변수 객체(variable object, VC)가 연결되어 있으며 해당 컨텍스트에서 정의된 모든 변수와 함수는 이 객체에 존재
  • 변수 객체는 코드에서 접근할 수 없으며 이면에서 데이터를 다룰 때 이 객체를 이용
  • 가장 바깥쪽에 존재하는 실행 컨텍스트는 전역 컨텍스트이며, ECMAScript를 구현한 환경에 따라 부르는 이름이 다름
  • 실행 컨텍스트는 포함된 코드가 모두 실행되었을 때 파괴되며 내부에 정의된 변수와 함수도 함께 파괴
  • 전역 컨텍스트는 애플리케이션이 종료될 때 파괴
  • 함수를 호출하면 독자적인 실행 컨텍스트가 생성되며 코드 실행이 함수로 들어갈 때 마다 함수의 컨텍스트가 컨텍스트 스택에 쌓임
  • 컨텍스트에서 코드를 실행하면 객체 변수에 스코프 체인(scope chain)이 만들어짐
  • 스코프 체인의 목적은 실행 컨텍스트가 접근할 수 있는 모든 변수와 함수에 순서를 정의하는 것
  • 스코프 체인의 앞쪽은 항상 코드가 실행되는 컨텍스트의 변수 객체
  • 컨텍스트가 함수라면 활성화 객체(activation object)를 변수 객체로 사용하며 활성화 객체는 항상 arguments 변수 단 하나로 시작
    • arguments 변수는 전역 컨텍스트에는 존재하지 않음
  • 변수 객체의 다음 순서는 해당 컨텍스트를 포함하는 부모 컨텍스트이며 스코프 체인을 쫓아감
  • 전역 컨텍스트의 변수 객체는 항상 스코프 체인의 마지막에 존재
  • 내부 컨텍스트는 스코프 체인을 통해 외부 컨텍스트 전체에 접근할 수 있지만 외부 컨텍스트는 내부 컨텍스트에 대해 전혀 알 수 없음
  • 컨텍스트 사이의 연결은 선형이며 순서가 중요
  • 스코프 체인 확장
    • 특정 문장은 스코프 체인 앞 부분에 임시로 변수 객체를 생성하여 스코프 체인을 확장하며 해당 변수 객체는 코드 실행이 끝나면 사라짐
    • 임시 객체가 생성되는 경우
      • try-catch 문의 catch 블록
        • 에러 객체를 선언하는 변수 객체가 추가
      • with
        • 해당 객체가 스코프 체인에 추가
        • with 내부에 선언한 변수는 함수의 컨텍스트로 편입
    • 자바스크립트에는 블록 레벨 스코프가 없음
      • 변수를 선언할 때 해당 변수를 현재 실행 컨텍스트에 추가
      • 변수 선언
        • var를 사용해 선언한 변수는 자동으로 가장 가까운 컨텍스트에 추가
        • 함수 내부에서는 함수의 로컬 컨텍스트가 가장 가까운 컨텍스트이며 with 문 내부에서는 함수 컨텍스트가 가장 가까운 컨텍스트
        • 변수를 선언하지 않은 채 초기화 하면 해당 변수는 자동으로 전역 컨텍스트에 추가
        • 변수를 선언하지 않은 채 초기화하는 행동은 에러를 유발
        • 스트릭트 모드에서는 변수를 선언하지 않고 초기화하면 에러를 발생
      • 식별자 검색
        • 컨텍스트 내에서 식별자를 참조하려 하면 검색을 해야함
        • 검색은 스코프 체인 앞에서 시작하여 주어진 이름으로 식별자를 탐색
        • 스코프를 찾을 때까지 스코프 체인을 따라 검색(스코프 체인 내부에 프로토타입 체인있으면 프로토타입 체인을 따라서 검색)
        • 스코프 체인을 찾아 전역 컨텍스트의 변수 객체까지 도달하여도 식별자를 찾지 못하면 정의되지 않은 것으로 판단
        • 식별자가 로컬 컨텍스트에 정의되어 있으면 부모 컨텍스트에 같은 이름의 식별자가 있다고 해도 참조할 수 없음
        • 변수 검색에도 비용이 들어가며 지역 변수가 전역 변수보다 빨리 검색됨

가비지 컬렉션

  • 자바스크립트는 실행 환경에서 코드 실행 중에 메로리를 관리하므로 가비지 콜렉션 언어
  • 필요한 메모리를 자동으로 할당하고 더 이상 사용하지 않는 메모리는 자동으로 회수
  • 가비지 컬렉터는 어떤 변수가 더 이상 사용되지 않는지, 사용될 가능성이 있는 변수는 무엇인지 추적해야 메모리 회수 대상을 정할 수 있음
  • 더 이상 사용하지 않는 변수를 실별하는 기준은 브라우저마다 다름
  • 표시하고 지우기(mark-and-sweep)
    • 자바스크립트에서 가장 널리 쓰이는 가비지 컬렉션 방법
    • 변수가 특정 컨텍스트 안에서 사용할 것으로 정의되면(함수 안에서 변수를 정의하면) 그 변수는 그 컨텍스트 안에 있는 것으로 표시
    • 컨텍스트 안에 존재하는 변수의 메모리는 해당 컨텍스트가 실행 중인 한 사용될 가능성이 있기 때문에 해제해서는 안됨
    • 변수가 컨텍스트 밖으로 나가면 컨텍스트 밖에 있는 것으로 표시
    • 과정
      • 가비지 컬렉터가 작동하면 메모리에 저장된 변수 전체에 표시를 남김
      • 컨텍스트에 있는 변수와, 컨텍스트에 있는 변수가 참조하는 변수에서 표시를 지움
      • 위 과정을 거치고도 남아 있는 변수는 컨텍스트에 있는 변수와 무관하므로 해제해도 안전
      • 가비지 컬렉터는 표시가 남아 있는 값을 ‘메모리 청소’를 실행해 모두 파괴
    • 대부분의 브라우저가 사용
  • 참조 카운팅(reference counting)
    • 널리 사용되지 않음
    • 각 값이 얼마나 많은 참조가 되었는지 추적
    • 과정
      • 변수를 선언하고 참조 값이 할당되면 참조 카운트는 1
      • 다른 변수가 같은 값을 참조하면 참조 카운트가 늘어남
      • 참조하는 변수에 다른 값이 할당하면 참조 카운트 값이 줄어듬
      • 참조 카운트 값이 0인 값에서 사용하던 메모리를 회수
    • ‘순환 참조’라는 심각한 문제가 발견
    • 넷스케이프 3.0과 IE8 에서는 순환참조 문제가 있었음
  • 성능
    • 가비지 컬렉터는 주기적으로 실행되며 메모리 내에 할당된 변수가 많다면 상당한 비용이 드는 작업이므로 가비지 컬렉션을 싱행하는 타이밍이 중요
    • 초기 인터넷 익스프롤러에서는 가비지 컬렉터를 너무 자주 실행하여 성능 문제가 있었음
    • 일부 부라우저에서는 가비지 컬렉션을 수동을 실행할 수 있음
  • 메모리 관리
    • 일반적으로 가바지 컬렉션을 지원하는 프로그래밍 환경에서는 개발자가 메모리 관리를 신경쓰지 않아도 됨
    • 자바스크립트라는 환경에서 메모리 관리와 가비지 컬렉션은 다른 환경과 매우 다름
    • 웹 브라우저에서 사용할 수 있는 메모리는 일반적인 데스크톱 애플리케이션의 가용 메모리에 비해 매우 적음
    • 자바스크립트가 시스템 메모리를 전부 사용해서 운영체제를 다운시키는 일을 방지하기 위함
    • 메모리 제한은 변수 할당, 호출 스택, 스레드에서 실행할 수 있는 문장 수에 영향
    • 가능한 최소한의 메모리만 사용해야 페이지 성능을 올릴 수 있음
    • 메모리 사용을 최적화하기 위해서는 코드 실행에 필요한 데이터만 유지
    • 필요없어진 데이터에는 null을 할당하여 참조를 제거하는 편이 좋음
    • 전역 변수 및 전역 객체의 프로퍼티는 주로 수동으로 참조 제거해야 할 대상
    • 지역 변수는 컨텍스트를 빠져나가는 순간 자동으로 참조가 제거됨
    • 변수에서 참조를 제거한다고 해서 할당된 메모리가 자동으로 반환되는 것은 아님
    • 참조 제거의 요점은 값의 컨텍스트를 없애서 다음에 가비지 컬렉션이 실행될 때 해당 메모리를 회수하도록 하는 것

SmileCat

SmileCat
How do you define yourself?

[Openlayers] Render Event 정리

## 이벤트### [Map](http://openlayers.org/en/latest/apidoc/module-ol_Map-Map.html)| event | module | note || :--: | -- | -- || [postcompose](...… Continue reading