자바스크립트

Jihyo Jeon·2023년 1월 10일
0

soju-study

목록 보기
3/4
post-custom-banner

가비지 컬렉션

reachability를 이용한 메모리 관리 : 자바스크립트 엔진 내에선 가비지 컬렉터(garbage collector)가 끊임없이 모든 객체를 모니터링하고, 도달할 수 없는 객체는 삭제합니다.

내부 알고리즘

  1. mark-and-sweep
  • 가비지 컬렉터는 루트(root) 정보를 수집하고 이를 ‘mark(기억)’ 합니다.
  • 루트가 참조하고 있는 모든 객체를 방문하고 이것들을 ‘mark’ 합니다.
  • mark 된 모든 객체에 방문하고 그 객체들이 참조하는 객체도 mark 합니다. 한번 방문한 객체는 전부 mark 하기 때문에 같은 객체를 다시 방문하는 일은 없습니다.
  • 루트에서 도달 가능한 모든 객체를 방문할 때까지 위 과정을 반복합니다.
  • mark 되지 않은 모든 객체를 메모리에서 삭제합니다.
  1. 최적화 기법:
  • generational collection(세대별 수집) – 가비지 컬렉터는 일정 시간 이상 동안 살아남은 객체를 '오래된 객체’로 분류하고, 가비지 컬렉터가 덜 감시합니다.
  • incremental collection(점진적 수집) – 자바스크립트 엔진은 이런 현상을 개선하기 위해 가비지 컬렉션을 여러 부분으로 분리한 다음, 각 부분을 별도로 수행
  • idle-time collection(유휴 시간 수집) – CPU가 유휴 상태일 때에만 가비지 컬렉션을 실행

참고


클로저

클로저(Closure)는 외부 변수를 기억하고 이 외부 변수에 접근할 수 있는 함수, 자바스크립트에서는 모든 함수가 자연스럽게 클로저가 된다.

Lexical Environment

  • 변수
    • 자바스크립트에선 실행 중인 함수, 코드 블록 {...}, 스크립트 전체는 렉시컬 환경(Lexical Environment) 이라 불리는 내부 숨김 연관 객체(internal hidden associated object)를 갖습니다.
    • 환경 레코드(Environment Record): 모든 지역 변수를 프로퍼티로 저장하고 있는 객체입니다. this 값과 같은 기타 정보도 여기에 저장됩니다.
    • 외부 렉시컬 환경(Outer Lexical Environment) 에 대한 참조: 외부 코드와 연관됨

’변수’는 특수 내부 객체인 환경 레코드의 프로퍼티일 뿐입니다. '변수를 가져오거나 변경’하는 것은 '환경 레코드의 프로퍼티를 가져오거나 변경’함을 의미합니다.

  • 함수
    • 함수 선언문(function declaration)으로 선언한 함수는 일반 변수와는 달리 바로 초기화됨

New Function()

함수는 특별한 프로퍼티 [[Environment]]에 저장된 정보를 이용해 렉시컬 환경을 참조함. 그런데!!! new Function을 이용해 함수를 만들면 함수의 [[Environment]] 프로퍼티가 현재 렉시컬 환경이 아닌 전역 렉시컬 환경을 참조하게 됩니다.


Decorator

: 인수로 받은 함수의 행동을 변경시켜주는 함수를 Decorator라고 한다.

function slow(x) {
  // CPU 집약적인 작업이 여기에 올 수 있습니다.
  alert(`slow(${x})을/를 호출함`);
  return x;
}

function cachingDecorator(func) {
  let cache = new Map();

  return function(x) {
    if (cache.has(x)) {    // cache에 해당 키가 있으면
      return cache.get(x); // 대응하는 값을 cache에서 읽어옵니다.
    }

    let result = func(x);  // 그렇지 않은 경우엔 func를 호출하고,

    cache.set(x, result);  // 그 결과를 캐싱(저장)합니다.
    return result;
  };
}

slow = cachingDecorator(slow);

alert( slow(1) ); // slow(1)이 저장되었습니다.
alert( "다시 호출: " + slow(1) ); // 동일한 결과

alert( slow(2) ); // slow(2)가 저장되었습니다.
alert( "다시 호출: " + slow(2) ); // 윗줄과 동일한 결과

객체에서 사용할 경우 래퍼가 기존 함수 func(x)를 호출하면 this가 undefined가 되기 때문에 에러가 발생 👇

// worker.slow에 캐싱 기능을 추가해봅시다.
let worker = {
  someMethod() {
    return 1;
  },

  slow(x) {
    // CPU 집약적인 작업이라 가정
    alert(`slow(${x})을/를 호출함`);
    return x * this.someMethod();
  }
};

// 이전과 동일한 코드
function cachingDecorator(func) {
  let cache = new Map();
  return function(x) {
    if (cache.has(x)) {
      return cache.get(x);
    }
    let result = func(x);
    cache.set(x, result);
    return result;
  };
}

alert( worker.slow(1) ); // 기존 메서드는 잘 동작합니다.

worker.slow = cachingDecorator(worker.slow); // 캐싱 데코레이터 적용

alert( worker.slow(2) ); // 에러 발생!, Error: Cannot read property 'someMethod' of undefined

해결방법
func.call(context, ...args): this를 명시적으로 고정해 함수를 호출할 수 있게 해주는 특별한 내장 함수 메서드

let worker = {
  someMethod() {
    return 1;
  },

  slow(x) {
    alert(`slow(${x})을/를 호출함`);
    return x * this.someMethod();
  }
};

function cachingDecorator(func) {
  let cache = new Map();
  return function(x) {
    if (cache.has(x)) {
      return cache.get(x);
    }
    let result = func.call(this, x); // 이젠 'this'가 제대로 전달됩니다.
    cache.set(x, result);
    return result;
  };
}

worker.slow = cachingDecorator(worker.slow); // 캐싱 데코레이터 적용

alert( worker.slow(2) ); // 제대로 동작합니다.
alert( worker.slow(2) ); // 제대로 동작합니다. 다만, 원본 함수가 호출되지 않고 캐시 된 값이 출력됩니다.
  1. 데코레이터를 적용한 후에 worker.slow는 래퍼 function (x) { ... }가 됩니다.
  2. worker.slow(2)를 실행하면 래퍼는 2를 인수로 받고, this=worker가 됩니다(점 앞의 객체).
  3. 결과가 캐시되지 않은 상황이라면 func.call(this, x)에서 현재 this (=worker)와 인수(=2)를 원본 메서드에 전달합니다.

여기서 func.call(this, ...arguments) 대신, func.apply(this, arguments)를 사용할 수도 있음.

func.call(context, ...args); // 전개 문법을 사용해 인수가 담긴 배열을 전달하는 것과
func.apply(context, args);   // call을 사용하는 것은 동일합니다.
profile
Software Engineer in London, UK
post-custom-banner

0개의 댓글