티스토리 뷰

자바스크립트는 크게 함수와 객체로 구성되어있다고 보면 된다. 사실 그게 전부다.

그런데 함수와 객체로 완벽히 나뉘어져 있는게 아니고 또 함수는 객체고 객체는 함수다. 이때부터 머리가 좀 복잡해진다.

일단 함수부터 보자. 우리가 아무런 생각없이 함수를 정의하게되면 함수의 메모리 모델링은 이런식으로 이루어진다.

 

코드실행영역, 지역변수영역, 함수객체영역, 프로토타입영역

 

일단 여기에 나오는 이 용어들은 공식적인건 아니고 그냥 내가 갖다쓰는것이므로 이 용어들까지 알필요는 없다.

코드실행영역과 지역변수영역은 여태 우리가 생각없이 쓰던 그대로 이해하면 된다. 코드실행영역은 변수 선언이 아닌 일반적 코드가 실행되는 부분이고 지역변수영역은 말그대로 지역변수들이 저장되는 영역이다. 그럼 함수객체영역은? 일단 함수객체라는 말도 잘 이해가 안될것이다. 자바스럽게 생각해보자. '객체'라는것은 생성자를 사용해 생성된 객체를 뜻한다. 이를 기반으로 생각해보면 함수객체라는것은 어떠한 생성자를 매개로 함수를 생성한다는것을 뜻하는 것일것이다. 그렇다면 그 생성자는 무엇인가?

 

우리가 아무생각없이, 태초부터 그랬다는듯이 작성하는 function test(){} 는 사실 이 문구 자체가 객체를 생성하고있는것이다. function 은 함수객체를 생성하는 리터럴표현인것이다. 마치 new Object() 로 생성하지않고 {}로 생성이 가능하듯이 말이다. 모든 함수들은 생성자를 갖고있으며 모든 함수의 생성자는 Function 생성자이다. 이것이 생성자라면 new Function()으로 객체생성이 가능해야하는게 아닌가? 맞다. 가능하다.

 

var test = new Function("a", "b", "if(a == 1){ return 'hello' } return a + b");

 

console.log(test(1, 5));

console.log(test(2, 5));

 

음...말도 안되는 함수인건 알지만 그냥 단순 예제니 한번 돌려보자. 잘 실행되는걸 볼수있을것이다. 앞 인자들은 아규먼트를, 가장 마지막 인사는 실행구문이다.

이런식으로 함수도 생성자를 통해 생성할 수 있다. 하지만 이 글을 보는분들이 여태 저런식으로 함수를 선언한 적이 없듯, 저런식의 생성은 거의 할일이 없다.

함수도 생성자를 매개로 생성되는 객체라는 것만 알아두자.

객체 포스팅에서 객체는 동적으로 속성추가가 가능하고 그 방법은 [] 혹은 도트(.) 연산자를 사용한다는것을 언급한적이 있다. 함수가 객체라는건 함수도 이런식의 사용이 가능하다는 것이다.

 

function test(){}

 

test.a = "hello";

test['b'] = "world";

 

참고로 test 함수객체에 저런식으로 동적속성 추가와 함수내부에서 var a; 이런식으로 선언하는건 메모리영역이 완전히 다르다. 후자는 지역변수 영역에 생성되기때문에 서로 전혀 연관이 없고 이름이 중복되도 상관없다. 간혹 함수내부에서 선언한 변수를 함수명.속성명 으로 쓰려고 시도하는분들을 본적이 있기때문에 한번 강조하고 넘어간다.

 

이게 위에서 말한 4가지 영역중 함수객체영역이다. 그럼 마지막 프로토타입 영역을 살펴보자.

우리가 아무생각없이 함수를 생성하게되면 자바스크립트 엔진은 아무도 모르게 그에 딸린 객체를 하나씩 생성하게된다. 이건 무조건적인 행위이기때문에 객체생성을 못하게 할수도없고 아무것도 모른체 사용하고있던 현재 소스의 함수들도 모두 객체를 하나씩 갖고있는 중이다.

그 객체가 바로 오늘 포스팅의 주인공인 프로토타입이다.

 

function test(){}

alert(test.prototype);

 

위에서 봤듯 우리가 prototype이란 속성을 추가한적이 없기때문에 undefined가 나와야하겠지만 결과는 그렇지않은걸 확인할 수 있다.

이 prototype 속성은 자바스크립트 엔진이 함수를 생성할때 무조건 적으로 생성하는 객체이며 여타 함수객체영역에 있는 속성과는 달리 delete 연산자로 삭제할 수가 없다.

*삭제할 순 없지만 여타 속성처럼 동적으로 변경할 순 있다. 그말은 곧 null을 집어넣을 수 있다는 뜻.

 

prototype은 빈객체다. {}라는 뜻이다. 사용방법도 똑같다. 함수명.prototype.속성 으로 여타 객체 쓰듯 사용하면 된다. 그렇다면 이 prototype은 왜 자동적으로 지울수도 없게 생성되는 것일까?

 

함수를 일반 메서드용으로만 쓴사람들은 아마 자신이 함수를 하나 생성할때마다 prototype 이라는 명칭의 객체가 생성되는 것을 몰라도 문제 없이 잘 사용해오고 있을것이다. 그렇다면 얘도 위에서 언급한 Function 처럼 그냥 이런애가 있다는것만 알면되는 애일까? 그렇지않다.

 

자바개발자가 아니라면 오히려 더 이해하기힘들겠지만 자바얘기를 먼저 조금 해보려한다. 자바에서 인스턴스별로 다른값이 아닌, 하나의 클래스를 생성자로 활용해 생성된 모든 객체들이 같은 값을 공유하려면 static 키워드를 써야한다. 이 static 키워드를 자바스크립트는 prototype을 이용해 지원하고있다.

 

function Const(_a, _b){

  this.a = _a;

  this.b = _b;

 

  this.setA = function(_a){

    this.a = _a;

  }

 

  this.getA = function(){

    return this.a;

  }

}

 

a와 b는 인스턴스별로 값이 달라질수있으나 setA()와 getA() 는 사실 인스턴스별로 달라질 내용이 없다. 그렇지만 Const()를 사용해 new 한 모든 객체들은 각자가 각자만의 영역에 setA(), getA()를 생성할 것 이다. 이걸 static으로 바꿔보자

 

function Const(_a, _b){

  this.a = _a;

  this.b = _b;

}

 

Const.prototype.setA = function(_a){

  this.a = _a;

}

 

Const.prototype.getA = function(){

  return this.a;

}

 

var obj1 = new Const(10, 50);

var obj2 = new Const(100, 500);

 

Const()로 생성되는 모든 객체들은 각자의 인스턴스영역에 a와 b의 공간만을 생성하고 setA()와 getA()는 프로토타입영역을 참조하게된다.

OS에서 한번 메모리를 할당받은 브라우저에서 또 메모리를 할당받아 구동되는 자바스크립트의 특성상 메모리가 매우 부족한 환경이며 그 메모리부족을 해결하기위해 이런 기법이 도입된것이다.

 

한가지 재미있는 점을 보면 위에서 생성한 obj1도 객체이기때문에 동적 속성할당이 가능하다. 아래소스를 실행해보자

 

console.log(obj1.getA);

console.log(obj2.getA);

obj1.getA = "hello world";

console.log(obj1.getA);

console.log(obj2.getA);

 

중간에 동적으로 getA의 값을 바꿨다. getA는 모든 객체들이 공유하는 자원이니 그 값을 보호하기위해 값이 안바뀌거나 혹은 다 바뀌어야하는게 상식적인 일일것이다. 하지만 실제 출력내용은 어떠한가? 그리고 이건 어떻게 된일일까? 객체에서 속성을 불러올때(read), 호출을 하는경우 자바스크립트엔진은 해당 객체에서 속성을 찾는다. 그리고 해당 속성이 없을경우 해당 객체가 참조하고있는 프로토타입을 거슬러 올라가 해당 속성을 찾는다. 뒷부분에 포스팅하겠지만 프로토타입은 3, 4단계 이상이 있을수있으므로 프로토타입의 끝까지 올라가 속성을 불러온다. 이거 왠지 익숙하지않은가? 중첩 함수에서 해당 함수에 변수가 없으면 상위 함수로 올라가는 변수스코프체인과 비슷하다. 때문에 용어도 프로토타입체인이라고 한다. 이건 공식적인 용어이므로 알아두는게 좋다.

속성을 호출할땐 이런 매커니즘을 갖게되는데 속성을 정의, 수정(write)할때는 다르다. 해당 객체에 속성이있는지만 찾아본 후 없을경우 프로토타입 체인이 실행되는게 아니라 해당객체에 인스턴스속성으로 추가하게된다. 위 소스에서 두번째 obj1.getA는 프로토타입체인에 의한 탐색이 아니라 자기자신의 속성으로 추가된 getA를 호출하는 것이다.

사실 같은 속성을 읽을때와 쓸때의 매커니즘이 다르다는것이 좀 이해가 안가거나 복잡하고 어렵게 느껴질수도있는데 이런식으로 굴러가는게 현실이다. 그냥 '아 이렇구나' 하는수밖에 없다.

댓글
댓글쓰기 폼