본문 바로가기

Javascript

[Javascript] Factory function (팩토리 함수)

 

Factory function이란?

A factory function is a function that returns a new object.

 

말 그대로 '객체'를 반환하는 함수 입니다.

이렇게만 들으면, 정말 별것 아닌것 같지만, 자바스크립트는 일급 함수를 지원한다는 점과 결부되어, 여러가지 재밌는 일들이 가능합니다.

 

예시를 보면서 설명드리겠습니다.

 

function createUser(lang){
  return function (msg){
        console.log(`user from ${lang}: ${msg}`)
  }
}
// javascript에서 function은 객체이므로 위 함수는 factory 함수입니다.

const koreanUserSpeaks = createUser('Korea');

koreanUserSpeaks('Hello world!') // user from Korea: Hello world!

createUser 함수가 익명 함수를 반환합니다.

재밌는건, createUser로 넘겨진 인자 'Korea'가 createUser의 실행 문맥이 끝남에도 유지가 됩니다.

createUser('Korea')의 결과로 반환된 익명함수의 클로져가, 제거된 createUser('Korea')의 변수환경을 참조하고 있기 때문입니다.

그리고 해당 변수 환경 (lang = 'Korea')는 익명함수에 의해 참조되고 있기에, 가비지 콜렉터(GC)에 의해 제거되지 않습니다.

 

const koreanUserSpeaks = createUser('Korea');
const franceUserSpeaks = createUser('France');

koreanUserSpeaks('ㅎㅇ'); // user from Korea: ㅎㅇ
franceUserSpeaks('bonjour'); // user from France: bonjour

위 예시의 경우에도 비록 동일한 렉시컬 환경(lexical environment)에서 실행되었을지라도, 각기 다른 실행 문맥에 의해 생성된 익명함수들은 각자의 클로저로써, 각각의 실행 문맥의 변수환경을 참조하게 됩니다. OOP의 캡슐화와 비슷하죠?

 

 

굳이 createUser의 결과값을 할당하지 않고 커링(currying) 방식으로 호출하여도 무방합니다

createUser('jambo')('hi')

 

 

 

다른 예시를 들어보겠습니다.

const user1 = { 
  authType : 'facebook',
  email : 'dovigod@do.god',
  accessToken : '1234567' 
}
const user2 = {
  authType : 'local',
  email : 'dovigod@do.god',
  password: '123123'
}
const user3 = {
  authType : 'wallet',
  address : '0x01234512',
  nonce : 'ruigbiudbaiwdnaw',
  provider: 'metamask'
}

 

위 데이터에서 각 유저들의 데이터 포맷이 올바른 데이터인지 체크하는 함수를 만들어 보겠습니다.

const validationListMap = {
  'wallet' : ['address','nonce','provider'],
  'facebook' : ['email','accessToken'],
  'local': ['email', 'password']
}

function checkSchema(key , user){
  for(const property of validationListMap[key]){
    if(!user.hasOwnProperty(property)){
      return false
    }
  }
  return true
}

export function createValidationTest(type){
  // this 대신, checkSchma용 변수환경을 따로 제공해줘도 됩니다
  // 그러면 정말 OOP같아질것 같네용
  return checkSchema.bind(this, type) // type을 checkSchema에 바인딩하여, 디폴트로 제공
}

const facebookValidationTest = createValidationTest('facebook');
facebookValidationTest(user1); /// true