메소드 호출을 이용한 객체 생성 (객체 리터럴 모듈 패턴)

객체.메소드() 과 같이 객채 내에 메소드를 호출하는 방법을 의미합니다.

메소드 호출 방식을 이용할때는 화살표 함수 표현을 사용하지 않습니다.

화설표 함수 표현식의 제한점

  • this 나 super에 대한 바인딩이 없고, methods로 사용될 수 없습니다.
  • new.target 키워드가 없습니다.
  • 일반적으로 스코프를 지정할 때 사용하는 call, apply, bind methods를 이용할 수 없습니다.
  • 생성자(Constructor)로 사용할 수 없습니다.
  • yield를 화살표 함수 내부에서 사용할 수 없습니다.
let counter = {
  privatekey : 0,
  increase: function(){
    this.privatekey+=1;
  },
  decrease: function(){
    this.privatekey-=1;
  },
  getKey: function(){
    return this.privatekey;
  }
}
let count1 = counter;
let count2 = counter;
count1.increase();
console.log(`count1 = ${count1.getKey()}   count2 = ${count2.getKey()}`);
// output : count1 = 1   count2 = 1
console.log(count1 === count2); // output : true

자바스크립트에서 모듈을 구현하는 가장 쉬운 방법은 객체 리터럴을 사용하는 방법입니다.

하나의 객체라는 점에서 싱글톤 패턴이라고도 할 수 있습니다.

객체 리터럴은 간단하지만 모든 속성이 공개되어 있다는 단점이 있습니다. 독립된 모듈은 자체적으로 필요한 내부 변수 및 내부 함수를 갖고 있어야 하므로 클로저를 이용해야 합니다.

클로저를 활용한 모듈 패턴

const closer = (function(){
  /*
    ------------------------------
    *모듈 패턴을 구현할 클로저 코드
    ------------------------------
  */
  // 은닉될 멤버 정의
  let privatekey = 0;
  function increase(){
    return privatekey+=1;
  }
  function getKey(){
    return privatekey;
  }

  // 공개될 멤버(특권 메소드) 정의
  return {
    publicKey: privatekey,
    publicIncrease: function() {
      return increase();
    },
    publicGetKey: function() {
      return getKey();
    },
  }
})();
closer.publicIncrease();
console.log(`closer.key = ${closer.publicGetKey()}`);
// output : closer.key = 1
console.log(closer);
// output
{
  publicKey: 0,
  publicIncrease: [Function: publicIncrease],
  publicDecrease: [Function: publicDecrease],
  publicGetKey: [Function: publicGetKey]
}

모듈 패턴의 반환값은 함수가 아닌 객체입니다. 익명 함수가 자동으로 실행되고 반환된 객체를 closer 변수에 할당합니다. 따라서 closer.publicIncrease() 처럼 메소드를 호출할 수 있습니다.

자동 호출되는 점을 제외하고 클로저와 유사합니다.

클로저(싱글톤 패턴 X)

function makeCounter(){
  let value : 0;
~~~~  ~~r~~eturn{
    increase: function(){
      this.value++;
    },
    getValue: function(){
      return this.value;
    }
  }
}

const makeCount1 = makeCounter();
const makeCount2 = makeCounter();
makeCount1.increase();
console.log(`makeCount1 = ${makeCount1.getValue()}   makeCount2 = ${makeCount2.getValue()}`)
// output : makeCount1 = 1   makeCount2 = 0

클로저는 인스턴스를 여러개 만들어 낼 수 있는 구조라는 점에서 싱글톤 패턴과 차이가 있습니다.

싱글톤 패턴

const singleton = (function(){
  let instance;
  let privatekey = 0;
  function increase(){
    return privatekey+=1;
  }
  function getKey(){
    return privatekey;
  }
  function init(){
    return {
      publicKey: privatekey,
      publicIncrease: function() {
        return increase();
      },
      publicGetKey: function() {
        return getKey();
      },
    }
  }

  return function(){

        // 싱글톤 패턴은 아래 조건문에서 처음 인스턴스가 선언되면
        // 다시 인스턴스를 만들지 않고 기존의 인스턴스를 리턴합니다.
    if(!instance){
      instance = init();
    }
    return instance;
  }
})()

const singleton1 = singleton();
const singleton2 = singleton();
singleton1.publicIncrease();
console.log(`singleton1 = ${singleton1.publicGetKey()}    singleton2 = ${singleton2.publicGetKey()}`)
// output : singleton1 = 1    singleton2 = 1
console.log(singleton1 === singleton2) // output : true

싱글톤(Singleton)이란?

 

    싱글톤 패턴의 예를 보여주는     클래스 다이어 그램

싱글톤 패턴은 인스턴스가 오직 1개만 생성 되야 하는 경우에 사용되는 패턴입니다.

싱글턴 패턴을 따르는 클래스는 생성자가 여러 차례 호출되더라도 실제로 생성되는 객체는 하나이고 최초 생성 이후에 호출된 생성자는 최초의 생성자가 생성한 객체를 리턴합니다.

싱글톤 패턴의 특징

  • 객체 자체에는 접근이 불가능해야 합니다.
  • 객체에 대한 접근자(비공개 멤버: 클로저)를 사용해 실제 객체를 제어할 수 있습니다.
  • 객체는 단 하나만 생성해야 하며, 해당 객체를 공유합니다.

생성자 함수 방식

익명함수(Anonymous function)란?
함수명 대신 변수명에 함수 코드를 저장하는 구현 방식 , 이름이 없는 함수

const anonymousFunction = function ( 매개변수 ){
    실행문;
};
function closure(){
  const count = 0;
  // 익명함수( 클로저 ) 반환
  return function() {
    return count+=1;
  }
}

하나의 인스턴스를 선언하지 않고 여러개의 인스턴스를 생성하려면 익명함수를 사용하지 않고 생성자 함수방식으로 만들면 됩니다.

const constructorFunction  = function() {
  let privateKey = 0;
  function increase(){
    return privateKey+=1;
  }
  function getKey(){
    return privateKey;
  }

  return {
    publicKey : privateKey,
    publicIncrease: function() {
      return increase();
    },
    publicGetKey: function() {
      return getKey();
    },
  }
}
let obj1 = constructorFunction();
let obj2 = constructorFunction();
console.log(obj1 === obj2) // output : false

함수를 정의하여 함수를 호출하면 여러개의 인스턴스를 생성할 수 있습니다.. 클로저 인스턴스와 유사하지만, 한가지 차이점은 내부의 익명함수에서 반환값이 함수가 아니라 객체를 반환한다는 점입니다.

모듈 패턴과 네임 스페이스 패턴을 활용한 코드

var app = app || {};
app.module = (function() {

    let privateKey = 0;
    function privateMethod() {
        return privateKey+=1;
    }

    return {
        publicKey: privateKey,
        publicMethod: function() {
            return privateMethod();
        }
    }
})();

console.log(app.module.publicMethod()); // 0
console.log(app.module.publicMethod()); // 1

🌐 문제

let videoList = [
      [
          ['id',1],
          ['title','first video'],
          ['description','This is first video'],
          ['view',24],
      ],
      [
          ['id',2],
          ['title','second video'],
          ['description','This is second video'],
          ['view',17],
      ],
      [
          ['id',3],
          ['title','third video'],
          ['description','This is third video'],
          ['view',15],
      ],
];

비디오의 데이터를 보관하기위해 배열에 데이터를 집어 넣었습니다. 하지만 데이터들을 좀더 효용성 있게 관리 하고 싶습니다.

[
    {
        id: 1,
        title: 'first video',
        description: 'This is first video',
        view: 24
    },
    {
        id: 2,
        title: 'second video',
        description: 'This is second video',
        view: 17
    },
    {
        id: 3,
        title: 'third video',
        description: 'This is third video',
        view: 15
    },
];

이때 배열안에 배열로 정리를 하기보다 객체를 집어 넣어 key와 property로 관리 하는 것이 더 효용성이 있습니다. 

🌟작성 코드

function convertArrayToObject(arr){
    let newArr = [];
    for(const prop of arr){        
        let newObj = {};
        prop.forEach(element => {
           newObj[element[0]] = element[1]
        });
        newArr.push(newObj);
    }
    return newArr;
}

코드 해석

입력받은 배열을 for ...of 문을 통해 prop에 배열안에 있는 배열의 데이터를 가져옵니다. 

for (const prop of arr){
	let newObj = {};
	console.log(prop);
}
/* output
[
  [ 'id', 1 ],
  [ 'title', 'first video' ],
  [ 'description', 'This is first video' ],
  [ 'view', 24 ]
]
...
*/

prop를 forEach문을 통해 배열의 각 요소를 가져옵니다.

for(const prop of arr){
	let newObj = {};
	prop.forEach(element => {
    	console.log(element);
        console.log('-------');
    }
}
/* output
[ 'id', 1 ]
------
[ 'title', 'first video' ]
------
[ 'description', 'This is first video' ]
------
[ 'view', 24 ]
------
...
*/

element에 담겨 있는 배열의 인덱스 값으로 하여금 newObj 에 객체를 추가합니다.

for(const prop of arr){
	let newObj = {};
	prop.forEach(element => {
    	newObj[element[0]] = element[1]
        // newObj['id'] = '1';
        // ...
        // newObj['view'] = 24;
    }
}

element가 prop 데이터 만큼 순회를 했을 경우 forEach를 종료하고 newArr에 추가한 newObj를 push 합니다.

let newArr = [];
for(const prop of arr){        
	let newObj = {};
	prop.forEach(element => {
		newObj[element[0]] = element[1]
	});
	newArr.push(newObj);
      	console.log(newObj);
      /* output
        {
          id: 1,
          title: 'first video',
          description: 'This is first video',
          view: 24
        }
    */
}

이 작업을 prop가 arr의 Symbol.iterator 속성이 있는 컬렉션 요소 갯수 만큼 반복합니다.

🗒️순서도

 

데이터 접근성 비교

첫번째 영상의 타이틀의 데이터를 가지고 오고 싶습니다.( 'first video' )

배열안에 배열일 경우

console.log(videoList[0][1][1]) // output: 'first video'

배열안에 객체일 경우

console.log(videoList[0].title) // output: 'first video'

배열안에 배열일 경우 현재 가지고 있는 데이터의 정보를 모르면 어느 부분에 접근하는지 자세히 알기 어렵습니다. 하지만 배열안에 객체일 경우는 내가 무엇을 가져오고자 하는지 직관적으로 알수 있습니다. 

 

* 틀린 부분이 있으면 언제든 피드백 부탁드립니다.

'JavaScript > JS' 카테고리의 다른 글

객체 리터럴 모듈 패턴 , 클로저 패턴, 싱글톤 패턴  (0) 2021.10.05

+ Recent posts