jQuery Plugin - JS OOP 2편, Self-Invocating function

한머리 2011.10.14 15:22 Views : 32368 Upvote:1

 

들어가기전

 

글을 쓰면서 항상 두려운 것이 있어 먼저 밝히고 들어 갑니다.

우선 전 전산분야의 비전공자이고 전산 분야에 제대로된 교육을 별도로 받은 적이 없습니다.

 

아~ 물론 실무를 바탕으로 제가 전산관련 학원강의를 한 경험은 있습니다.

 

하지만 알고 있는 것과 책을 바탕으로 길을 안내해드리는 일과 이렇게 어떤 내용에 관해서 글로 남기며 이 길과 정의가 맞다고 이야기 해나가는 약간 다른 의미가 있는 것 같습니다.

 

그래서 비전공자이면서 동시에 제대로된 이론 교육없이 작성을 하다보니 개념이나 용어가 다소간 원래의 의미에 맞지 않거나 통용되는 정의와 전혀 다른 혼자만의 정의였을 가능성을 배제할 수 없습니다.

그런 부분이 있다면 너그러이 이해해주시고 그냥 지나치지 마시고 지적해주셨으면 합니다.

 

 

 

자 본론으로 들어가겠습니다.

 

이전 강좌에서 다루었던 object의 instance를 생성하고 이를 확장하는 방식으로 OOP하는 것으로 왠만하면 충분하고 그런대로 문제가 없다고 했습니다.

그리고 실제로 jQuery plugin을 만들 때 대부분 이를 응용합니다.

 

 

하지만 그냥 "대부분의 경우에 충분하고 그런대도 괜찮다"는 딱 거기 까지 한계입니다.

(후후 이전 강좌를 배신하는 이 한마디...)

 

 

결국 Javascript라는 Script언어로 모든 쓰임새를 충족하고 쉽게 만족하는 OOP를 하기에는 어느 정도 Javascript script언어자체에서부터의 한계점이 있을 것 같아보입니다.

 

자.. 그리고 이전 강좌에서 다룬 Object instance를 확장하는 어설픔 방법(와~~ 완전 배신이다.)으로는 쉽게 생각해도 개체형식의 상속 등이 그 태생때문에서 부터 어려워 보입니다.

 

 

결국은 이번에 다루는 function으로 class를 선언하듯 하고 이를 new 키워드를 통해서 object형으로 instance화 해서 사용하는 방법이 중요하다는 이야기를 하고 싶은 것 입니다.

 

 

function 사용한 class 생성

기본적인 class 정의 방법와 그 정의를 확장하고 사용하는 방법까지를 간단한 예제로 살펴보도록 하겠습니다.

<script language="javascript" type="text/javascript">
//함수를 정의하는 것이 곧 class를 정의하는 행위가 되는 겁니다.
function MyCompany() {
//함수 안에서 this 키워드로 속성을 작성합니다.
this.CompanyName = "My Company";
//함수 안에서 this 키워드를 이용하여 익명함수를 할당함으로써 메서드를 작성합니다.
this.AlertCompanyName = function () {
alert(this.CompanyName);
}
}
//class 정의 밖에서 해당 형식이 object형을 상속 받아서 만들어지는데 object의 밑의 prototype을 이용하여 속성 만들기
MyCompany.prototype.Owner = "Me";
//class 정의 외부에서 익명함수를 할당함으로써 메서드 구현하기
MyCompany.prototype.AlertOwner = function () {
alert(this.Owner);
}
//위에 정의된 MyCompany형의 instance를 생성합니다.
var _company = new MyCompany();
//속성을 사용한다.
alert(_company.CompanyName);
//메서드를 호출한다.
_company.AlertCompanyName();
_company.AlertOwner();
</script>

헉~~ 함수를 new를 통해서 개체로 instance화 되어 지고 있습니다.

그럼 이 new라는 키워드가 함수를 개체로써 instance화 시킬 능력이 있다는 이야기 입니다.

아래 예제를 통해서 new 키워드의 동작을 흉내내어 자세히 살펴보도록 하겠습니다.

<script language="javascript" type="text/javascript">
function MyCompany() {
this.CompanyName = "My Company";
this.AlertCompanyName = function () {
alert(this.CompanyName);
}
}
//new 키워드가 무엇을 하는지 살펴보기 위해서 new MyCompany를 분해하여 다음과 같이 코딩합니다.
var _company = new Object(); // 또는 var _company = {};
MyCompany.call(_company);

alert(_company.CompanyName);
_company.AlertCompanyName();
</script>

위의 예제를 통해서 금방 눈에 들어오는 이야기이지만 결국 new 키워드는 object 만들고 class 생성하고자는 함수 이름의 [function name].call 매서드를 통하여 해당 function을 생성자로 빈 object를 채워주는 것 입니다.

과연 그럼 new 가 빈 object에 call메서드를 동작한 걸까요?

아님을 증명하는 예제를 살펴보도록 하겠습니다.


<script language="javascript" type="text/javascript">
function MyCompany() {
this.CompanyName = "My Company";
this.AlertCompanyName = function () {
alert(this.CompanyName);
}
}
MyCompany.prototype.Owner = "Me";
MyCompany.prototype.AlertOwner = function () {
alert(this.Owner);
}
//new 키워드로 생성한 개체
var __company = new MyCompany;
alert(__company.CompanyName);
__company.AlertCompanyName();
__company.AlertOwner();
//new 키워드를 흉내낸 방법
var _company = new Object(); // 또는 var _company = {};
MyCompany.call(_company);
alert(_company.CompanyName);
_company.AlertCompanyName();
//여기서 에러 나죠.
_company.AlertOwner();
</script>

자 위의 예제를 통해서 보면 알 수 있듯 new 키워드는 function 생성자를 통해 정의된 것과 prototype을 통해서 확장된 것까지를 완전히 instance화하는 것입니다.

하지만 흉내낸 방법은 단지 빈 object에 생성자 function안 있는 내용을 instance화 해서 넣어주는 것 입니다.

하여튼 위의 예제들을 통해서 알리고자는 것은 function을 new함으로써 기본이 object형을 상속받아서 function의 내용을 instance화 하여 사용할 수 있다는 것입니다.

그럼 function을 통해서 instance화 시킬 수 있는 class를 정의할 수 있지 않을까요?

(스스로가 의구심을 가지고 대답하시기 바랍니다.)

 

상속

개체정의의 형의 속성, 메서드를 그대로 가지고 다른 형을 다시 정의할때 이를 상속이라고 합니다.

VS의 intellisense에서 볼 수 있는 개체의 namespace의 상하 관계와는 아무런 관련이 없습니다.


<script language="javascript" type="text/javascript">
function Company() {
this.name = "My Company";
this.AlertName = function () {
alert(this.name);
}
}

//Company를 상속 받고 새로운 생성자를 지정함으로써 Department 형을 만듭니다.
Department.prototype = new Company();
Department.constructor = Department;

function Department() {
this.name = "Dept Name";
}
//company 자신의 method가 실행됩니다.
(new Company).AlertName();
// company로 부터 상속 받은 method를 실행합니다.
(new Department).AlertName();
</script>

 

상속이라는 단어를 사용하고 있지만 실상은 function object의 prototype이라는 속성에 상속할 object의 instance를 생성해 넣어 주고 새로운 constructor로 새로운 function object를 지정해주는 겁니다.

 

this 의미

this는 해당 instance를 function형 그 자체를 의미합니다.


<script language="javascript" type="text/javascript">
function Company() {
this.name = "My Company";
this.AlertName = AlertName;
}
function Employee() {
this.name = "Person Name";
this.AlertName = AlertName;
}

function AlertName() {
alert(this.name);
}

(new Company).AlertName();
(new Employee).AlertName();
</script>

많은 사람들은 소유자(owner)를 의미한다는 표현을 많이 사용합니다.

위의 표현으로 정확한 의미를 이해 하려고 하면 형은 상속관계를 가지고 있는 것을 상기해야 합니다.

많은 분들이 표현하는 owner라고 하는 것은 최하위 형을 의미하지만 최하위에서 최상위까지 계속 찾아간다는 걸 잘 이해하지 않으면 굉장히 어려운 상황에 직면하는 경우가 종종 생깁니다.


<script language="javascript" type="text/javascript">
function Company() {
this.CompanyName = "My Company";
this.AlertCompanyName = function () {
alert(this.name);
}
}

//Company를 상속 받고 새로운 생성자를 지정함으로써 Department 형을 만듭니다.
Department.prototype = new Company();
Department.constructor = Department;

function Department() {
this.DepartmentName = "Dept Name";
this.AlertDepartmentName = function () {
// 소유자인 현재의 department에는 companyname이 없습니다.
// 그러면 상속해준 개체로 찾아 간다. 계속 찾아가다 보면 object()형 까지 올라가게 되는 것 입니다.
alert(this.CompanyName + ' > ' + this.DepartmentName);
}
}
(new Department).AlertDepartmentName();
</script>

 

전역 variant의 지옥이라는 표현이 적절하리라고 생각하는 this의 지옥이라 표현이 있을 만큼 어려운 상황을 만드는 것은 전역 변수를 남용하게 되면 생기게 되는 것 입니다.

여기서 말하는 global이라는 표현보다는 page level이라는 용어가 더 어울리지 않을까 합니다.

그리고 몇가지 오해는 상속관계 등에 관한 정확한 이해가 없어서 발생하기도 합니다.


결론적으로 정확한 개념을 가지고 this를 사용해야 하고 global영역의 변수들은 왠만하면 솔루션을 만들거나 협업으로 많은 사람이 개입되어 있는 페이지를 작업할 때는 사용하지 않는 편이 좋습니다.

 

사용은 못하게 하고 글로벌 영역에서 바로 실행되어야 할 코딩에 관한 대안이 없다면 당연히 그건 무책인 한 것 입니다.

그건 욕하는 아이에게 욕하면 안된다고 하고 그 욕들 대신에 할말을 가르치지 않는 것과 같은 것 같습니다.

 

그 대안은 self-invocating function에서 찾도록 하고 그건 좀 있다 다시 설명하도록 하겠습니다.


prototype 이해

쉽게 말하면 이것 저것 붙여서 맘대로 할 수 있는 레고같은 모형장난감에 비유하면 될 것입니다.

하지만 여기서 분명히 기억하여야 할 것은 function 정의를 확장한다는 것입니다.

위의 비용는 사실 형상적으로 나타나는 것을 쉽게 이해하기 위한 용도로 비유하여 설명한 것이고 좀 더 본연의 모습을 깊이 살펴보도록 합니다.

이전에 prototype은 object의 function 생성자 이외에서 정의를 확장하기 위한 속성이라고 했습니다.

prototpe은 object형의 prototype을 상속받은 겁니다.

여기서 상속이라는 말을 사용하고 있긴 하지만 좀더 정확히 이야기 하면 상속자를 instance화하여 피상속자의 prototype이라는 속성에 넣고 따로이 생성자를 지정하는 방법을 사용했었습니다.

결국 prototype으로 지정된 속성은 해당 object의 소유가 아니라 이전 상속자의 instance가 소유한 것 입니다.

이런 내용을 hasownproperty라는 method를 통해서 확인할 수 있는 예제를 살펴보겠습니다


<script language="javascript" type="text/javascript">
function vitamin() {
this.name = "비타민 이름";
}
vitamin.prototype.ingredient = "C";

var vitamin1 = new vitamin();
alert(vitamin1.hasOwnProperty("name")); //true
alert(vitamin1.hasOwnProperty("ingredient")); //false
</script>

참 그리고 object가 instance화 되고 나면 prototype은 사용할 수 없는 것입니다. (확신이 없지만 본 적도 없고 상식적으로 아닐 것 같습니다.)

여기서 당연하고 흥미로운 사실 두가지를 알아보도록 하겠습니다.


<script language="javascript" type="text/javascript">
function vitamin() {
this.name = "비타민 이름";
}
vitamin.prototype.ingredient = "C";

var vitamin1 = new vitamin();

//가격은 정의되기 이전이므로 당연히 여기까지는 undefined입니다.
alert(vitamin1.price); //undefined

// 이미 instance화 되어 있다고 해도 해당 원래 형의 정의에 prototype에 새로 추가를 하면
// 그 이미 그 형으로 instance화 되었던 것 까지 모두 해당 속성이 새로 추가되어져 있음을 확인할 수 있습니다.
// 왜냐하면 형의 정의에 해당 하는 function에 prototype이라는 속성 안에 이미 instance화되어 있는 prototype을 참조하고 있기 때문입니다.
vitamin.prototype.price = 20000;
alert(vitamin1.price); // 20000

</script>


<script language="javascript" type="text/javascript">
function vitamin() {
this.name = "비타민 이름";
}
vitamin.prototype.ingredient = "C";

var vitamin1 = new vitamin();
//prototype에 의해 상속 받아진 것이므로 당연 해당 object가 가진 속성이 아닙니다.
alert(vitamin1.hasOwnProperty("ingredient")); //false

// 해당 속성의 값을 바꿉니다.
vitamin1.ingredient = "mineral";

// 속성의 값을 바꿀려고 했을 뿐인데 ??
// 이전 까지의 강좌를 들으셨으면 감이 오실 겁니다. object는 사전이다. 해당 key가 없으니깐 새로 해당 key로 값을 할당하는 겁니다.
// 그런 과정때문에 새로운 속성이 생긴겁니다.
// 물론 prototype의 속성은 그대로 "C"가 들어 있을 겁니다. 아마도 ㅎ
alert(vitamin1.hasOwnProperty("ingredient")); //true

</script>



이형체

사실 jQuery plugin을 공부하면서 이형체에 관해서 언급할 개연성은 없어보인다 하지만 여기까지 왔으니 간단하게 한번 살펴보고 가도록 하겠습니다.

아래와 같은 살짝 억지스런 예제를 살펴보도록 합니다.


<script language="javascript" type="text/javascript">
// 다른 언어에서 하듯이 class를 선언하듯 하고 상속하듯 합니다.
function DrawingObject() {
this.Draw = function () {
alert('generic drawing');
}
}

Circle.prototype = new DrawingObject();
Circle.constructor = Circle;
function Circle() {
this.Draw = function () {
alert('circle');
}
}

Square.prototype = new DrawingObject();
Square.constructor = Square;
function Square() {
this.Draw = function () {
alert('Square');
}
}

//사용을 하려니 모든 형은 var 결국 object입니다. 그래서 변수를 데이터형을 지정해서 선언할 수 있는 다른 언어라면 아래와 같이 이형체의 예제를 만들 수 있겠지만
//아쉽게도 javascript는 var이라는 키워드로 형을 별도로 지정해서 선언 할 수 없습니다.ㅋ
/*
DrawingObject[] _drawing = new DrawingObject[2]
_drawing[0] = new Circle();
_drawing[1] = new Square();

_drawing[0].Draw();
_drawing[1].Draw();
*/
// 이전 강좌를 통해서 말씀 드린 적인 있는데 object는 dictionary입니다. 그걸 응용해보겠습니다.
var _drawings = new DrawingObject();
_drawings[0] = new Circle();
_drawings[1] = new Square();
_drawings[0].Draw();
_drawings[1].Draw();
</script>

다음으로 이형체에서 언급하는 게 맞는 건지 아니면 별도로 뭔가 타이틀을 잡고 해야 할지 확신이 없지만 javascript는 같은 개체 안에서 다른 메서드로 구현하는 건 불가능합니다.

하지만 함수의 parameters의 형이 어자피 object 동일하고 function 호출시 몇 개를 제외하고 해도 그 값이 null인 형태로 function호출이 가능하므로 그냥 하나의 함수에서 여러 개의 내용을 모두 담거나 그 안에서 받는 서로 다른 private한 function으로 분기하는 편이 나은 방법입니다.


<script language="javascript" type="text/javascript">
function 캐릭터() {
// 받고자하는 매개변수를 왕창 다 몰아서 하나의 함수안에서 분기 합니다.
this.외치기 = function (외칠말, 외칠횟수) {
if (외칠횟수 == null)
alert(외칠말);
else
alert(외칠말 + ' * ' + 외칠횟수);
}
}
var 나의캐릭 = new 캐릭터();
//사용할 때는 마치 이형체 처럼..
나의캐릭.외치기("야호~~");
나의캐릭.외치기("멍~", 2);
</script>


필자가 억지로 JS OOP라고 해서 가만히 있는 javascript language 억지로 OOP이라는 틀에 맞추려고 경향이 있긴 합니다.

이 모든 것이 제가 부족한 탓에 단순히 있는 그대로를 나열하면 어려워서 개인적으로 난잡한 개념을 추상화하는 가운데 생긴 부산물일 수 있습니다.

이를 너그러이 살펴 주시옵고 조금이라도 이상하면 지적질~~~ 부탁드립니다.

감사합니다.

휴~ 사실은 위까지만 쓰고 다음 번에 다시 작성하려고 했는 기왕 내친 김에 다음 강좌와 내용을 겹쳐서 바로 갑니다.

Javascript Self-Invating Function

self-invocating 또는 self-executing function은 말 그대로 혼자서 실행되는 함수 또는 class 라고 보시면 됩니다.

복잡한 설명 먼저 드리는 것보다는 아래와 같은 예제를 살펴보는 편이 나을 것 같습니다.

<script language="javascript" type="text/javascript">
// 자가 실행 함수 (self-invocation)
(
function () {
var str = "연습";
alert(str);
}
)();
</script>

이게 저게 뭐야 말이 어렵지 뭐할려고 저런 걸 만들었을까? 라는 생각이 절로 드는 pattern일 수 있습니다.

그래서 아래와 같이 복잡하게 많은 js 파일들로 구성된 예제를 살펴 보겠습니다.

<script language="javascript" type="text/javascript">
//각각의 js에 모두 흩어져 있는 코드들이라고 상상해보세요.

//이것은 dummycompanyutil200901.js
var str = "엄청중요한값임 흔한 변수명에다 담아버림";

//이것은 dummycompanyutil201109.js
//이 회사는 뭔 util.js가 매달 별로 만들까요? 아니면 프로젝트별, 개인별 어디서 많이 보는 저희들의 모습이죠.
//그런데 이런 흔한 이름으로 변수명을 써버리면 그것도 전역으로 당연 겹쳐쓰시는 분들이 생겨날것 입니다.
var str = "진짜 중요한것";

//이것은 mycompany.js
//그리하여 별개의 class 안에서 독립된 작업을 하고 이를 self-invocation
(
function () {
var str = "연습";
alert(str);
}
)();

//여긴 test.html
// dummycompany.js들에 의해서 결과는 뭐가 나올지 어디서 그렇게 된 건지 찾기가 참 난감해진다.
alert(str);
</script>


바로 그것 입니다.

function이 내에서 선언되어서 사용되는 변수는 외부와 독립적이라는 것 입니다.

다시 말해 외부적인 환경에 신경쓰지 않고 편하게 변수명와 함수명을 사용하면 가독성 높은 프로그래밍이 가능하다 것 입니다.

위의 예제는 변수이지만 그것보다도 내부에서 함수를 사용하다면

<script language="javascript" type="text/javascript">
//이제는 function의 예를 들어 보자

//이것은 dummycompanyutil200901.js
function test() {
alert("연습");
}

//이것은 dummycompanyutil201109.js
function test() {
alert("가나다라마바사");
}

//이것은 mycompany.js
//그리하여 별개의 class 안에서 독립된 작업을 하고 이를 self-invocation
(
function () {
function test()
{
alert("동해물과백두산이마르고");
}
test();
}
)();

test();
</script>


오~~ 여기까지만 보면 마치 self-executing function은 마치 그 안이 별천지 같이 독립된 공간처럼 보입니다.

하지만 그 환상을 깨야 합니다. 아래의 예제를 보겠습니다.


<script language="javascript" type="text/javascript">
var str = "문자열";

(
//이미 설명 드린봐와 같이 self-invocating function 안쪽 영역은 외부와 관련 없는 공간 입니다.
function () {
var str = "여긴 다른 세계";
alert(str);
}
)();

(
//하지만 외부의 값을 읽을 수 있죠.
function () {
alert(str);
}
)();

(
//class 영역 안에서 선언하지 않고 사용하게 되면 그 class 속한 상위 영역에서 찾게 되고 바로 함수 밖의 변수를 그대로 사용하게 되는 것 입니다.
function () {
str = "이러면 어떻게 될까요?";
alert(str);
}
)();
alert(str);
</script>



위의 예제로 느끼셨겠지만 딱 혼자서 자동으로 실행하는 class 딱 거기까지가 self-invocating function의 능력입니다.

 

마치며

급 마무리 하겠습니다.

편집을 마무리하며 차분히 읽어보니 처음 보시는 분들께는 다소간 어려울 것 같은 생각이 많이 듭니다.

짧은 시간에 준비하며 욕심을 좀 부리다가보니 그렇게 되었습니다.

너그러운 이해 부탁드리겠습니다.

facebook like 도 좀 해주시고 반응도 괜찮고 하면 원하시는 부분 짚어서 다시 한번 상세히 다루는 기회를 마련해보도록 하겠습니다. 하하 (추천 구걸 이건 나쁜 건데.ㅠ)

다음 강좌는 이전 까지의 다소 복잡한 내용을 넘어서 가벼운 맘으로 jQuery plugin을 실제로 만드는 부분으로 넘어가도록 하겠습니다.

감사합니다.

 

 

당신을 사랑합니다. 나 또한 어쩔 수 없는 세상의 것임을 늘 부끄러이 고해합니다. 그러나 전 언제나 당신을 향해 있습니다. 그 무엇도 막지 못할 영원이길 애타게 바라며


 

No. Subject Author Date Views
Notice 2023년 1월 - SQLER의 업데이트 강좌 리스트 코난(김대우) 2023.01.02 2140
56 jQuery MsgBox 0.2.6 BETA 출시! 컴포지트 2012.05.10 30539
55 jQuey plugin - 코드 샘플 Code Sample 한머리 2012.04.09 24224
54 기존 요소 유지하면서 텍스트만 바꾸기. 컴포지트 2012.02.16 31470
53 attr를 활용한 마우스따라 리스트색상 변하기... [2] 박규정 2012.02.08 31009
52 골때리는 자바스크립트 미니버전 [1] 컴포지트 2011.11.18 30141
51 jQuery Plugin - Plugin 작성 가이드 [2] 한머리 2011.11.16 43691
50 윈도우를 위한 node.js 초간단 구축 가이드 컴포지트 2011.11.08 43507
» jQuery Plugin - JS OOP 2편, Self-Invocating function [3] 한머리 2011.10.14 32368
48 jQuery Plugin - JS OOP 1편 [1] 한머리 2011.10.11 24469
47 jQuery plugin - 들어가기 전에 [7] 한머리 2011.10.07 32101
46 jQuery Plugin Example - 자진삭제 [5] 한머리 2011.10.07 24312
45 ajax 페이지에서 live, delegate의 오남용!! [2] 싸우라비 2011.09.20 22993
44 컴포지트가 추천하는 조낸 빠른 자바스크립트 CDN 활용 컴포지트 2011.08.31 26552
43 IE 감지 스크립트 [1] 컴포지트 2011.07.26 18626
42 [jQuery 동영상 강좌] 20. jQuery Performance [3] 승연아빠 2011.07.10 36663
41 [jQuery 동영상 강좌] 19. jQuery Event - 이벤트에 생명을~ 승연아빠 2011.07.10 34560
40 [jQuery 동영상 강좌] 18. jQuery Event - bind() 메서드 승연아빠 2011.07.10 26410
39 [jQuery 동영상 강좌] 17. jQuery Event - 이벤트 지원 메서드 승연아빠 2011.07.10 31411
38 [jQuery 동영상 강좌] 16. jQuery Form API - 폼 지원 메서드에 대하여 승연아빠 2011.07.10 30441
37 [jQuery 동영상 강좌] 15. jQuery Attribute - 요소의 속성 관련 메서드에 대하여 승연아빠 2011.07.10 21815





XE Login