기본 자바스크립트 문법 정리는 프로토타입으로 마무리하고 앞으로는 ES6에 추가된 문법을 정리하겠습니다. 기본 자바스크립트는 실행컨텍스트와 프로토타입의 비중이 커서 이 부분은 빡세게 했는데, 대신 나머지 문법은 레퍼런스로 남긴 강의를 수강하시는게 오히려 정리가 빠를거에요. 그 정도로 자바스크립트의 문법이 유연하다는거 정도만 염두해 두시고 이후 기본 객체를 활용하기 위해 라이브러리 같이 정리된게 필요하시면 구글링을 하세요.

ES6 정리 순서는

(1) let, const 키워드

(2) Destructuring

(3) template, Arrow function, module export & import

(4) Set, Map, Proxy

정도로 예상합니다. 물론 순서는 바뀔수 있습니다~

ES6는 기존 자바스크립트와 큰 차이로 접근하기보다는 기능이 좀 더 확장되었다고 생각하고 접근하시는게 좋습니다. 근데 좀 많이 확장된 탓에 IE에서 잘 안돌아갈 뿐이고요.. -,.-;; 그래도 걱정마세요. 우리에겐 크롬이 있잖아요!

.. 그럼 다음 글에서 이어갑니다~

Posted by kevin.jeong.
,

지금까지 프로토타입 원리에 대해서 쭉 설명드렸습니다.

저도 처음 봤을때 헤맸던게, prototype으로 뭔가 해주는건 알겠는데 이게 어떻게 동작하는건지 감이 안오니 보면서 멍때릴수밖에 없더라고요. 간단하게 쓸수 있는 방법이라도 알았으면 원리만 어떻게든 빨리 이해해보고 적용할텐데요.

그래서 prototype을 간단하게 설정할 수 있는 메소드를 이번에는 정리했습니다.


※ Object 객체에서 제공하는 static methods

Object 객체는 자바스크립트 내 모든 객체의 원형 답게 새로운 객체를 생성하고 프로토타입을 할당할 수 있는 static method를 제공합니다. 이걸 이용하면 앞에서 했던 복잡한 코드 없이도 간편하게 객체간의 관계를 설정하거나, 객체 리터럴로 생성해서 데이터만 담고 있는 객체에도 prototype 구현에 의한 의존관계를 주입할 수가 있죠. 하나씩 알아보죠.


※ Object.cretae(), Object.assign()

Object.cretae() 메소드는 생성자 함수 없이 특정 자바스크립트 객체를 본떠서 새로운 객체를 만들수 있게 합니다. 객체를 카피한다고 생각하시면 되고요.

Object.assign() 메소드는 한 객체에다가 다른 객체의 프로퍼티를 복사해주는 역할을 합니다. 간단하게는 객체 리터럴로 이식할 프로퍼티만 표현해서 프로퍼티를 추가하는데 쓰이며, 객체에서 특정 프로퍼티만 쏙 뽑아오는 용도로 쓰일수도 있죠. 어떻게 활용도를 높일지는 개발자의 상상력에 달렸다고 생각하세요.

이것 역시 예제를 만들어보죠.

const obj01 = {
  name : 'kevin',
  age : 33
}

const obj02 = Object.create(obj01);

console.log(obj01 === obj02);

const obj03 = Object.assign(obj01, {
  job : 'freelancer',
  mainTechs : ['javascript', 'nodeJs', 'mongodb'],
});

console.dir(obj03);

const methods = {
  showTechs : function(){
    console.log(this.name + '의 주 스킬은');
    this.mainTechs.forEach(function(val, idx){
    	console.log((idx + 1) + '번째 : ' + val);
    })
  }
}

const obj04 = Object.assign(obj01, methods, {
  job : 'freelancer',
  mainTechs : ['javascript', 'nodeJs', 'mongodb'],
});

obj04.showTechs();

실행결과 입니다.

첫번째 출력은 Object.cretae()로 obj01을 카피한 다음 카피한 객체와 원래 객체가 같은지 확인해봤습니다. 객체를 카피했기 때문에 메모리 주소가 달라져서 false를 출력하네요. 프로퍼티를 하나씩 비교했으면 몰라도 중요한건 카피되었기 때문에 프로퍼티와는 상관없이 다른걸로 본다는 점입니다.

두번째 출력은 Object.assign() 을 실행한 결과인데요. obj01 객체에 job과 mainTechs 프로퍼티가 추가된게 보이시죠? 소소코드만 보면 showTechs() 메소드는 목록에 나오면 안될거 같지만, 자바스크립트에서는 hoisting 이라는 기능이 있어서 순서가 의도한대로 되지 않은거 같습니다. hoisting 기능은 검색을 하시거나 앞의 글에서 계속 레퍼런스로 달았던 강의를 참고하시고요. 고쳐서 다시 실행해보겠습니다.

const obj01 = {
  name : 'kevin',
  age : 33
}

const obj02 = Object.create(obj01);

console.log(obj01 === obj02);

const obj03 = Object.assign(Object.create(obj01), {
  job : 'freelancer',
  mainTechs : ['javascript', 'nodeJs', 'mongodb'],
});

console.dir(obj03);

const methods = {
  showTechs : function(){
    console.log(this.name + '의 주 스킬은');
    this.mainTechs.forEach(function(val, idx){
    	console.log((idx + 1) + '번째 : ' + val);
    })
  }
}

const obj04 = Object.assign(Object.create(obj01), methods, {
  job : 'freelancer',
  mainTechs : ['javascript', 'nodeJs', 'mongodb'],
});

obj04.showTechs();

Object.assign() 메소드의 첫번째 인자는 대상 객체가 되고, 그 다음 인자부터는 추가할 프로퍼티를 갖는 객체들이 되겠습니다. 저렇게 넣는순서대로 추가가 되죠. 위의 예제에서는 obj01을 그대로 사용했기 때문에 obj01에 다 붙어버린거 같고요. 이번에는 Object.cretae()로 실행 단계마다 대상 객체를 처음상태로 보존했습니다. 실행과정을 설명하면 대상객체를 객체 원형으로 인식하는 새로운 객체를 만들어서 그 안에 인자 객체들의 프로퍼티들을 카피했네요.

세번째 출력은 obj01을 카피한 객체에 showTechs() 메소드를 이식해서 실행한 결과인데요. methods 객체 자체는 mainTechs 프로퍼티가 없지만 이식해서 실행하니까 실행컨텍스트가 obj04 가 되서 obj04에서 mainTechs 프로퍼티를 찾아서 출력해주네요.


※ Object.setPrototypeOf()

Object.setPrototype()은 첫번째 인자 객체의 prototype을 두번째 객체로 설정하는 기능을 합니다. 생성자 함수 없이 prototype을 설정하는 방법이라고 할수 있죠. 예제를 보겠습니다.

const obj01 = {
  name : 'kevin',
  age : 33,
  job : 'freelancer',
  mainTechs : ['javascript', 'nodeJs', 'mongodb'],
}

const methods = {
  showTechs : function(){
    console.log(this.name + '의 주 스킬은');
    this.mainTechs.forEach(function(val, idx){
    	console.log((idx + 1) + '번째 : ' + val);
    })
  }
}

Object.setPrototypeOf(obj01, methods);
obj01.showTechs();
console.dir(obj01);

실행결과는 다음과 같습니다.

첫번째 출력은 obj01에 이식한 showTechs() 메소드를 실행한 결과고요. 두번째 출력으로 프로토타입이 설정된 obj01 객체의 구조를 보겠습니다. 값만 갖고 있던 객체에 메소드를 갖고 있는 methods 객체를 프로토타입으로 설정해서 showTechs 메소드를 접근할수 있는 겁니다.


※ Object.setPrototypeOf() 으로 prototype chaining 구현

Object.setPrototype()으로 prototype을 설정하는 방법을 이용해서 prototype chaining을 2탄의 예제보다 훨씬 깔끔하게 구현할 수 있습니다.  예제를 보도록 하죠.

const methods_var = {
  getName : function(){
    return this.name;
  },
  getAge : function(){
    return this.age;
  },
  getJob : function(){
    return this.job;
  },
  getMainTechs : function(){
    return this.mainTechs;
  },
}

const methods_detail = {
  showTechs : function(){
    console.log(this.name + '의 주 스킬은');
    this.mainTechs.forEach(function(val, idx){
    	console.log((idx + 1) + '번째 : ' + val);
    })
  },
  getInfo : function(){
    console.log('※ ' + this.name + '의 정보');
    console.log('이름 : ' + this.name);
    console.log('나이 : ' + this.age);
    console.log('직업 : ' + this.job);
    console.log('주요기술 : ' + this.mainTechs.join(', '));
  }
}

Object.setPrototypeOf(methods_detail, methods_var);

const obj01 = {
  name : 'kevin',
  age : 33,
  job : 'freelancer',
  mainTechs : ['javascript', 'nodeJs', 'mongodb']
}

Object.setPrototypeOf(obj01, methods_detail);
console.log(obj01.getName());
console.log(obj01.getAge());
obj01.getInfo();
console.dir(obj01);

실행결과는 다음과 같습니다.

Object.setPrototypeOf() 을 이용해서 prototype chaining을 구현한 결과 객체의 구조가 훨씬 단순해졌네요. 생성자 객체와 prototype 프로퍼티가 없는 덕분인데요. 이 역할을 methods_var, methods_detail 객체와 Object.setPrototypeOf() 가 대신 한다고 생각하시면 되겠습니다. 다르게 말하면 생성자 함수와 prototype 프로퍼티 대신 값만 넣는 객체 리터럴과 Object.setPrototypeOf() 메소드로 __proto__ 프로퍼티만 설정해줘서 상-하위 객체 관계를 구현한 것이죠. 그렇다면 "__proto__에 직접 본뜰 객체로 삽입하면 되지 않나요?" 라고 질문하실 분도 있으실텐데요.

보통 자바스크립트에서 __로 시작해서 __로 끝나는 이름을 갖는 프로퍼티는 직접 접근하지 않는게 원칙입니다. 실행컨텍스트와 밀접한 관련이 있기 때문에 안전성을 보장받기 힘들어지거든요. 그래서 Object.setPrototypeOf() 같은 static 메소드가 등장하는 것입니다. 실행컨텍스트를 유지할 수 있는 방법으로 활용하라고 있는거죠.


※ 자바 문법을 자바스크립트에서 돌리다. 

한국에서 열심히 자바만 코딩해오신 분들에게 자바스크립트 공부할때 진입장벽으로 작용하는게 클래스와 객체의 혼동일텐데요. 이론적으로 이해를 했다고 하더라도 실제 코드를 보면

이렇게 말하는 분들이 태반이죠. 네 저도 불과 몇년전까지 똑같은 소리 했었고요..

그래서 ES6 에서 class를 구현할 수 있는 문법이 추가되었습니다.

예제로 설명드리죠.

class Person {
  constructor(name, age, job, mainTechs){
    if(!(mainTechs instanceof Array)){
      console.log("4번째 인자는 배열로 전달해야 합니다!");
      return false;
    }

    this.name = name;
    this.age = age;
    this.job = job;
    this.mainTechs = mainTechs;
  }

  getName(){
    return this.name;
  }

  getAge(){
    return this.age;
  }

  getJob(){
    return this.job;
  }

  getMainTechs(){
    return this.mainTechs;
  }
}

class PersonExt extends Person{
  constructor(name, age, job, mainTechs){
    super(name, age, job, mainTechs);
  }

  showTechs(){
    console.log(this.name + '의 주 스킬은');
    this.mainTechs.forEach(function(val, idx){
      console.log((idx + 1) + '번째 : ' + val);
    })
  }

  getInfo(){
    console.log('※ ' + this.name + '의 정보');
    console.log('이름 : ' + this.name);
    console.log('나이 : ' + this.age);
    console.log('직업 : ' + this.job);
    console.log('주요기술 : ' + this.mainTechs.join(', '));
  }
}

const pobj = new PersonExt('kevin', 33, 'freelancer', ['javascript', 'nodeJs', 'mongodb']);
console.log(pobj.getName());
console.log(pobj.getAge());
pobj.getInfo();

실행결과는 다음과 같습니다.

딱 자바에서 짰던거 처럼 실행되네요. public/private/protected와 인스턴스 변수 선언 부분이 없는거 말고는 자바 클래스 짜는거랑 비슷하네요. 클래스의 생성자를 무조건 constructor() 로 구현하는 거 정도가 마지막 차이이고 코드 구조만으로는 큰 차이가 없습니다. 그럼 한번 이 클래스를 한번 까볼까요?

prototype, constructor, __proto__ 프로퍼티가 있네요. 2탄에서 열심히 prototype chaining 예제 짤때랑 똑같이 나왔네요. 즉, 소스코드의 문법은 클래스로 보이지만, 실제로는 prototype chaining 구현 방식대로 객체가 만들어지는거고요. class로 선언한 이름이 실제로는 <<클래스이름>>.prototype의 실행컨텍스트라고 생각하시면 되겠습니다.


※ 레퍼런스

이 글은 인프런 사이트의 ‘핵심개념을 알아보는 Javascript Flow’ 강좌 및 '모던자바스크립트(javascript) 개발을 위한 ES6 강좌' 강좌의 내용을 토대로 예제코드를 작성하여 설명하였습니다. 자바스크립트를 더 깊이 이해하기를 원하시면 해당 강좌를 수강하시는 것을 권장합니다. 첫번째 강좌는 무료이나, 두번째 강좌는 비용 결재 후 수강 가능합니다. 다만 인프런 사이트 내 상당수의 강좌가 무료이며, 유료강좌도 비용이 강좌당 몇만원 수준이어서 크게 부담은 안되실겁니다. 그리고 인프런과 협약을 맺은 대학교의 학생들은 이 유로강좌도 무료로 들을 수 있으니 인프런 사이트를 참고하시기 바랍니다.

Posted by kevin.jeong.
,