지난 편에 이어 여전히 변수에 대한 이야기를 하겠다.

자바스크립트를 코딩하는 것은 자원이 작은 환경(embeded)의 에서 코딩하는 것과 유사하다.

브라우저는 C/C++ 처럼 빠르지 않을 뿐더러 자바스크립트 특성상 잘 못 쓰면 한없이 느려지기 때문이다.

자원을 작게 쓰면서도 성능을 높일 수 있는 방법을 찾아야한다.

이번에는 Object, Array, Function 에 대한 이야기를 해보자.

객체는 참조를 가진다.

자바스크립트에서는 기본적으로 모든 자료형이 객체이다. 다만 변할 수 있는 것과 변할 수 없는 것들이 있는데.

이번에는 값이 변할 수 있는 것들에 대한 이야기를 하고자 한다.

Object, Array, Function 은 모두 참조를 가진다.

var o =  { a : 10 } ;
var a = [ 10 ];
var f = function () { };

참조를 가진다는 것은 언제든지 값이 변할 수 있다는 의미가 된다.

그래서 생각하는 것보다 잘 정의하고 써야한다.

매번 생성되는 참조는 GC 의 대상이 될 수도 있고 잘 못 하면 계속 증가하는 메모리 릭(leak)의 원인이 될 수 있다.

참조는 언제나 GC 의 대상이 될 수 있다.

사용하지 않는 참조가 있다면 언제나 GC 의 대상이 된다. GC 가 된다는 소리는 그 시간 동안 아무런 동작을 하지 않고 GC 만 수행하게 된다.

브라우저에서 어떠한 이벤트를 실행할때 화면이 끊기거나 버벅인다면 나도 모르는 타이밍에 GC 가 계속 일어나는 것일 수 있다.

왜냐하면 GC 가 일어나는 동안 브라우저는 실행을 멈추기 때문이다.

그래서 되도록이면 GC 가 안 일어나게 하던가 아니면 작게 일어나게 해야한다.

그럴려면 객체 생성(참조를 가지는)을 주의깊게 관찰해야한다.

내가 생성한 객체가 계속 살아있을지 아님 없어져도 되는 것인지 명확히 파악해야한다.

객체의 용도를 확인하자.

계속 사용될 객체인지, 아님 실행 중간에 없어져야할 객체인지, 또는 연산에만 사용될 객체인지

잘 구별할 필요가 있다.

생성된 객체는 잘 관리해야할 필요가 있는 것이다.


// 한번에 많은 루프를 도는 함수가 있다. 
function BigLoop(max) {
  var arr = [];
  for(var i = 0; i < max; i++) {
    arr[i] = i * 2; 
  }
  return arr;
}

function getMaxValue(arr) {
  var maxValue = arr[0];
  for(var i = 1 , len = arr.length; i < len; i++) {
    if (maxValue < arr[i]) maxValue = arr[i];
  }
  return maxValue;
}

var bigList = BigLoop(100);       // 100 개 배열 리턴 
var bigList2 = BigLoop(10000);    // 10000개 배열 리턴 
var bigList3 = BigLoop(1000000);  // 1000000개 배열 리턴 

var max1 = getMaxValue(bigList);  // max 값을 구해보자. 
var max2 = getMaxValue(bigList2);
var max3 = getMaxValue(bigList3);

큰 배열을 만들고 그 안에서 어떠한 결과값을 구하는 함수가 있을 때 이후 배열은 어떻게 될까?

더 이상 쓰지 않는 배열이기 때문에 gc 의 대상이 될 수도 있고 bigList 나 배열 변수가 global 로 선언이 되어 있다고 친다면 사라지지 않고 계속 메모리에 남아있을 수 있다.

사실 배열은 연산을 위해서 가지고 있던 데이타라 실제 연산이 끝나면 필요없어지는 것이기 때문에 이런 경우는 연산을 위한 배열을 하나만 만들어두는 것도 괜찮다.

코드를 조금 바꿔보자.

var ArrMaxLength = 1000000;
var bigArr = new Array(ArrMaxLength);

// 한번에 많은 루프를 도는 함수가 있다. maxCount 까지만 초기화 한다. 
function BigLoop(maxCount) {
  for(var i = 0; i < maxCount; i++) {
    bigArr[i] = i * 2; 
  }

  return bigArr;
}

// maxCount 까지의 요소를 가지고 max 를 구한다. 
function getMaxValue(arr, maxCount) {
  var maxValue = arr[0];
  for(var i = 1; i < maxCount; i++) {
    if (maxValue < arr[i]) maxValue = arr[i];
  }
  return maxValue;
}

var max1 = getMaxValue(BigLoop(100), 100);  // max 값을 구해보자. 
var max2 = getMaxValue(BigLoop(10000), 10000);
var max3 = getMaxValue(BigLoop(1000000), 1000000);

자, 여기는 어떠한 현상이 벌어졌을까?

  • Array 객체 생성을 한번만 한다.
  • BigLoop 가 몇번이 실행되어도 상관이 없다.

다만 Array 의 전체 개수를 알 수 없기 때문에 어느정도 경험에 기반한 코딩이 필요한다.

여기서 보듯이 객체의 용도가 명확한 경우 Resource 의 부하를 많이 줄일 수 있다.

객체 생성은 결코 싸지 않다.

함수 생성도 최소화 하자.

자바스크립트에서는 함수도 객체다. 그 말은 함수를 정의하는 것만으로도 객체처럼 동작한다는 것이다.

자바스크립트에서는 함수도 변수에 할당해서 사용할 수 있다. 선언은 익명함수로 하던 어떤것으로 하던 상관 없다.


function A() { } 

var A = function () { }

둘은 모두 함수이다.

함수를 정의하는 것도 객체를 생성하는 것과 같기 때문에 당연히 비용이 든다.


var self = this; 

// setTimeout 을 통해 지속적으로 paint 함수를 호출한다. 
var self = this; 
this.paint = function () {
 setTimeout(function() {
    self.paint();
  }, 1000/60);
};

this.paint();

위의 코드는 정상적으로 동작할 것이다. 다만 몇가지 우려스러운 부분이 존재한다.

paint() 함수를 실행할때마다 setTimeout 을 지속적으로 호출하게 되는데 이때 함수를 계속 정의하고 있다는 것이다.

정의된 함수는 한번만 실행되고 버려진다. (즉 1회성 함수)

비슷한 로직에 대해서는 함수를 계속 정의하는 것은 함수를 객체를 계속 생성하는 것이기 때문에 이것도 성능에 안좋은 영향을 미칠 수 있다.

아래와 같이 고쳐보자.

var callback = (function(self) { 
  return function () {
    self.paint();
  };
})(this);

// requestAnimationFrame 을 통해 지속적으로 paint 함수를 호출한다. 
this.paint = function () {
 setTimeout(callback, 1000/60);
};

this.paint();

this 의 context 를 유지하는 callback 이라는 함수를 한번만 생성한다. 이후에 setTimeout 이 실행되더라도 callback 에 대한 정의를 하지 않는다.

animation 이나 기타 이벤트로 움직이는 형태의 코딩을 해야한다면 주의깊게 봐야할 부분이다.

매번 생성하는 것과 한번만 생성하는 것은 큰 차이가 난다.

results matching ""

    No results matching ""