티스토리 뷰

이번포스팅은 지난번 객체 포스팅에 대한 보충설명 격이다.

지난번 포스팅이 생각보다 너무 길어져서 따로 빼내 작성하는게 좋을것같아 이렇게 쓰게됐다.

사실 별 생각없이 넘어가면 머리아프게 생각할필요 없는 부분이기도한데 호기심이 왕성한 분들은 매우 궁금해할수도 있는부분이다.

우리는 함수의 3가지 역할에 대해 공부를 했다. 그중 1가지는 값으로서 전달되는 '변수'의 역할이니 이번포스팅에선 따로 다루지않는다.

그럼 나머지 2개가 무엇인가?

일반 메서드로서의 함수와 생성자로서의 함수가 바로 오늘 좀더 깊게 알아볼 것 들이다.

지난번에 배운 생성자 함수를 봐보자

 

function Cons(nick, job){

this.nick = nick;

this.job  = job;

}

 

var obj = new Cons("LichKing", "programmer");

 

자바에서의 생성자와 크게 다른부분도 없고 사용법도 클래스가 없을뿐 생소하지않는 부분이다. 그럼 여기서 이렇게 호출을 하게되면 어떻게 될까?

 

var obj = Cons("LichKing", "programmer");

 

뭔가 올바른 사용법이 아닌것같다. 하지만 그렇다고 저게 에러가 날것같지도않다. 생성자로 사용하는것과 메서드로 사용하는것은 사용자의 마음이지 사실 함수 정의차원에서 얘는 생성자로만, 얘는 메서드로만 사용하라고 강제하는것은 없다. 첫글자 대문자? 그냥 코딩규칙일 뿐이다.

실행시켜보면 알겠지만 에러는 발생하지않는다. 썡쌩 잘 돌아간다. 그리고 obj를 출력해보면 undefined가 나오는것을 알수있다.

그럼 그상태 그대로 그냥 전역에서 nick을 호출해보자. 이런식이다.

 

function Cons(nick, job){

this.nick = nick;

this.job = job;

}

 

var obj1 = new Cons("LichKing", "programmer");

var obj2 = Cons("LichKing", "programmer");

 

console.log(obj2); // undefined

console.log(nick);

 

"LichKing"이 나오는가?

분명 전역 변수는 선언한 적이 없다. 그렇다고 함수 내부에서 var 없이 선언한 변수도 없다. 그럼 이게 어찌된일일까?

이것에 대해 이해하려면 일단 new 연산자가 하는일을 알아야한다.

new 연산자는 빈객체 생성과 함수호출, 객체반환을 일으키는 연산자다. 잠깐 짧게 짚고 넘어간다면 new 연산자가 '함수호출'도 일으키기때문에 생성자 함수를 이런식으로 작성해도 실행된다.

 

function Cons(){

this.nick = "LichKing";

this.job = "programmer";

}

 

var obj = new Cons;

 

우리가 여태 배운 지식을 근거로 생각해볼때 함수 호출 연산자인 ()가 없기때문에 Cons함수는 생성자고 메서드고 나눌거 없이 함수의 참조값을 담은 변수의 역할을 해야된다. 하지만 new 연산자가 앞에있을경우 ()연산자는 상관없이 new가 함수를 호출시키므로 저 함수는 정상적으로 객체를 생성하게된다.

다만 인자를 전달하기위해선 꼭 ()가 있어야 하므로 그냥 이렇게 쓸수도있다는것만 알고 넘어가자.

 

다시한번 말하지만 new 연산자는 객체생성->함수호출->객체반환을 일으킨다. 객체 생성 후 함수호출을 일으킨다는것은 다시 말하면 Cons() 함수를 이용해 객체를 생성할때 nick과 job 속성을 갖고있는 객체를 생성하는게 아니라 일단 빈객체를 생성하고 거기에 속성들을 덧붙인다는 말이다. 소스로 표현하자면 다음과 같다.

 

function Cons(){

this.nick = "LichKing";

this.job = "programmer";

}

 

var obj = new Cons();

 

이 구문에서 new로 인해 Cons함수가 이렇게 변한다고 보면 된다.

 

function Cons(){

var temp = {};

obj.nick = "LichKing";

obj.job = "programmer";

 

return temp;

}

 

var obj = Cons();

 

사실 이렇게 설명하는것 보다는 apply나 call함수를 이용해 설명하는게 좀 더 직관적이고 실제 작동원리에도 더 가까울거라고 보는데 call과 apply 함수는 추후 this에 대해 설명할때 설명하려고 계획중이다. 이미 call과 apply를 알고있다면 이 소스를 이해할수 있을것이다.

 

var temp = {};

var obj = Cons.apply(temp);

 

생성자 함수에는 기본적으로 return문이 존재하지않지만 위에 말한대로 new 연산자가 함수반환을 일으키기때문에 obj라는 변수에 생성된 객체가 들어가게된다.

결국 new 연산자의 핵심을 간추려본다면 객체 생성후 함수 내부의 this에 생성한 객체를 대입하여 멤버들을 추가하고 그 객체를 반환 하는것이라 볼수있다.

 

그럼 원점으로 돌아가보자. 왜 new가 없었을땐 뜬금없이 전역변수가 생겨난걸까?

그 답을 알고싶다면 다음함수를 실행시켜보자

 

function test(){

return this;

}

 

console.log(test());

 

test() 를 실행하게되면 이는 우리가 여태 알고있는 메서드로의 함수역할을 하게된다.

함수내부에서 this가 무엇을 참조하고있는지 확인해보자

콘솔에는 window 객체가 찍힐것이다. 그럼 이 소스를 다시한번 보자.

 

function Cons(nick, job){

this.nick = nick;

this.job = job;

}

 

 

var obj2 = Cons("LichKing", "programmer");

 

console.log(obj2);

console.log(nick);

 

new를 이용하게되면 빈객체를 생성한 후 this에 대입시킨다. 그럼 new 없이 메서드로서 사용하면? 위위 예제를 통해 new가 없을때의 this는 window객체를 가리키고 있다는것을 알게되었다. 저 함수에서 nick과 job이라는 속성을 추가하고 있는 객체는 window 객체였던 것이다.

자바스크립트에서는 모든것이 함수와 객체라고 표현을 한적이있다.

좀 어려운 개념인데 짧게 설명하고 가자면(이것도 포스팅으로 설명하면 1개 이상의 분량을 요구할듯..) 우리가 전역이라고 부르는것도 사실은 window 객체내부이자 전역함수(실행컨텍스트)내부인것이다. 그렇기에 전역변수들은 사실상 window객체가 관리하게된다.

 

var a = "a";

this.b = "b";

window.c = "c";

 

console.log(a);

console.log(b);

console.log(c);

 

전역에서는 모두 의미가 같다.

다만 파싱에서 배웠듯이 var 변수로 선언한 부분만 파싱때 최상단으로 끌어올려져 undefined로 정의되는 수순을 밟고 나머지는 파싱때는 끌어올려지지않고 런타임에 정의가 되게된다.

 

이 얘기는 이쯤 하고 다시 new로 돌아가자.

이제 new가 있고 없고가 좀 이해가 되는가?

this라는 키워드가 자바개발자들에게 반가운 감정을 줬을지 모르겠지만 사실 자바의 this와 자바스크립트의 this는 같은듯 다르다.

가만 생각해보면 클래스가 없기때문에 다른게 당연할수도있다고 생각이 들지만 자바의 this는 정적이고, 변수명이 겹치는 상황이 아니라면 생략도 가능한데 반해 자바스크립트의 this는 동적으로 변한다. 이때는 이 객체를 가리켰다가 저때는 저 객체를 가리킨다. 그리고 생략도 하면 안된다.

 

function Cons1(nick, job){

this.nick = nick;

this.job = job;

}

 

function Cons2(nick, job){

nick = nick;

job = job;

}

var obj1 = new Cons1("LichKing", "programmer");

var obj2 = new Cons2("LichKing", "programmer");

 

이 둘은 완전히 다른 결과를 가져온다.

this에 대해서는 추후에 자세히 다루겠다. 지금은 자바의 this와 연계해서 생각하는건 금물이라는것만 알아두자.

 

위 예제가 이해된다면 이건 어떻게 실행될까?

 

function plus(num1, num2){

var num = num1 + num2;

 

return num;

}

 

var variable1 = new plus(1, 2);

var variable2 = plus(1, 2);

 

console.log(variable1);

console.log(variable2);

 

new없이 호출한건 여태 써오던대로 그냥 받아들이면 된다. 함수는 1+2인 3을 변수에 담아 그 변수를 반환할것이다. 그럼 new는? 차근차근 위에서 공부한대로 생각해보자.

일단 빈객체를 생성할것이다. 빈객체를 생성하고, 함수를 호출해서 함수 내부 this에 방금 생성한 객체를 대입할것이다. 그런데 plus 함수에는 this가 없다. 그래서 빈객체에는 아무런 속성이 추가되지않고 빈객체가 반환되게 될것이다.

이 추리가 맞을지는 직접 확인해보자.

 

여기서 한가지 재미있는 부분이있다. 이것만 설명하고 오늘의 포스팅을 마치겠다.

 

function plus(num1, num2){

var num = num1 + num2;

return num;

}

 

이 함수를 new 연산자로 실행하면 빈객체를 반환할 것이라고 했었다. 즉 return num; 이부분을 빈객체를 반환하는 return문으로 덮어쓴다고 보면 이해가 쉬울것이다.

다만 지금같은경우는(return num;) 반환하는 타입이 참조타입이 아니라 기본타입이다. 이처럼 기본타입을 반환하는 함수는 new연산자를 사용했을때 new연산자로 인한 객체를 반환하도록 덮어쓰는반면 참조타입을 반환하는 함수는 return문을 덮어쓰지못한다. 예를들어 이 소스를 보자.

 

function plus(num1, num2){

var num = num1 + num2;

this.nick = "LichKing";

 

return {

nick : "LichKing",

job : "programmer"

}

}

 

var obj = new plus(1, 2);

 

함수실행문에는 num에 파라미터들을 더하고있는데 리턴은 뜬금없이 객체 리터럴로 생성된 객체를 반환하고있다.

return위쪽으로는 우리가 알고있는대로 실행된다. 빈 객체를 생성하고 this에 빈객체를 대입한다. 그럼 빈객체를 생성하고 그 객체에 nick이라는 속성이 추가된 객체가 있을것이다.

그런데 여기서 return문을 덮어쓰질 못한다. new를 쓰나 안쓰나 똑같이 객체 리터럴로 생성된 객체가 반환되게 된다.

그럼 new를 사용해서 생성된 nick속성을 갖고있는 plus객체는 없어진걸까?

 

function plus(num1, num2){

var num = num1 + num2;

this.nick = "LichKing";

plusObj = this;

 

return {

nick : "LichKing",

job : "programmer"

}

}

 

var obj = new plus(1, 2);

console.log(plusObj.nick);

 

new로 인해 빈객체가 생성되고 함수를 호출한다. 함수내부 this키워드에 new로 인해 생성된 객체를 대입한다. var로 선언된 num은 우리가 알고있는대로 함수레벨의 파싱과 런타임 과정을 거친다. this.nick. new로 인해 생성된 객체에 nick 속성을 추가한다.

그럼 var 도 없고 this도 없는 plusObj는???

var 없이 선언된 변수는 런타임에 전역변수로 선언된다는걸 이미 배웠으니 당황할 필요없다.

plubObj는 함수의 런타임에 전역변수로 선언되며 new 연산자로 생성한 객체를 저장하게된다. return은 객체 리터럴을 반환하고있기때문에 obj는 new 연산자가 생성한 객체를 저장하지않는다. 하지만 전역으로 정의된 plusObj는 new 연산자가 실행되면서 생성된 객체를 담고있기때문에 리턴문은 쌩뚱맞은걸 하더라도 new로 인해 생성된 객체는 분명히 존재한다는걸 확인할 수 있다.

일단 생성은 될것이고 아마 저런식으로 붙잡고있지않으면 나중에 CG에 의해 수거될 운명의 객체일것이다.

 

나도 태생이 자바개발자이고 포스팅 대상도 나와 같은 기존 자바개발자들이다보니 자바얘기가 심심찮게 나오게되는데 사실 자바스크립트를 공부하면서 가장 뼈저리게 느꼈던건 자바와 비슷한 문법들은 많지만 절대 자바와 비교하면서 공부하면 안된다는 것이다.

'자바는 이런데 얘는 왜이렇지?' 애초에 자바와 자바스크립트는 다른 언어인데 자바가 이렇다고 얘도 똑같아야된다고 생각하는것 자체가 무리였던 것이다.

비슷한 문법, 비슷한 명칭에 반가워는 하되 반가움에서 끝내야지 비교하기 시작하면 머리만 더 아파진다.

그런점에서 어찌보면 개발공부를 처음하는 분들이 더 유리한 측면이 있을수도 있지않을까 생각한다.

'Java Script & HTML' 카테고리의 다른 글

#08. JavaScript의 클로저(closure)  (0) 2015.10.02
#07. JavaScript의 변수스코프  (0) 2015.09.22
#06. JavaScript의 new 와 생성자함수  (0) 2015.08.31
#05. JavaScript의 객체  (2) 2015.08.30
#04. JavaScript의 함수  (0) 2015.08.28
#03. JavaScript의 연산자  (0) 2015.08.27
댓글
댓글쓰기 폼