'2012/02'에 해당되는 글 8건

  1. 2012.02.25 자바스크립트 메서드 빌려쓰기
  2. 2012.02.25 자바스크립트 상속 6
  3. 2012.02.23 자바스크립트 상속 5
  4. 2012.02.21 자바스크립트 상속 4
  5. 2012.02.21 자바스크립트 상속 3
  6. 2012.02.21 자바스크립트 상속 2
  7. 2012.02.21 자바스크립트 상속 1
  8. 2012.02.19 자바스크립트 method() 함수

자바스크립트 메서드 빌려쓰기


객체 메서드 빌려 쓰기

  1. function Parent(name){
  2.     this.name = name;
  3. };
  4.  
  5. Parent.prototype.substr = function(s, e){
  6.     return this.name.substr(s, e);
  7. }
  8.  
  9. function method(name){
  10.     this.name = name;
  11.     return this;
  12. }
  13.  
  14. method.prototype.getName = function(s, e){
  15.     // Parent 객체의 메서드 빌려쓰기.
  16.     return new Parent().substr.apply(this, [s, e]);
  17. }
  18.  
  19. alert(new Parent('jsk').substr(0, 1)); // j
  20. alert(new method('jsk').getName(1, 1)); // s

모듈을 작성하다보면 어떤 객체의 메서드 한 두 개만 마음에 드는 경우가 있습니다.
 
이 메서드들을 재사용 하고 싶지만, 해당 객체와 부모-자식 관계까지 만들고 싶지는 않을때
위같은 메서드 빌려쓰기 작성법을 이용하여 원하는 메서드만 골라서 사용할 수 있습니다.


  1. function f(){
  2.     var args = [].slice.call(arguments, 1, 3);
  3.  
  4.     return args;
  5. }
  6.  
  7. alert(f(1, 2, 3, 4)); // 2, 3

가장 대표적인 사례를 뽑자면, 위의 코드에서 처럼 함수의 arguments 객체와 [].slice 메서드를 빌려 원하는 매개변수를 골라내는 방법이 있습니다.

자바스크립트 상속 6


기타 상속 방식


  1. var Inherit = (function(){
  2.    
  3.     var Parent = {
  4.         name: 'test' ,
  5.         getName: function(){
  6.             return this.name;
  7.         }
  8.  
  9.     }
  10.  
  11.     function Child(){ ; };
  12.  
  13.     Child.prototype = Parent;
  14.     Child.prototype.constructor = Child;
  15.  
  16.     return new Child();
  17.  
  18. })();
  19.  
  20. alert(Inherit.getName()); // test
  21. alert(Inherit.constructor); // Child

1. Parent 객체를 생성 합니다.

2. Child 생성자 함수의 프로토타입 맴버에 Parent 객체를 추가 합니다.

 
3. new Child 객체를 반환 합니다.
 
4. 여기서 new Child 객체는 new Parent 객체의 맴버 모두(name, getName())를 상속
받습니다.


부모 객체를 생성자 함수가 아닌 객체 리터널 방식으로 생성하여 자식 객체에게 상속한
방법 입니다.

자바스크립트 상속 5


4. 클래스 방식 상속(프로토타입 공유)


  1. var Inherit = (function(){
  2.    
  3.     function Parent(name){
  4.         this.name = name || [1];
  5.         return this;
  6.     }
  7.  
  8.     Parent.prototype.getName = function(){
  9.         return this.name;
  10.     };
  11.  
  12.     function Child(){
  13.     };
  14.    
  15.  
  16.     Child.prototype = Parent.prototype;
  17.     // Child.prototype 맴버에 추가된 Parent.prototype으로 인해 Parent 생성자 함수로 변경된 Child.prototype.constructor 를 재설정해준다.
  18.     // (보통 정보성으로만 사용되는 프로퍼티이기 때문에 기능에는 거의 영향을 미치지 않는다.)
  19.     Child.prototype.constructor = Child;
  20.  
  21.     return new Child();
  22.  
  23. })();
  24.  
  25. alert(Inherit.getName); // getName()
  26. alert(Inherit.getName()); // undefined
  27.  
  28. alert(Inherit.constructor); // Child



1. Parent 생성자 함수에 인스턴스 맴버 name 속성을 추가 합니다.
 
2. Parent 생성자 함수에 프로토타입 맴버 getName 메서드를 추가 합니다.
 
4. Child 생성자 함수의 프로토타입 맴버에 Parent 생성자 함수의 프로토타입 맴버를
추가 합니다.
 
5. new Child 객체를 반환 합니다.
 
6. 여기서 new Child 객체는 new Parent 객체의 맴버(프로토타입) 모두(getName())를
상속 받습니다.



이전 포스트의 클래스 방식 상속은 모두 부모 생성자를 호출한것과 달리 이번에 살펴볼 상속 패턴은 부모 생성자를 호출하지 않습니다.
 
원칙적으로 객체 멤버에서 재사용될 멤버는 인스턴스 맴버가 아닌 프로토타입 맴버에 추가되어야 하며, 즉 상속 되어야 하는 모든 멤버들은 프로토타입에 존재해야 한다는 것입니다.
 
하지만 이 방법은 이전과 달리 부모 생성자 함수를 한번도 호출하지 않으므로 인스턴스 맴버는 상속 받지 못한다는 단점을 가지고 있습니다.

 

자바스크립트 상속 4


4. 생성자 빌려쓰기를 통한 다중 상속


  1. var Inherit = (function(){
  2.    
  3.     function Parent1(name){
  4.         this.name = name;
  5.         return this;
  6.     }
  7.  
  8.     Parent1.prototype.getName = function(){
  9.         return this.name;
  10.     };
  11.  
  12.     function Parent2(title){
  13.         this.title = title;
  14.         return this;
  15.     }
  16.  
  17.     Parent2.prototype.getTitle = function(){
  18.         return this.title;
  19.     };
  20.  
  21.  
  22.     function Child(name, title){
  23.         Parent1.call(this, name);
  24.         Parent2.call(this, title);
  25.         return this;
  26.     };
  27.  
  28.     return new Child('name', 'title');
  29.    
  30. })();
  31.  
  32. alert(Inherit.name); // name
  33. alert(Inherit.title); // title


다중 상속 구조를 설명하면 아래와 같습니다.

1. Parent1 생성자 함수에 인스턴스 맴버 name 속성을 추가 합니다.
 
2. Parent1 생성자 함수에 프로토타입 맴버 getName 메서드를 추가 합니다.
 
3. Parent2 생성자 함수에 인스턴스 맴버 title 속성을 추가 합니다.
 
4. Parent2 생성자 함수에 프로토타입 맴버 getTitle 메서드를 추가 합니다.
 
5. Child 생성자 함수 내부에서 Function.prototype.call() 메서드를 활용하여 Parent1 생성자
함수와 Parent2 생성자 함수의 인스턴스 맴버를 Child 생성자 함수의 맴버로 복사한다.
 
6. Child 생성자 함수의 프로토타입 맴버에 new Parent 객체를 추가 합니다.
 
7. new Child 객체를 반환 합니다.
 
8. 여기서 new Child 객체는 new Parent 객체의 인스턴스 맴버 모두(name)를 상속 받습니다.
(이전과 달리 프로토타입 맴버를 상속받지 않는 이유는 다중 상속 구조에서는 프로토타입
맴버를 추가 할 수 없기 떄문입니다.)
 
9. alert(Inherit.name);과 alert(Inherit.title);의 반환값이 각각 'name', 'title'이 나오는 이유는 5번 내용과 같이 Child 생성자 함수 내부에서 Function.prototype.call() 메서드를 활용하여 Parent1 생성자 함수와 Parent2 생성자 함수의 인스턴스 맴버를 Child 생성자 함수의 맴버로 복사하고, 그와 동시에 각가의 생성자 함수의 인자값('name', 'title') 또한 주었기 때문입니다.


자바스크립트 상속 3


3. 클래스 방식 상속(생성자 빌려쓰기) 1 - 2

 
  1. var Inherit = (function(){
  2.    
  3.     function Parent(name){
  4.         this.name = name || [1];
  5.         return this;
  6.     }
  7.  
  8.     Parent.prototype.getName = function(){
  9.         return this.name;
  10.     };
  11.  
  12.  
  13.     function Child(name){
  14.         // Function.prototype.call() 메서드를 활용하여 Parent 생성자 함수의 인스턴스 맴버를 Child 생성자 함수의 맴버로 복사한다.
  15.         Parent.call(this, name);
  16.         return this;
  17.     };
  18.  
  19.     Child.prototype = new Parent();
  20.  
  21.     return new Child([2]);
  22.  
  23. })();
  24.  
  25. alert(Inherit.getName()); // 2
  26.  


상속 구조를 설명하면 아래와 같습니다.

1. Parent 생성자 함수에 인스턴스 맴버 name 속성을 추가 합니다.
 
2. Parent 생성자 함수에 프로토타입 맴버 getName 메서드를 추가 합니다.
 
3. Child 생성자 함수 내부에서 Function.prototype.call() 메서드를 활용하여 Parent 생성자 함수의 인스턴스 맴버를 Child 생성자 함수의 맴버로 복사한다.
 
4. Child 생성자 함수의 프로토타입 맴버에 new Parent 객체를 추가 합니다.
 
5. new Child 객체를 반환 합니다.
 
6. 여기서 new Child 객체는 new Parent 객체의 맴버(인스턴스, 프로토타입)
모두(name, getName())를 상속 받습니다.
 
7. alert(Inherit.getName()); 반환값이 '1'아닌 '2'가 나오는 이유는 3번 내용과 같이 new Child 객체의 인스턴스 맴버는 Child 생성자 함수의 프로토타입을 통해 new Parent
객체의 인스턴스 맴버를 상속받지 않고 
Child 생성자 함수 내부에 Function.prototype.call() 메서드를 활용하여 Parent 생성자 함수의 인스턴스 맴버를 Child 생성자 함수의 맴버로 복사하고, 그와 동시에 생성자 함수의 인자값([2]) 또한 주었기 때문입니다.

 

자바스크립트 상속 2


2. 클래스 방식 상속 1 - 1

 
  1. var Inherit = (function(){
  2.    
  3.     function Parent(){
  4.         this.names = [1];
  5.         return this;
  6.     }
  7.  
  8.     Parent.prototype.getNames = function(){
  9.         return this.names;
  10.     };
  11.  
  12.  
  13.     function Child(){
  14.         return this;
  15.     };
  16.  
  17.     var p = new Parent();
  18.    
  19.     // 자바스크립에서 객체는 참조만 전달되기 때문에, 위와 같이 자식객체(new Parent)에서 names 맴버 속성을 수정하면 부모의 names 맴버 속성까지 수정된다.
  20.     p.names.push(2);
  21.     Child.prototype = p;
  22.  
  23.     return new Child();
  24.  
  25. })();
  26.  
  27. alert(Inherit.getNames()); // 1, 2


상속 구조를 설명하면 아래와 같습니다.

1. Parent 생성자 함수에 인스턴스 맴버 name 속성을 추가 합니다.
 
2. Parent 생성자 함수에 프로토타입 맴버 getName 메서드를 추가 합니다.
 
3. new Parent 객체의 names 맴버 속성을 수정한 후 Child 생성자 함수의 프로토타입 맴버에
new Parent 객체를 추가 합니다.
 
4. new Child 객체를 반환 합니다.
 
5. 여기서 new Child 객체는 new Parent 객체의 맴버(인스턴스, 프로토타입)
모두(name, getName())를 상속 받습니다.
 
6. alert(Inherit.getName()); 반환값이 1, 2가 나오는 이유는 5번 내용과 같이 수정된 new Parent
객체의 맴버를 모두 상속 받았기 때문입니다.


자바스크립트 상속 1


1. 클래스 방식 상속 1


  1. var Inherit = (function(){
  2.    
  3.     function Parent(){
  4.         this.name = 'parent';
  5.         return this;
  6.     }
  7.  
  8.     Parent.prototype.getName = function(){
  9.         return this.name;
  10.     };
  11.  
  12.  
  13.     function Child(){
  14.         return this;
  15.     };
  16.  
  17.    
  18.     Child.prototype = new Parent();
  19.  
  20.     return new Child();
  21.  
  22.  
  23. })();
  24.  
  25.  
  26. alert(Inherit.getName()); // parent
  27.  
 

위 코드가 가장 널리 쓰이며, 가장 기본적인 상속 방법 입니다.


상속 구조를 설명하면 아래와 같습니다.

1. Parent 생성자 함수에 인스턴스 맴버 name 속성을 추가 합니다.
 
2. Parent 생성자 함수에 프로토타입 맴버 getName 메서드를 추가 합니다.
 
3. Child 생성자 함수의 프로토타입 맴버에 new Parent 객체를 추가 합니다.
 
4. new Child 객체를 반환 합니다.
 
5. 여기서 new Child 객체는 new Parent 객체의 맴버그룹(인스턴스, 프로토타입)
모두(name, getName())를 상속 받습니다.

6. alert(Inherit.getName()); 반환값이 Parent가 나오는 이유는 5번 내용과 같이 new Parent
객체의 맴버를 모두 상속 받았기 때문입니다.

또한, 상속 과정을 거친 프로토타입은 아래처럼 서로 연결 됩니다.

--> (_proto_ 속성이 가리키는 방향)

new Child --> (Child.prototype : Object.prototype) --> new Parent (member: name) --> (Parent.prototype (member: getName()) : Object.prototype)



 

자바스크립트 method() 함수


이전 프스트 내용 중 모듈패턴1 의 내용을 다시금 상기시켜 보도록 하겠습니다.


자바스크립트에서는 생성자 함수를 통해 인스턴스 생성시 크게 2가지 형태의 맴버 그룹을
가질 수 있다고 말씀드렸습니다.


즉, 인스턴스 맴버그룹과 프로토타입 맴버그룹이 그것입니다.

두 가지 방식의 큰 차이점은 아래와 같습니다.

1. 인스턴스 맴버

- 각 인스턴스 생성시 생성자 함수의 this에 맴버를 추가하게 됩니다. 이는 각 인스턴스마다 맴버가 생성된다는 말과 같으며, 그만큼 메모리 관리에는 비효율적이라는 말과도 같습니다.


2. 프로토타입 맴버

- 인스턴스 맴버와는 달리 생성자함수 객체 속성인 prototype에 추가되며, 생성 주기 또한 각 인스턴스가 아닌 함수 생성시 한번만 실행되기 때문에, 재 사용 가능한 메서드들은
인스턴스 맴버가 아닌 프로토타입 맴버에 추가하여 사용하는것이 메모리 관리에 효율적입니다.


그러나 이런 자바스크립트만의 개발 방식이 기존 객체 지향 언어와는 개발자간에 여러 혼란을 주는것도 사실입니다.

이번 포스트에는 이런 혼란을 줄이기 위한 방법으로 더글라스 크록포드가 고안한 method 함수를 살펴 보겠습니다.

  1. // method 함수 추가
  2. (function(){
  3.     if (typeof Function.prototype.method !== 'function'){
  4.         Function.prototype.method = function(n, fn){
  5.  
  6.             if (typeof n !== 'string' || n.length === 0) return false;
  7.             if (typeof fn !== 'function') return false;
  8.            
  9.             this.prototype[n] = fn;
  10.  
  11.             return this;
  12.         }
  13.     };
  14. })();
  15.  
  16.  
  17. var a = function(id, name)
  18. {
  19.     this.id = id;
  20.     this.name = name;
  21. }
  22. .method('getId', function(){ return this.id; })
  23. .method('getName', function(){ return this.name; });
  24.  
  25. alert(new a('yanione1', 'name1').getId()); // yanione1
  26. alert(new a('yanione2', 'name2').getName()); // name2
  27.  
  28.  
  29.  
  30. var train = (function(){
  31.    
  32.     var init = function(name){
  33.         this.name = name;
  34.  
  35.     }.method('test1', function(){
  36.         return this.name;
  37.     });
  38.  
  39.     return init;
  40.    
  41. })();
  42.  
  43. var o = new train('name1');
  44. alert(o.test1()); // name1


단 여기서 이상한 점이 하나 있다면, 함수 선언부 코드 작성시 함수 리터널(var a = function(id, name)) 방식을 써야 생성시 에러가 나지 않는다는 것입니다.

분명 function a()와 var a = function() 모두 전역변수이며, 둘다 Function.prototype에 추가된 method 함수의 호출 까지는 에러가 나지 않지만 생성시 문법 오류가 때문입니다.

이 문제의 답에 대해서는 좀 더 알아본 후 포스트 내용에 추가 하도록 하겠습니다.
prev 1 next