JS코드를 짜면서 Factory Function에 대해서 알게됐다. Class와 굉장히 유사해보이는데 둘이 어떤 차이가 있고 무엇을 사용하는 것이 더 좋을지 궁금해졌기에 정리를 해보고자 한다.
Factory Function의 정의는 보통 객체를 반환하는 함수를 말한다.
function person(name){
const sayHello = ()=>{
console.log(`Hi I'm ${name}`)
}
return Object.freeze({
sayHello,
})
}
const peter = person('peter')
peter.sayHello() // Hi I'm peter
person이라는 함수를 생성해서 내부 함수로 sayHello를 만들어서 객체에 담아 반환해주었다.
class Person {
constructor(data) {
this.name = data;
}
sayHello() {
console.log(`Hi I'm ${this.name} `);
}
}
const peter = new Person('peter')
peter.sayHello() // Hi I'm peter
결론부터 말하면 개인적으로 Fatory Funtion을 사용하는 것이 더 좋아보인다. 물론 Class를 사용하면 안된다는 것은 아니지만 JS특성상 this 바인딩 문제로 혼란을 줄 수도 있고(물론 축약형 메서드로 해결이 가능하지만) 또 JS가 프로토타입 기반이기 때문에 자바처럼 완전한 Class가 아닌 흉내낸 느낌이 들어서 가능하다면 함수형으로 작성하는 것이 좋다는 것이 결론이다.
둘의 직접적인 차이를 비교하면서 정리해보겠다.
지금은 클래스에 private(es2019부터)선언이 가능해져서 원하는 변수,메서드에 private이 가능하지만 이전에는 모두 public이었다.
class Person {
constructor(data) {
this.name = data;
}
sayHello() {
console.log(`Hi I'm ${this.name} `);
}
}
const peter = new Person('peter')
console.log(peter.name) // peter
peter.name = 'no peter'
console.log(peter.name) // no peter
메서드에 있지않은 변수가 마음대로 호출되고
심지어 외부에서 수정까지 가능하다.
function person(name){
const pname = name
const sayHello = ()=>{
console.log(`Hi I'm ${name}`)
}
return Object.freeze({
sayHello,
})
}
const peter = person('peter')
console.log(peter.pname) // undefined
Factory Function은 함수 내에서 선언된 지역 변수들은 반환객체에 포함되지않으면 외부에서 절대 접근이 불가능하다.
this는 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수이다. 자바같은 클래스 기반 언어에서는 this가 항상 클래스가 생성하는 인스턴스를 가리킨다. 하지만 JS는 함수가 호출되는 시점에서 this 바인딩이 결정되기 때문에 제대로 코드를 작성하지 않으면 동적으로 this가 가리키는 곳이 바뀔 수 있다.
//success
class Person1 {
constructor(data) {
this.name = data;
}
sayHello(){
console.log(`Hi I'm ${this.name} `);
}
}
const peter1 = new Person1('peter1')
올바른 방식인 메서드 축약형으로 함수를 만들면 Person1의 프로토타입에 sayHello메서드가 잘 들어가 있는 것을 볼 수 있다.
class Person {
constructor(data) {
this.name = data;
}
sayHello=()=>{
console.log(`Hi I'm ${this.name} `);
}
}
const peter = new Person('peter')
만약 메서드를 화살표 함수로 선언하게되면 sayHello가 프로퍼티가 아닌 상위에 위치해 있다. 화살표 함수는 this binding을 따로 해주지 않기 때문에 상위 스코프의 this를 그대로 따른다. 그래서 생성자 함수의 this에 같이 포함이 되어 프로토타입에 위치하지 않는 것이다.
또한 map, reduce 같이 콜백함수를 사용하는 경우에는 내부에서 화살표 함수를 사용할 때는 상위 스코프를 그대로 사용하기 때문에 문제가 없지만 일반함수를 사용했을 때 this 바인딩이 새로되기 때문에 문제가 발생할 수 있다.
이런 여러 경우에 따라 사용해야하는 함수들을 고려해야할 필요가 있기 때문에 개발자 입장에서는 this 사용을 줄이는 것이 효율적이라고 본다.
function person(name){
const pname = name
const sayHello = ()=>{
return `Hi I'm ${pname}`
}
return Object.freeze({
sayHello,
})
}
const peter = person('peter')
console.log(peter.sayHello()) // Hi I'm peter
Factory Function을 사용하는 경우에는 this를 메서드에서 사용을 할 수는 있지만 this없이도 충분히 자기참조가 가능하다.
Factory Function도 클래스처럼 상속이 가능하다.
Object.create()를 사용해서 프로토타입으로 넣어줄 수 있다.
function act(){
const say = () => 'hi';
return {
say,
}
}
function person(name){
return Object.create(act(), {
sayHello: {
value: ()=> `Hi I'm ${name}`
}
})
}
const peter = person('peter')
console.log(peter.sayHello()) // Hi I'm peter
console.log(peter.say()) // hi
또는 원하는 객체의 메서드를 직접 넣어주는 방법도 존재한다.
Factory Function의 유일한 단점은 메모리 비용이 class를 사용하는 것보다 더 많이 든다는 것이다.
결국 상황에 따라 적절한 방식을 사용하는 것이 맞지만
둘 중 하나를 선택할 수 있는 상황이라면 Factory Function을 사용하겠다.
이 방식이 조금 더 JS에 어울리는 것같고 예상치 못한 에러를 막는데에도 좋아보이기 때문..