• javascript 

    1. 글에 대해

    • 이 글은 Hika Maeng 님이 추천해주신 ECMAScript 6 길들이기라는 책과 개인적인 테스트를 통해 작성되었습니다.

    • 또한 모든 결과는 Chrome 브라우저를 통해 테스트된 결과입니다.

    2. 클래스 선언 및 표현식

    • ES6 클래스는 ES5 가 가진 객체 지향 모델을 좀더 명시적으로 다룰수 있도록 개선된 새로운 모델이다.

    • 클래스 선언식

      ```javascript
      // 클래스 선언
      class A {
          constructor(id){
        
              console.log(arguments); // ['yanione']
        
              try{
                  console.log(arguments.callee);
              }
              catch(e){
        
                  // ES6 클래스 body 는 기본적으로 strict mode 위에서 동작한다.
        
                  // 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions
                  // or the arguments objects for calls to them
                  console.log(e.message);
              }
        
              this.id = id;
          }
        
          // 프로토타입 맴버 선언
          getId(){
              return this.id;
          }
      }
        
      // 선언된 클래스는 기존 함수 객체와 거의 유사한 구조를 가지고 있다.
      console.dir(A);
        
      // 선언된 클래스는 function 데이터 타입을 반환한다.
      console.log(typeof A); // function
        
      // 선언된 클래스의 constructor 속성은 Function (생성자)함수 객체를 반환한다.
      console.dir(A.constructor); // Function function object
        
      // 선언된 클래스가 가진 원형 객체
      console.log(A.prototype);
        
      // 선언된 클래스의 prototype 객체 프로퍼티는 getId 맴버를 포함하고 있다.
      console.log(A.prototype.getId); // getId function object
        
      // newAObject 객체를 생성한다.
      var newAObject = new A('yanione');
        
      // newAObject 객체의 __proto__ (내부)속성은, A.prototype (원형)객체를 참조하고있다.
      console.log(newAObject.__proto__ === A.prototype); // true
        
      // newAObject 객체의 prototype chain 에는 A.prototype 객체가 존재하므로 아래 코드를 통해 true 를 반환하게 된다.
      console.log(newAObject instanceof A); // true
        
      // newAObject 객체의 (생성자)함수(클래스)인 A 함수(클래스) 객체를 반환한다.
      console.log(newAObject.constructor); // A class
      console.log(typeof newAObject.constructor); // function
        
      console.log(newAObject.getId()); // yanione
        
      // newAObject 객체는, ES5 의 (생성자)함수 객체를 통해 생성된 객체와 동일한 구조를 가지고있다.
      console.dir(newAObject);
      ```
        
      선언된 클래스 내부
        
      ![](/blog/assets/images/posts/20160203/class_0-1.png)
        
      newAObject 객체 내부
        
      ![](/blog/assets/images/posts/20160203/class_05.png)
        
      <h2>정리</h2>
        
      - <span style="color:#c11f1f">A</span> 클래스는 기존 <span style="color:#c11f1f">함수</span> 객체와 거의 <span style="color:#c11f1f">유사한 구조</span>를 가지고 있다.<p>
        
      - <span style="color:#c11f1f">A</span> 클래스는 <span style="color:#c11f1f">function</span> 데이터 타입을 <span style="color:#c11f1f">반환</span>한다.<p>
        
      - <span style="color:#c11f1f">A</span> 클래스의 prototype 객체 프로퍼티는 선언된 <span style="color:#c11f1f">프로토타입 맴버</span>인 getId 메서드를 포함하고 있다.<p>
        
      - newAObject.constructor 속성은 (생성자)함수인 <span style="color:#c11f1f">A</span> 함수(클래스) 객체를 참조하고 있다.<p>
        
      - newAObject 객체는, ES5 의 (생성자)<span style="color:#c11f1f">함수</span> 객체를 통해 생성된 객체와 <span style="color:#c11f1f">동일한 구조</span>를 가지고있다.<br><br><br><br>
      
    • 그밖의 테스트

      • 클래스 선언식EC 진입실행 코드 처리 후에도 VO 의 새로운 속성으로 추가되지않는다.

          // ec 진입 시 VO 의 새로운 속성으로 추가되지않는다.
          try {
              console.log(A);
          }
          catch(e){
              console.log(e.message); // A is not defined
          }
        
          // 클래스 선언식
          class A {
              ...
          }
        
          // 실행 코드 처리후에도 VO 의 새로운 속성으로 추가되지않는다.
          console.log(window.A); // undefined
        
      • 일반적인 함수 호출의 경우 아래와 같은 예외가 발생하게된다.

          // 클래스 선언
          class A {
              constructor(){
                  console.dir(this);
              }
          }
        
          // 일반적인 함수 호출의 경우 아래와 같은 예외가 발생하게된다.
        
          try {
              A();
          }
          catch(e){
              // Class constructors cannot be invoked without 'new'
              console.log(e.message);
          }
        
        
          // call, apply, bind 메서드를 통해 호출한 경우에도 동일한 에러가 발생하게된다.
          try {
              A.call({});
          }
          catch(e){
              // Class constructors cannot be invoked without 'new'
              console.log(e.message);
          }
        
          try {
              A.apply({});
          }
          catch(e){
              // Class constructors cannot be invoked without 'new'
              console.log(e.message);
          }
        
          try {
              var _A = A.bind({});
              _A();
          }
          catch(e){
              // Class constructors cannot be invoked without 'new'
              console.log(e.message);
          }
        
        
      • ES5 에서 ES6 클래스와 같이, 일반적인 함수 호출막는 방법은 아래와 같다.

          function A(id, name){
        
              // this 값 내부 prototype chain 에 A.prototype 이 존재하지 않는 경우
              if (!(this instanceof arguments.callee)){
                  throw new Error('Uncaught TypeError: Class constructors cannot be invoked without \'new\'');
              }
        
              // 인스턴스 맴버를 정의한다.
              this.id = id;
              this.name = name;
        
              return this;
          }
        
          // 일반적인 함수 호출의 경우, 초기화된 this 값(global Object) 내부 prototype chain 에는 A.prototype 이 존재하지 않는다.(즉 예외가 발생하게된다)
          A(); // Uncaught TypeError: Class constructors cannot be invoked without new\
        
        
          // 하지만 call(or apply, bind) 메서드를 통해, A.prototype 이 포함한 객체를 전달할 경우, (new 연산자가 생략된)일반적인 함수 호출을 막을수는 없다.
          console.log(A.call(Object.create(A.prototype, {age: {value: 18}}), 'yanione', 'mohwa')); // Object {id: "yanione", name: "mohwa", age: 18}
        
        
      • 선언된 클래스와 동일한 식별자 이름으로 선언 시 아래와 같은 에러가 발생한다.

        
          // 클래스 선언식
          class A {
              ...
          }
        
          // 선언된 클래스와 동일한 식별자 이름으로 선언 시 아래와 같은 에러가 발생한다.
        
          // Uncaught SyntaxError: Identifier 'A' has already been declared
          // var A;
        
        





    • 클래스 표현식

      ```javascript
      // 클래스 표현식
      var A = class A{
          constructor(id){
        
              console.log(arguments); // ['yanione']
        
              try{
                  console.log(arguments.callee);
              }
              catch(e){
        
                  // ES6 클래스는 기본적으로 strict mode 위에서 동작한다.
        
                  // 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions
                  // or the arguments objects for calls to them
                  console.log(e.message);
              }
        
              this.id = id;
          }
        
          // 프로토타입 맴버 선언
          getId(){
              return this.id;
          }
      }
        
      // 표현식에서 클래스명은 생략 가능하다.
      var _A = class{
          constructor(id){
        
              console.log(arguments); // ['yanione']
        
              try{
                  console.log(arguments.callee);
              }
              catch(e){
        
                  // ES6 클래스는 기본적으로 strict mode 위에서 동작한다.
        
                  // 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions
                  // or the arguments objects for calls to them
                  console.log(e.message);
              }
        
              this.id = id;
          }
        
          // 프로토타입 맴버 선언
          getId(){
              return this.id;
          }
      }     
      
      // 선언된 클래스는 기존 함수 객체와 거의 유사한 구조를 가지고 있다.
      console.dir(A);
        
      // 일반적인 함수 호출의 경우 함수 선언식과 동일한 예외가 발생하게된다.
      try {
          A();
      }
      catch(e){
          // Class constructors cannot be invoked without 'new'
          console.log(e.message);
      }
        
      var newAObject = new A('yanione');
      console.dir(newAObject);
        
      var new_AObject = new _A('yanione');
      console.dir(new_AObject);
      ```
        
      선언된 클래스 내부(<em>이전 <span style="color:#c11f1f">클래스 선언식</span>과 달리 \<function scope\> 내부에 <span style="color:#c11f1f">Script 속성</span>이 포함되지 않았다</em>)
        
      ![](/blog/assets/images/posts/20160203/class_0-2.png)
              
      (기명) 클래스 표현식 결과
        
      ```javascript
      var newAObject = new A('yanione');
      console.dir(newAObject);
      ```
      ![](/blog/assets/images/posts/20160203/class_14.png)
        
      (익명) 클래스 표현식 결과
        
      ```javascript
      var new_AObject = new _A('yanione');
      console.dir(new_AObject);
      ```      
      ![](/blog/assets/images/posts/20160203/class_15.png)
        
      <h2>정리</h2>
        
      - 클래스 표현식은 클래스명에 대한 <span style="color:#c11f1f">기명</span> / <span style="color:#c11f1f">익명</span> 표현이 가능하다.<br><br><br><br>     
      
    • 그밖의 테스트

      • 클래스 표현식EC 진입VO 의 새로운 속성으로 추가되지않는다.

          // ec 진입 시 VO 의 새로운 속성으로 추가되지않는다.
          try {
            console.log(A);
          }
          catch(e){
            console.log(e.message); // A is not defined
          }
        
        
          // 표현식에서 클래스명은 생략 가능하다.
          var _A = class A{
            ...
          }
        
          // 실행 코드 처리 후
          console.dir(window.A); // undefined
        
          // 선언된 _A 변수에 할당된다.
          console.log(window._A); // A class
        
      • 선언된 클래스를 (new 연산자를 생략 후) 호출 하면 에러가 발생한다.

          try{
              // 함수 호출
              A();
          }
          catch(e){
              // Class constructors cannot be invoked without 'new'
              console.log(e.message);
          }
        
        

    3. 접근자 선언

    • 접근자 선언

      ```javascript
      // 클래스 선언
      class A {
        
          constructor(id){
              // 접근자 사용을 위해 내부 속성인 __id__ 속성을 선언한다.
              this.__id__ = id;
          }
        
        
          // 접근자 생성 시 A.prototype 객체 내부에는 id 속성과 get/set 접근자 메서드가 생성되어있다.
          get id(){
              return this.__id__;
          }
        
          set id(value){
              value = value || '';
              this.__id__ = value;
          }
      }
        
      console.dir(A); // A class
        
      var newAObject = new A('yanione');
        
      console.dir(newAObject);
        
      // get
      console.log(newAObject.id); // yanione
        
      // set
      newAObject.id = 'yanione2';
        
      console.log(newAObject.id); // yanione2
        
      ```
        
      접근자 생성시 A.prototype 객체 내부에는 <span style="color:#c11f1f">id</span> 속성과 get/set <span style="color:#c11f1f">접근자</span> 메서드가 생성되어있다.
        
      ```javascript
      console.dir(A);
      ```
        
      ![](/blog/assets/images/posts/20160203/class_06.png)
        
      newAObject 객체 내부(newAObject 객체의 <span style="color:#c11f1f">인스턴스 맴버</span>로 <span style="color:#c11f1f">id</span> 속성이 추가된것을 볼 수 있다)     
        
      ```javascript
      var newAObject = new A('yanione');
      console.dir(newAObject);
      ```
        
      ![](/blog/assets/images/posts/20160203/class_07.png)<br><br><br><br>
      
    • ES5 인스턴스 맴버를 통한 접근자 선언

      ```javascript
      function A(__id__) {
                    
          // 객체(this) prototype chain 에 A.prototype 이 존재하는지 않는 경우         
          if (!(this instanceof A)) return;
        
          var id = __id__;
        
          // ES5 에서는 오직 Object.defineProperty 메서드를 통해서만 접근자를 생성할 수 있다.
          Object.defineProperty(this, 'id', {
              get: function() {
                  return id;
              },
              set: function(value) {
                  id = value;
              }
          });
      }
        
      var new_AObject = new A('yanione');
        
      console.log(new_AObject);
        
      console.log(new_AObject.id); // yanione
        
      new_AObject.id = 'yanione2';
      console.log(new_AObject.id); // yanione2
       
      ```
        
      newAObject 객체 내부(이 경우 선언된 **접근자 프로퍼티**에 대한, 모든 <span style="color:#c11f1f">접근자</span> 메서드들이 <span style="color:#c11f1f">인스턴스 맴버</span>로 포함되는 <span style="color:#c11f1f">단점</span>(메모리)이 존재한다)
             
      - 즉 <span style="color:#c11f1f">new</span> 연산자를 통해 생성되는, 모든 객체에 선언된 모든 <span style="color:#c11f1f">접근자</span> 메서드가 생성되며, 그로인해 메모리가 낭비되는 <span style="color:#c11f1f">단점</span>이 존재한다.
        
          ```javascript
          // new 연산자를 통해 생성되는 모든 객체(인스턴스)에 선언된, 접근자 메서드가 생성되며, 그로인해 메모리가 낭비되는 단점이 존재한다.
          var newAObject = new A('yanione');
          console.dir(newAObject);
            
          var newAObject2 = new A('yanione');
          console.dir(newAObject2);
           
          ```
            
          ![](/blog/assets/images/posts/20160203/class_09.png)<br><br><br><br>
      
    • ES5 프로토타입 맴버를 통한 접근자 선언

      ```javascript
      // A 함수 객체를 선언한다.
      function A() {
          if (!(this instanceof A)) return;
      }
        
      // A.prototype 객체에 id 속성에 대한 get/set 접근자 메서드를 생성한다.
      Object.defineProperty(A.prototype, 'id',{
          get: function(){
              // 내부 속성인 __id__ 속성을 통해 접근자에 접근한다.
              return this.__id__
          },
          set: function(value){
              this.__id__ = value;
          }
      });
        
        
      var newAObject = new A();
        
      console.dir(newAObject);
        
      newAObject.id = 'yanione';
      console.log(newAObject.id); // yanione
       
      ```
                                    
      Object.defineProperty 메서드를 통한, 접근자 생성시 A.prototype 객체 내부에는 <span style="color:#c11f1f">id</span> 속성과 get/set <span style="color:#c11f1f">접근자</span> 메서드가 생성되어있다.
        
      ```javascript
      console.dir(A);
      ```
        
      ![](/blog/assets/images/posts/20160203/class_0-7.png)
                                          
      newAObject 객체 내부(이 경우 ES6 에서의 <span style="color:#c11f1f">접근자 생성</span>과 같이, 선언된 모든 <span style="color:#c11f1f">접근자 메서드</span>들이 A.prototype 객체에 <span style="color:#c11f1f">할당</span>되며, 이전에 가졌던 (메모리 낭비에 대한)<span style="color:#c11f1f">단점</span>을 피할 수 있다)
        
      ```javascript
        
      var newAObject = new A();
      console.dir(newAObject);
      ```
      ![](/blog/assets/images/posts/20160203/class_0-6.png)<br><br><br><br>        
      

    4. 정적 메서드 선언

    • 정적 메서드 선언

      ```javascript
      // 클래스 선언
      class A {
        
          constructor(){
          }
        
          // 정적 메서드 선언
          static post(url){
              this.url = url;
          }
      }
        
      console.dir(A);
        
      // A.post 메서드는 function 데이터 타입을 반환한다.
      console.log(typeof A.post); // function
        
      // 선언된 정적 메서드는 해당 클래스의 새로운 속성으로 할당된다.
      console.dir(A.post); // post function object
        
      // A.post.constructor 속성은 Function (생성자)함수 객체를 참조하고있다.
      console.log(A.post.constructor); // Function function object
        
      // A.post 정적 메서드는 prototype 객체 프로퍼티를 갖지않는다.
      console.log(A.post.prototype); // undefined
        
      // (생성자)함수 객체로 호출 시 아래와 같은 에러가 발생한다.
      try {
          console.log(new A.post('http://mohwa.com'));
      }
      catch(e){
          /*
           Uncaught TypeError: post(url){
           this.url = url;
           } is not a constructor
          */
          console.log(e.message);
      }
      ```
        
      ![](/blog/assets/images/posts/20160203/class_10.png)<br><br><br><br>                  
      
    • ES5 를 통한 정적 메서드 선언

      ```javascript
      function A(id) {
      }
        
      A.post = function(){
      };
        
      console.dir(A); // A function object
        
      console.log(A.post); // post function object
        
      // (생성자)함수 객체로 호출 시 새로운 객체가 생성된다.
      var newAPostObject = new A.post('http://mohwa.com');
      console.dir(newAPostObject);
        
      ```
        
      A 함수 객체 내부
        
      ![](/blog/assets/images/posts/20160203/class_11.png)<p>
        
      newAPostObject 객체 내부
        
      ![](/blog/assets/images/posts/20160203/class_0-3.png)<br><br><br><br>
      

    5. 제네레이트 메서드 선언

    • 제네레이트 메서드 선언

      ```javascript
      // 클래스 선언
      class A {
        
          constructor(){
          }
        
          // 제네레이터 메서드 선언
          * post(){
              yield 1;
              yield 2;
              yield 3;
          }
      }
        
      console.dir(A);
        
      // 선언된 제네레이터 메서드는 A 클래스의 prototype 객체 프로퍼티에 할당된다.
      console.dir(A.prototype.post); // post function object
        
      // 제네레이터 메서드의 (생성자)함수 객체로 GeneratorFunction 함수 객체를 반환한다.
      console.dir(A.prototype.post.constructor); // GeneratorFunction function object 
        
      var newAObject = new A;
      var generator = newAObject.post();
        
      console.dir(newAObject);
        
      console.log(generator.next()); // 1
      console.log(generator.next()); // 2
      console.log(generator.next()); // 3
      console.log(generator.next().done); // true
      ```
        
      <span style="color:#c11f1f">A</span> 클래스 내부(선언된 제네레이터 메서드는 A 클래스의 prototype 객체 프로퍼티에 할당된다)
        
      ![](/blog/assets/images/posts/20160203/class_0-5.png)<p>
                     
      newAObject 객체 내부(제네레이터 메서드의 (생성자)함수 객체로 <span style="color:#c11f1f">GeneratorFunction</span> 함수 객체를 반환한다)
        
      ![](/blog/assets/images/posts/20160203/class_0-4.png)<p>                   
      

    6. 클래스 상속

    • ES6 에서는 extends 절 및 super 키워드를 통해 상속을 구현할 수 있다.

      ```javascript
      function A(id){
          // 이 경우 this 는 new C 를 통해 생성된 객체를 가리킨다.
          this.id =  id;
      }
        
      A.prototype.getId = function(){
          return this.id;
      };
        
      // 클래스가 아닌 함수 객체에 대한 상속도 가능하다.
      class B extends A {
        
          constructor(id, name){
        
              // super 키워드를 통해 인스턴스 맴버 초기화 및 위임 과정이 발생한다.
              super(id);
      
              // 이 경우 this 는 new C 를 통해 생성된 객체를 가리킨다.
              this.name = name;
          }
        
          getName(){
              return this.name;
          }
      }
        
      // 클래스 상속
      class C extends B {
        
          constructor(id, name, age){
        
              // super 키워드를 통해 인스턴스 맴버 초기화 및 위임 과정이 발생한다.
              super(id, name);
        
              // 이 경우 this 는 new C 를 통해 생성된 객체를 가리킨다.
              this.age = age;
          }
        
          getAge(){
              return this.age;
          }
      }
        
      console.log(new C('yanione', 'mohwa', 35));
      ```
      
    • **chrome 48 버전**의 결과
      ![](/blog/assets/images/posts/20160203/class_12.png)<br><br><br><br>
      
    • chrome 49 버전을 통해 다시 확인해본결과, 이전 결과와 달라진것을 볼 수 있다.

      • 인스턴스 이름A ==> C 로 바뀐것은 맞는듯한데, 그 하위의 prototype chain 이름들이 상이?한듯 하다.(즉 C 인스턴스의 __proto__ 속성이 C.prototype 이 아닌, B.prototype 을 참조하고 있다고 출력하고 있다.(하지만 보여지는 prototype 객체 내부에는, C 클래스의 프로토타입 메서드인 getAge 를 포함하고 있는것을 볼 수 있다))





    • ES5 를 통해 클래스 상속을 구현해본 예(ES6 에서의 super 키워드는 아래 this.\_\_proto\_\_.constructor.call 과 같은 일종의 매크로 구현으로 볼 수 있다)

      ```javascript
      function A(id){
          this.id =  id;
      }
        
      A.prototype.getId = function(){
          return this.id;
      };
        
        
      function B(id, name){
        
          // call 메서드를 통해 A (생성자)함수 객체(this.__proto__.__proto__.__proto__)가 가진 인스턴스 맴버를 초기화한다.
          this.__proto__.__proto__.__proto__.constructor.call(this, id);
        
          this.name =  name;
      }
        
      // B.prototype 객체 프로퍼티에 A.prototype (원형)객체를 포함한 새로운 객체를 할당한다.
      B.prototype = Object.create(A.prototype);
      B.prototype.getName = function(){
          return this.name;
      };
        
      B.prototype.constructor = B;
        
      function C(id, name, age){
        
          id = id || '';
          name = name || '';
          age = age || 0;
        
          // call 메서드를 통해 B (생성자)함수 객체(this.__proto__.__proto__)가 가진 인스턴스 맴버를 초기화한다.
          this.__proto__.__proto__.constructor.call(this, id, name);
        
          this.age = age;
      }
        
      // C.prototype 객체 프로퍼티에 B.prototype (원형)객체를 포함한 새로운 객체를 할당한다.
      C.prototype = Object.create(B.prototype);
      C.prototype.getAge = function(){
          return this.age;
      };
        
      C.prototype.constructor = C;
        
        
      var newCObject = new C('yanione', 'mohwa', 35);
        
      console.log(newCObject.getId()); // yanione
      console.log(newCObject.getName()); // mohwa
      console.log(newCObject.getAge()); // 35
        
      console.log(newCObject);
      ```
            
      ![](/blog/assets/images/posts/20160203/class_13.png)<br><br><br><br>
      
    • 파생 클래스constructor(생성자) 내부에서는 반드시 super 키워드가 호출되어야한다.

      ```javascript
      function A(id){
        
          this.id =  id;
      }
        
      A.prototype.getId = function(){
        return this.id;
      };
        
      // 클래스가 아닌 함수 객체에 대한 상속도 가능하다.
      class B extends A {
        
          constructor(id, name){
        
              // 파생 클래스의 constructor 내부에서는 super 키워드만을 사용(호출)하거나, this 키워드 사용전에 반드시 호출되어야한다.
              // 즉 constructor 내부에서는 super 키워드가 반드시 호출되어야한다.
          }
        
          getName(){
              return this.name;
          }
      }
        
      try{
          console.log(new B('yanione', 'mohwa'));
      }
      catch(e){
          // 아래와 같은 예외가 발생한다.
          console.log(e.message); // this is not defined
      }
      ```
      
    • super 키워드가 this 처리 후에 호출될 경우, 예외가 발생된다.(반드시 this 사용전에 호출되어야한다)

      ```javascript
      function A(id){
          this.id =  id;
      }
        
      A.prototype.getId = function(){
          return this.id;
      };
        
      // 클래스가 아닌 함수 객체에 대한 상속도 가능하다.
      class B extends A {
        
          constructor(id, name){
        
              // 반드시 this 사용전에 호출되어야한다.
              // super(id);
                
              try{
                  this.name = name;
              }
              catch(e){
                  // Uncaught ReferenceError: this is not defined
                  console.log(e.message);
              }
        
              // 이 경우 super 키워드는 처리되지 않는다.
              super(id);
          }
        
          getName(){
              return this.name;
          }
      }
        
      console.log(new B('yanione', 'mohwa'));
      ``` 
      
    • super 키워드를 통해, 부모 클래스에 선언된 static method 에 접근 가능하다.

      ```javascript     
      let A = class A {
        
        constructor() {
        }
          
        static x(){
          console.log('x method');
        }
      }
        
      let B = class B extends A {
        
        constructor(length) {
          
          // 파생 클래스의 constructor 내부에서는 반드시 super 키워드가 호출되어야한다.
          super();
        }
        
        static y() {
        
          // static 메서드 내부 "this" 는 "B class" 와 동일하다.
          console.log(this); // B class
        
        
          // "super" 는 "this.__proto__(A class)" 와 동일하다.
        
          // 즉 this.__proto__.x() 메서드를 통해 호출 가능하다.
          this.__proto__.x(); // x method
            
          // super 를 통한 접근
          super.x(); // x method
        }
      }
        
      // var _B = new B();
      B.y();
      ``` 
      
    • ES5 를 통해, 위 super 키워드의 특성을 구현해본 예

      ```javascript         
      // A 함수 객체를 생성한다.
      function A(){
      }
        
      // A 함수 객체의 x static method 를 생성한다.
      A.x = function(){
          console.log('x method');
      };
        
      // B 함수 객체를 생성한다.
      function B(){
      }
        
      // B 함수 객체의 y static method 를 생성한다.
      B.y = function(){
          // this(B function object).__proto__(A function object).x 메서드에 접근 가능하다.
          this.__proto__.x(); // === super.x();
      };
        
      // B 함수 객체의 __proto__ 속성은 A 함수 객체를 참조하고있다.
      B.__proto__ = A;
        
      B.y(); // x method
      ```  
      
    • super 키워드를 통해, 부모 클래스에 선언된 prototype method 에 접근 가능하다.

          let A = class A {
        
            constructor(id) {
        
              this.id = id;
            }
        
            x(){
              console.log('x method');
            }
          }
        
          let B = class B extends A {
        
            constructor(id, name) {
        
              super(id);
        
              this.name = name;
            }
        
            y() {
        
              // this.__proto__(B.prototype).__proto__(A.prototype).x()
              this.__proto__.__proto__.x(); // x method
        
              // super 키워드는 this.__proto__.__proto__ 와 같다.
              super.x(); // === this.__proto__.__proto__.x() === x method
            }
          }
        
        
          let _B = new B('yanione', 'mohwa');
        
          console.dir(_B); // {id: "yanione", name: "mohwa"}
        
          _B.y();
      
    • ES5 를 통해, 위 super 키워드의 특성을 구현해본 예

      ```javascript         
      // A 함수 객체를 생성한다.
      function A(id){
        
          this.id = id;
      }
        
      // A 함수 객체의 x static method 를 생성한다.
      A.prototype.x = function(){
          console.log('x method');
      };
        
      // B 함수 객체를 생성한다.
      function B(id, name){
        
          // this.__proto__.__proto__.constructor(A function object)
          this.__proto__.__proto__.constructor.call(this, id);
          this.name = name;
      }
        
      // B.prototype 에, A.prototype 을 원형으로 갖는 새로운 객체를 할당한다.
      B.prototype = Object.create(A.prototype);
        
      // y prototype method 를 생성한다.
      B.prototype.y = function(){
        
          // this(B instance).__proto__(B.prototype).__proto__(A.prototype).x
          this.__proto__.__proto__.x(); // === super.x 와 같다 === x method
      };
        
      B.prototype.constructor = B;
        
      var _B = new B('yanione', 'mohwa');
        
      console.log(_B); // {id: "yanione", name: "mohwa"}
        
      _B.y();
      ```      
      

    Read more


  • javascript 

    1. 글에 대해

    • 이 글은 (JS 위임 과정의 이해를 돕기 위한 글이 아닌)최근 시작한 ES567 스터디 그룹의 연구 과제를 위해 작성된 글이다.

      • 그 이해를 돕기위한 내용은 이 을 참고할 수 있다.<p>
    • 즉 글 전반적으로 서술된 글이 아닌, 테스트 코드와 그에 대한 주석을 통해 설명하고있다.

    2. 위임(과정) 이란?

    • 결국 객체들간의 원형 복제 과정을 말한다.(이 말을 잘 기억해두길 바란다)

      • (생성자)함수 (객체)를 통한 위임 과정

        ```javascript
        // global execution context
            
        // A 함수 객체를 선언한다.
        function A(){
            
            // function execution context
            
        }
            
        // A 함수 객체의 prototype 프로퍼티로 포함된 원형 객체(이후 newAObject 객체로 위임될 객체이기도 하다)
        console.log(A.prototype);
            
        // new 연산자와 A (생성자)함수 (객체)를 통해 새로운 객체가 생성된다.
        var newAObject = new A;
            
        // newAObject 객체의 __proto__ (내부)속성은, A.prototype (원형)객체를 참조하고있다.
        console.log(newAObject.__proto__ === A.prototype); // true
            
        // newAObject 객체의 (생성자)함수인 A 함수 (객체)를 반환한다.
        console.log(newAObject.constructor); // A function object
        console.log(newAObject.constructor === A.prototype.constructor); // true
            
        // newAObject 객체의 prototype chain 에는 A.prototype 객체가 존재하므로 아래 코드를 통해 true 를 반환하게 된다.
            
        // instanceof 연산자는 왼쪽 파라메터로 전달된 객체(newAObject)의 prototype chain 내부에, 오른쪽 파라메터로 전달된 함수 객체(A)의 prototype 프로퍼티가 존재한다면 true 를 반환하게된다.
        console.log(newAObject instanceof A); // true
            
        console.dir(newAObject);
        ```
            
        ![](/blog/assets/images/posts/20160203/class_00.png)
                        
        - 정리
            
          - 선언된 A 함수 객체는 <span style="color:#c11f1f">prototype</span> 이라는 (원형)<span style="color:#c11f1f">객체</span>를 가지고 있다.<p>
              
          - <span style="color:#c11f1f">new</span> 연산자를 통해 객체 생성시, A.prototype 객체는 newAObject 객체의 \_\_proto\_\_ (내부)속성으로 <span style="color:#c11f1f">위임</span>된다.<p>
              
          - 예상했던 결과와 같이, newAObject.constructor 속성은 (생성자)함수인 A 함수 객체를 참조하고 있다.<p>
              
        [instanceof 연산자](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof)<br><br><br><br>
        
      • 객체(Object)를 통한 위임 과정

        ```javascript
        // A 객체를 생성한다.
        var A = {
            id: '',
            getId: function(){
                return this.id;
            },
            setId: function(id){
                this.id = id;
            }
        };
            
        // A.id 값을 초기화한다.
        A.setId('yanione');
            
        // B 함수 객체를 선언한다.
        function B(name){
            
            name = name || '';
            
            this.name = name;
        }
            
        // B.prototype 객체 프로퍼티에 getName 메서드를 할당한다.
        B.prototype.getName = function(){
            return this.name;
        };
            
        // B.prototype 객체가 가진 맴버들을, 위임할 객체(_A Object)에 추가하기위해, (임시)변수에 저장해둔다.
        var __prototype__ = B.prototype;
            
        // 위임할 A 객체를 복제한다.
        var _A = {
            id: A.id,
            getId: A.getId
        };
            
        // 복제된 _A 객체를 B.prototype 객체 프로퍼티로 위임한다.
        B.prototype = _A;
            
        // __prototype__ 변수에 저장된 객체 맴버를, B.prototype 객체로 재 할당한다.
        B.prototype.getName = __prototype__.getName;
            
        //B.prototype.constructor = B;
            
        // newBObject 객체를 생성한다.
        var newBObject = new B('mohwa');
            
        // newBObject.__proto__ 속성은 B.prototype 객체를 참조하고 있다.
        console.log(newBObject.__proto__ === B.prototype); // true
            
        // B.prototype 객체 프로퍼티는 복제된 _A 객체를 참조하고 있다.
        console.log(newBObject.__proto__ === _A); // true
            
        console.log(newBObject.getId()); // yanione
        console.log(newBObject.getName()); // mohwa
            
        // instanceof 연산자 테스트
        try {
            // instanceof 연산자의 오른쪽 파라메터에는 반드시 함수 객체가 위치해야한다.
            console.log(newBObject instanceof _A); // error
        }
        catch(e){
            // 에러가 발생한다.
            console.log(e.message); // Expecting a function in instanceof check, but got #<Object>
        }
            
        // newBObject 객체의 prototype chain 에는 B.prototype 객체가 존재하므로 아래 코드를 통해 true 를 반환하게 된다.
        console.log(newBObject instanceof B); // true
            
        // _A 객체의 위임 과정으로인해, newBObject.constructor 속성은 Object 함수 객체를 반환하게된다.
        console.log(newBObject.constructor); // Object function object
        ```
            
        ![](/blog/assets/images/posts/20160203/class_01.png)
            
        - 정리
            
          - <span style="color:#c11f1f">_A 객체</span>의 <span style="color:#c11f1f">위임</span> 과정으로인해, newBObject.constructor 속성은 (생성자)함수인 B 함수 객체가 아닌, <span style="color:#c11f1f">Object (생성자)함수 객체</span>를 참조하게된다.
            
          - 위 상황을 <span style="color:#c11f1f">우회</span>(명확한 해결책은 아닌)하기 위해, 아래와 같은 코드를 추가할 수 있다.
            
             ```javascript
             // B.prototype 객체에 constructor 속성을 추가시킨다.
             B.prototype.constructor = B;
             ```                
          <br><br><br><br>
        
      • new 연산자를 통한 위임 과정

        ```javascript
        // A 함수 객체를 선언한다.
        function A(id){
            
            id = id || '';
            
            this.id = id;
        }
            
        A.prototype.getId = function(){
            return this.id;
        };
            
            
        // B 함수 객체를 선언한다.
        function B(name){
            
            name = name || '';
            
            this.name = name;
        }
            
        B.prototype.getName = function(){
            return this.name;
        };
            
        var __prototype__ = B.prototype;
            
        var newAObject = new A('yanione');
            
        // newAObject 객체를 B.prototype 객체 프로퍼티에 할당한다.
        B.prototype = newAObject;
            
        //B.prototype.constructor = B;
            
        B.prototype.getName = __prototype__.getName;
            
        var newBObject = new B('mohwa');
            
        // newBObject.__proto__.__proto__ 속성은 A.prototype 객체를 참조하고있다.
        console.log(newBObject.__proto__.__proto__ === A.prototype); // true
            
        // 즉 newBObject.__proto__.__proto__(A.prototype) 속성은 newAObject.__proto__(A.prototype) 속성과 동일하다.
        console.log(newBObject.__proto__.__proto__ === newAObject.__proto__); // true
            
        console.log(newBObject.getId()); // ''
        console.log(newBObject.getName()); // mohwa
            
        // newBObject 객체의 prototype chain 에는 B.prototype 객체가 존재하므로 true 를 반환하게된다.
            
        // newBObject.__proto__ === B.prototype
        console.log(newBObject instanceof B); // true
            
            
        // newBObject 객체의 prototype chain 에는 A.prototype 객체도 존재하므로 true 를 반환하게된다.
            
        // newBObject.__proto__.__proto__ === A.prototype
        console.log(newBObject instanceof A); // true
            
        // new A 객체의 위임 과정으로인해 newBObject.constructor 속성은 A 함수 객체를 참조하게된다.
        console.log(newBObject.constructor); // A function object
            
        console.log(newBObject);
        ```
            
        ![](/blog/assets/images/posts/20160203/class_02.png)
            
        - 정리
            
          - newBObject 객체 prototype chain 에는 <span style="color:#c11f1f">A.prototype 객체</span>와 <span style="color:#c11f1f">B.prototype 객체</span> 프로퍼티가 모두 존재하며, <span style="color:#c11f1f">instanceof 연산자</span>를 통해 모두 <span style="color:#c11f1f">true</span> 를 반환하게된다.<p>
            
          - <span style="color:#c11f1f">new A</span> 객체의 <span style="color:#c11f1f">위임</span> 과정으로인해, newBObject.constructor 속성은 B (생성자)함수 객체가 아닌, <span style="color:#c11f1f">A 함수</span> 객체를 참조하게된다.<br><br><br><br>
        
      • Object.create 메서드를 통한 위임 과정

        ```javascript
        // A 함수 객체를 선언한다.
        function A(id){
            
            id = id || '';
            
            this.id = id;
        }
            
        A.prototype.getId = function(){
            return this.id;
        };
            
            
        // B 함수 객체를 선언한다.
        function B(name){
            
            name = name || '';
            
            this.name = name;
        }
            
        B.prototype.getName = function(){
            return this.name;
        };
            
        var __prototype__ = B.prototype;
            
        // Object.create 메서드를 통해 A.prototype 객체가 위임된, 새로운 객체가 생성된다.
        /*
            
            {
               __proto__: A.prototype
            }
        */
        var newObject = Object.create(A.prototype);
            
        // B.prototype 객체에 Object.create 메서드를 통해 생성된 객체를 할당한다.
        B.prototype = newObject;
            
        //B.prototype.constructor = B;
            
        B.prototype.getName = __prototype__.getName;
            
        var newBObject = new B('mohwa');
            
        console.log(newBObject.getId()); // undefined
        console.log(newBObject.getName()); // mohwa
            
        // Object.create 메서드를 통한 위임 과정으로인해, newBObject.constructor 속성은 A 함수 객체를 참조하게된다.
        console.log(newBObject.constructor); // A function object
            
        console.log(newBObject);
        ```
            
        ![](/blog/assets/images/posts/20160203/class_03.png)
            
        - 정리
            
          - <span style="color:#c11f1f">Object.create</span> 메서드를 통해 <span style="color:#c11f1f">new</span> 연산자를 통해서만 가능했던 <span style="color:#c11f1f">위임</span> 과정을 구현했다.<p>
              
          - <span style="color:#c11f1f">Object.create</span> 메서드를 통한 위임 과정으로인해, newBObject.constructor 속성은 B (생성자)함수 객체가 아닌, <span style="color:#c11f1f">A 함수</span> 객체를 참조하게된다.<p>
                                                                               
          - <span style="color:#c11f1f">Object.create</span> 메서드를 통한 위임 과정으로인해, 인스턴스 맴버인 <span style="color:#c11f1f">id</span> 속성을 초기화하지 못했다.(<em>반드시 <span style="color:#c11f1f">new</span> 연산자를 통해서만 인스턴스 맴버를 초기화할 수 있다</em>)<br><br><br><br>
        
      • (인스턴스 맴버를 초기화하는)Object.create 메서드를 통한 위임 과정

        ```javascript
        // A 함수 객체를 선언한다.
        function A(id){
            
            id = id || '';
            
            this.id = id;
        }
            
        A.prototype.getId = function(){
            return this.id;
        };
            
            
        // B 함수 객체를 선언한다.
        function B(id, name){
            
            id = id || '';
            name = name || '';
            
            this.name = name;
            
            // call 메서드를 통해 A (생성자)함수 객체가 가진 인스턴스 맴버를 초기화한다.
            A.call(this, id);
        }
            
        B.prototype.getName = function(){
            return this.name;
        };
            
        var __prototype__ = B.prototype;
            
        var newObject = Object.create(A.prototype);
            
        // B.prototype 객체에 Object.create 메서드를 통해 생성된 객체를 할당한다.
        B.prototype = newObject;
            
        //B.prototype.constructor = B;
            
        B.prototype.getName = __prototype__.getName;
            
        var newBObject = new B('yanione', 'mohwa');
            
        console.log(newBObject.getId()); // yanione
        console.log(newBObject.getName()); // mohwa
            
        console.log(newBObject);
        ```
            
        ![](/blog/assets/images/posts/20160203/class_04.png)
            
        - 정리
            
          - <span style="color:#c11f1f">call</span> 메서드를 통해 <span style="color:#c11f1f">B</span> (생성자)함수 객체 호출 시 생성된 <span style="color:#c11f1f">인스턴스 맴버</span>로, <span style="color:#c11f1f">A</span> (생성자) 함수 객체의 <span style="color:#c11f1f">인스턴스 맴버</span>인 <span style="color:#c11f1f">id</span> 속성이 초기화되었다.<p>                                               
        

    관련 URL

    Read more


  • javascript 
    • JQuery UI 에서 제공하고 있는 widgets 중 하나인 (Util)Selectmenu 를 구현한 예제이다.

      • 이전 Menu UI 와의 (구현 상)가장 큰 차이점으로는 (대치될)메뉴를 위한 사용자 정의 태그가 따로 존재하며, 또 그것을 바탕으로한 새로운 엘리먼트 영역을 구현한다는 점이다.<p>

      • 전 처리 과정을 통해, 사용자가 정의한 Semantic(의미 있는) 태그를 새로운 태그로 대치시킨다는 말이다.<p>

      • 또한, 대치될 Selectmenu 엘리먼트의 focus 이벤트 처리를 위해, 최상단 엘리먼트($('.select-menu-container'))에 tabindex="0" 속성 값을 할당한다.(blur 이벤트를 사용하기 위한 구현)

    • 구현 1(각 메뉴 영역을 border 값으로 구분지은 구성)

    • 구현 2(각 메뉴 영역을 구분짓지않는 패턴(이는 요구 사항에 따라 얼마든지 변경될 수 있다))

    Read more