loading
본문 바로가기
FRONT-END/JS, TS

OOP - (2)팩토리펑션, 상속

by pikiforyou 2020. 1. 12.

팩토리함수 (Factory Function)


모든 함수는 새로운 객체를 반환할 수 있고, 

그중에서 생성자함수(Constructor)를 통해 만든 함수가 아닌것을 '팩토리함수'라고 부른다.

 

팩토리펑션을 이용하면 훨씬 편하고 직관적으로 코드를 사용할 수 있다.

 

앞서 만들었던 티비가 놀랍게도 인기가 많아져서 대량생산을 하고싶어졌다면?

하나하나 새롭게 코드를 적기에는 너무 힘이 든다...

마치 공장(Factory)에서 기계를 이용해 편하게 물품을 생산하듯이

재사용성을 위해 함수를 만들어 호출하는 방식이 더 유용할것이다.

 

 

// 생성자함수 ...
function Tv(){
  Tv.prototype.color = function(){
    console.log('black');
  }
}

let tv2 = new Tv();
console.log(tv2.color());


// 팩토리함수 ...

let proto = {
  color(){
    console.log('black');
  }
};

function createTv(){
  return Object.create(proto);
}

let tv3 = createTv();
let tv4 = createTv();

console.log(tv3);

 

생성자함수는 new 생성자를 이용해서 만드는 방법이고,

그 외의 방식은 팩토리함수이다. 

 

상황에 따라 원하는 방식을 사용하면 되겠으나, 

생성자함수의 경우 this키워드나 prototype-chain등에 관하여 생각을 많이 해야한다.

 

ken님이 추천해준 링크를 따라 들어가서 읽어봤는데, 

new생성자함수나 클래스를 사용하는것보다는 팩토리를 사용하는게 더 이점이 많다는 글이다.

 


 

팩토리 함수를 사용하는 이점

1. Return any arbitrary object and use any arbitrary prototype

2. No refactoring worries

3. No 'new'

4. Standard 'this' behavior

5. No deceptive 'instanceof'

 

 

팩토리 함수를 사용할때 단점

1. 'this' doesn’t refer to the new object inside the factory

2. Doesn’t create a link from the instance to 'Factory.prototype'

 


 

만약 daram.create();로 만든 경우라고 가정해보자.

여기에서 thisdaram을 의미하게 되므로 훨씬 직관적으로 구별할 수 있다.

call(), apply()역시 스텐다드하게 작동하므로 this가 무엇인가에 대해 덜 고민할 수 있다.

다만 팩토리함수로 만들어진 instance는 factory.prototype을 참조할 수 없음을 기억하자.

 

 

 


 

 

 

상속 (Inheritance)


 

엄밀히 말하자면, 상속이라고 표현하기보다는 확장(extend)이라고 해야겠지만

자바스크립트에서도 프로토타입을 이용해 상속을 구현할 수 있다.

프로토타입은 다음포스트에서 작성할건데, 

아빠 = construtor / 엄마 = prototype / 자식 = instance 라고 생각하면 쉽다.

//name, gender을 인자로 받는 Person 함수...
function Person(name, gender){
	this.name = name;
	this.gender = gender;
}
//Person의 프로토타입 생성 ...
Person.prototype.sayHi = function(){
	console.log(`${this.name} said 'Hi'`); 
};

 

예제용 Person함수를 만들었다. 이후 Person의 속성을 가져오는 Pet의 속성을 만들려고한다.

 

//추가 생성된 Pet함수...
function Pet(name, gender, pet){
	Person.apply(this, arguments);
	this.pet = pet;
}

//this 설명을 위해 잠시 추가된 변수...
let me = new Pet('daram', 'female', 'rang');

여기서 Person.apply(this, arguments)라는 부분을 보자. (apply 대신 call로도 사용가능)

Person();이라는 함수가 실행이 된것이며, 인자로 arguments가 들어갔다는 소리이다.

 

this let me = new Pet이라는 생성자에서 호출이 되었기때문에,

여기서 만들어진 새로운 인스턴스(객체)를 this로 해서

Person이라는 함수를 호출해달라고 요청한것이다.

 

즉,  new생성자 를 통해 Pet이라는 함수가 호출되고,

새로운 객체가 만들어져서 let me 에 담기게 되었는데,

this로 인해 ('daram', 'female', 'rang') 을 인자로 해서 Person함수의 속성까지 담기는 것이다.

보기쉽게 정리하자면 아래의 코드처럼 담기게 된 셈이다.

let me = {
	this.name = name;  //daram...
	this.gender = gender; //female...
	this.pet = pet; //rang...
};

 

 

 

 

이제 pet.prototype에도 다른 메소드를 추가해주고, 프로토타입끼리 연결해보자.

//별도로 Pet.prototype에도 기능을 추가 ...
Pet.prototype.sayHello = function(){
	alert(`${this.pet} said '야옹'`);
};

//프로토타입을 연결해준다 ...
Pet.prototype = Object.create(Person.prototype);
//오류수정을 위해서 constructor를 다시 잡아준다...
Pet.prototype.constructor = Pet;

 

 

Object.create라는 새로운 객체를 만들어주는 메소드를 통해서 연결해주게 된다.

주의점은 프로토타입으로 연결을 해주어야 하며, 오류수정을 한번 해주어야 한다는 점이다.

 

Pet.prototype 에  Person.prototype 을 연결해주게 됨으로써, 

Pet.prototype 의  constructor 를 잃어버리게 되므로 이를 다시 잡아줘야한다.

이는 코드를 작성할때 잊지말고 꼭! 해주어야 하는 과정이다.

 

 


자 이제 호출을 해보자!

let me = new Pet('daram', 'female', 'rang');
let he = new Person('bumgo', 'male', 'dal');

me.sayHello(); // Rang said '야옹' 
he.sayHello(); // error..
he.sayHi(); // Bumgo said 'hi'

me라는 변수의 안에는 각종속성들은 있지만 sayHello라는 메소드는 없다.

하지만 Pet의 prototype에 연결을 시켜주었기때문에 sayHello()를 실행할 수 있는것이다.

즉, 상속과 프로토타입체인으로 인해 자신에게 먼저 정보가 있는지 찾아보고

없다면 자신이 속한 객체의 프로토타입에서 정보를 가져올수있는것이다.

 

 

 

신기한 점은  Person이라는 상위클래스와 Pet이라는 하위클래스로 나누어져있다는 점이다.

 

 

 

그래서 new Person으로 생성된 he에는 상위클래스의 정보만 선택적으로 들어있으며

new Pet으로 생성된 me에는 하위클래스 정보과 상위클래스 정보 전부가 들어있는것이다.

따라서 he에서는 sayHello라는 속성을 찾을수 없기때문에 에러가 나오게 된다.

단,  he에서 sayHi를 호출한다면 이 속성은 존재하기때문에 출력이 된다!

 

 

 


 

 

 

MDN에 따르자면, 프로토타입을 이용한 상속에는 3가지가 있다

 

  • 위임형 상속 

  • 연결형 상속

  • 함수형 상속

 

 위임형 상속 

프로토타입 객체는 다른 객체의 기반이 되며 가장 흔하게 구현되는 타입니다.

 

자신이 속성을 가지고 있는지 먼저 체크한다 → Prototype 속성을 체크한다

→ 프로토타입 최상위에 있는 Object.prototype 까지 반복한다.

 

하나의 원형을 공유하기때문에, 원형을 수정하면 모든 참조된 객체의 상태도 변경된다.

 

 

 

 

 연결형 상속 

한 객체의 속성을 다른 객체에 모두 복사함으로 상속을 구현한다.

 Object.assign()을 통해 보통 구현한다. 클로저와 같이 사용하면 더욱 효과적이다.

//연결형 상속...
let proto = {
	hello: function hello(){
		return `Hi, i am ${this.name}`;
	}
};

let daram = Object.assign({}, proto, {name: 'daram'});
let hi = daram.hello();

console.log(hi); //Hi, i am daram

 

 

 함수형 상속 

새로운 속성을 연결형 상속처럼 하되, 상속 기능을 Factory함수로 만들어 사용하는 방식

이 방식은 아직 잘 이해가 가지않기때문에 후일 다시 공부해서 추가할것이다.

 

 

 

 


틀린점은 언제든지 댓글로 알려주세요 :)

 

참고 및 추가로 보면 좋을 내용들 :

1.https://medium.com/javascript-scene/javascript-factory-functions-vs-constructor-functions-vs-classes-2f22ceddf33e

2.https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Inheritance_and_the_prototype_chain

3.https://medium.com/@han7096/javascript-core-3-%ED%94%84%EB%A1%9C%ED%86%A0%ED%83%80%EC%9E%85-prototype-c2da4c24820e

 

 

'FRONT-END > JS, TS' 카테고리의 다른 글

OOP - (1) 캡슐화, 추상화  (0) 2020.01.10

댓글