• javascript 
    • Execution Context 와 ECStack 내부

      ```javascript
          
        // global execution context
      
        function A(){
            
          // A 함수 선언식 내부
          // function execution context
          var x = 1; // AO.x
      
          function B(){
      
            // B 함수 선언식 내부
            // active function execution context
            var y = 2; // AO.y
          }
      
          B(); // AO.B() == null.B()
        }
      
        A(); // this.A();
      ```
      
    • ECStack 내부

      ```javascript
      var ECStack = [
        // B function execution context
        <B> activeFunctionExecutionContext: { 
          AO(VO): {
            y: 2
          },
          Scope(Scope Chain): [
            AO(VO): {
              y: 2
            },     
            <A> functionExecutionContext.AO(VO): {
              x: 1,
              B: < reference to function >
            },                         
            globalExecutionContext.VO: {
              A: < reference to function >
            }
          ]
        },
        // A function execution context
        <A> functionExecutionContext: {
          AO(VO): {
            x: 1,
            B: < reference to function >
          },
          Scope(Scope Chain): [
            AO(VO): {
              x: 1,
              B: < reference to function >
            },
            globalExecutionContext.VO: {
              A: < reference to function >
            }
          ]          
        },
        // global execution context
        globalExecutionContext: {
          VO: {
            A: < reference to function >
          },
          Scope(Scope Chain): [
            globalExecutionContext.VO
          ]
        }
      ];
      ```
      
    • Global Execution Context 의 Scope Chain

      • Global Execution Context 내부에 생성되는 Scope Chain 은 globalExecutionContext.VO 만 포함한다.

            globalExecutionContext:{
              VO: ...
              Scope(Scope Chain): [
                globalExecutionContext.VO
              ]
            }
        
    • 함수 생명 주기 와 Function Execution Context 의 Scope Chain

      • 함수 생명 주기는 함수 생성함수 호출로 나눌 수 있다.

      • 함수 생성

        • 함수 생성 시 [[Scope]] property 가 생성된다.<p>

        • [[Scope]] property 는 직접적인 접근이 불가능하며, 초기화된 값은 변경되지 않는다.<p>

        • [[Scope]] property 는 해당 Function Execution Context 상위에 있는 모든 부모 계층의 VO 를 갖는다.<p>

        • A 함수를 선언한다

          
            // global execution context
          
            function A(){
          
            }
          
          
        • 함수 생성 시 함수 객체 내부

            A: {
              [[Scope]]: [
                // A 함수는 [[Scope]] property 를 통해 global execution context 의 VO 를 갖는다.
                globalExecutionContext.VO
              ];
            }
          
        • 함수 내부에 또 다른 함수 생성

          
            // global execution context
          
            // 변수 선언 VO.x
            var x = 1;
          
            function A(){
          
              // function execution context
              // 변수 선언 AO.y
              var y = 1;
          
              function B(){
          
                // function execution context
              }
            }
          
            A();
          
          
        • 각 함수 객체 내부

              A: {
                [[Scope]]: [
                  // A 함수는 global execution context 의 VO 를 갖는다.
                  globalExecutionContext.VO
                ];
              }
          
              B: {
                [[Scope]]: [
                  // B 함수는 global execution context 의 VO 와 부모 함수인 A 함수의 AO(VO)를 갖는다.
                  <A> functionExecutionContext.AO(VO),
                  globalExecutionContext.VO
                ];
              }
          
          
      • 함수 호출

        • 함수 호출 시, Function Execution Context 내부에 Scope Chain 이 추가된다.<p>

        • Scope Chain 은 함수 [[Scope]] property 를 그대로 가져가 초기화되며, Function Execution Context 내부 AO(VO)는 Scope Chain**가장 앞**에 추가된다.

                  
              // global execution context
                  
              var x = 1;
                  
              function A(){
                  
                // function execution context
                  
                var y = 2;
              }
                  
              A(); // call A function object
          
        • ECStack 내부

                      
              var ECStack = [
                // A function execution context
                <A> functionExecutionContext: {
                  AO(VO): {
                    y: 2
                  },
                  Scope(Scope Chain): [
                    // 함수 호출 시, 생성된 AO 는 Scope Chain 의 가장 앞(ScopeChain[0])에 추가되며, 식별자 검색 시 가장 먼저 검색된다.
                    AO(VO): {
                      y: 2
                    },                  
                    globalExecutionContext.VO: {
                      x: 1,
                      A: < reference to function >
                    }
                  ]
                },
                globalExecutionContext: {
                  VO: {
                    x: 1,
                    A: < reference to function >								
                  },
                  Scope(Scope Chain): [
                    globalExecutionContext.VO
                  ]                  
                }
              ];
          
      • 식별자 해석 과정

        • 식별자 검색 순서는 **Scope Chain** 의 가장 **바닥**(ScopeChain[0])에서부터, 가장 **상위**(ScopeChain[scope.length]) 까지 검색 후, 그 값을 반환한다.

        • 즉, 이와같은 검색 매커니즘에 의해, 해당 함수가 가진 AO(VO) 의 속성을 가장 빠르게 접근할 수 있으며, Global Execution Context 내부 VO 의 속성에 접근하는것이 가장 느린것이다.

                  
              // global execution context
                  
              var x = 1;
                  
              function A(){
                  
                // function execution context
                  
                var y = 2;
                          
                // functionExecutionContext.scopeChain.globalExecutionContext.VO.x                
                console.log(x); // 1
                // functionExecutionContext.scopeChain.AO.y
                console.log(y); // 2
              }
                  
              A();
          
        • ECStack 내부

              var ECStack = [
                <A> functionExecutionContext: {
                  AO(VO): {
                    y: 2
                  },
                  Scope(Scope Chain): [
                    AO(VO): {
                      y: 2
                    },                  
                    globalExecutionContext.VO: {
                      x: 1,
                      A: < reference to function >
                    }
                  ]
                },
                globalExecutionContext: {
                  VO: {
                    x: 1,
                    A: < reference to function >								
                  },
                  Scope(Scope Chain): [
                    globalExecutionContext.VO
                  ]                  
                }
              ];
          
        • 만약, 각 Execution Context 에 선언된 식별자 이름이 같은 경우, 위에서 언급한 Scope Chain검색 순서로 해당 값이 정해지게 된다.

        • BFunctionExecutionContext.AO.y

                      
            // global execution context
            var y = 1;
                
            function A(){
                
              // function execution context
                        
              var y = 2;
                
              function B(){
                
                // active function execution context
                
                var y = 3;
                          
                // BFunctionExecutionContext.AO.y
                console.log(y); // 3
              }
                
              B(); // AO.B() === null.B();
            }
                
            A();
          
        • AFunctionExecutionContext.AO.y

                  
              // global execution context
                        
              var y = 1;
                  
              function A(){
                  
                // function execution context
                  
                var y = 2;
                  
                function B(){
                  
                  // active function execution context
                        
                  // B 함수에 선언된 y 변수를 삭제한다.
                  // var y = 3;
                  // AFunctionExecutionContext.AO.y
                  console.log(y); // 2
                }
                  
                B(); // AO.B() === null.B();
              }
                  
              A();
          
        • globalExecutionContext.VO.y

                      
              // global execution context
              var y = 1;
                  
              function A(){
                  
                // function execution context
                          
                // A 함수에 선언된 y 변수를 삭제한다.
                // var y = 2;
                  
                function B(){
                  
                  // active function execution context
                  // B 함수에 선언된 y 변수를 삭제한다.
                  // var y = 3;
                  
                  // globalExecutionContext.VO.y
                  console.log(y); // 1
                }
                  
                B(); // AO.B() === null.B();
              }
                  
              A();
          
        • ECStack 내부

          ```javascript
          var ECStack = [
            
            <B> activeFunctionExecutionContext: {
              AO(VO): {
                y: 3
              },
              Scope(Scope Chain): [
                AO(VO): {
                  y: 3
                },                
                <A> functionExecutionContext.AO(VO): {
                  y: 2,
                  B: < reference to function >
                },                
                globalExecutionContext.VO: {
                  y: 1,
                  A: < reference to function >
                }
              ]
            },
            <A> functionExecutionContext: {
              AO(VO): {
                y: 2,
                B: < reference to function >
              },
              Scope(Scope Chain): [
                AO(VO): {
                  y: 2,
                  B: < reference to function >
                },                
                globalExecutionContext.VO: {
                  y: 1,
                  A: < reference to function >
                }
              ]
            },
            globalExecutionContext: {
              VO: {
                y: 1,
                A: < reference to function >
              },
              Scope(Scope Chain): [
                globalExecutionContext.VO
              ]                
            }
          ];
          ```
          
        • 또 다른 Scope Chain 예

          ```javascript
              
          // global execution context
            
          var num2 = 5;
            
          function add(num1, num2){
            
            // function execution context
                      
            // result 변수는 undefined 로 초기화된다.
            console.log(result); // undefined
                    
            // functionExecution.AO.num1
            // functionExecution.AO.num2
            return num1 + num2;
          };
                  
          var result = add(5, 10);
                  
                  
          // 실행 코드 처리 후 result 변수에 15 가 할당된다.
          console.log(result); // 15
          ``` - <span style="color:#c11f1f">ECStack</span> 내부
                
            ```javascript
            var ECStack = [
              <add> functionExecutionContext: {
                AO(VO): {
                  arguments: {
                    0: 5,
                    1: 10
                  },
                  num1: 5,
                  num2: 10
                },
                Scope(Scope Chain): [
                  AO(VO): {
                    arguments: {
                      0: 5,
                      1: 10
                    },
                    num1: 5,
                    num2: 10
                  },                  
                  globalExecutionContext.VO: {
                    result: undefined,
                    add: < reference to function >,
                    num2: 5
                  }
                ]
              },
              globalExecutionContext: {
                VO: {
                  result: undefined,                  
                  add: < reference to function >,
                  num2: 5
                },
                Scope(Scope Chain): [
                  globalExecutionContext.VO
                ]                  
              }
            ];
            ```
          
    • JS 에서 말하는 Closure란? 별도의 개념이 아닌, **Scope Chain 매커니즘**에 의한 바인딩 환경을 말하는것이다.<p>

      • 일반적인 Closure 환경

        ```javascript    
        // global execution context
              
        var x = 1;
        function A(){
              
            // function execution context;
              
            // 식별자 검색을 통해, 상위 계층의 VO 에 접근하여 결과를 반환한다.
            // AFunctionExecutionContext.scopeChain[1].VO.x
            console.log(x); // 1
        }
              
        A();
        ```          
        
      • 하지만 Closure 환경으로 인해, 메모리 누수가 발생할 수 있다.

        • makeAdder 함수 호출 시, 반환받은 add5 함수 [[Scope]] 속성에는, 모든 부모 계층의 VO, AO(VO)영구적으로 포함된다.

        • 즉 makeAdder 함수(상위 계층) 종료 시, 소멸되는 AO(VO) 가, 해당 [[Scope]] 속성 내부에 영구적으로 보관된다는 것이다.(메모리 누수의 원인)

          ```javascript
                    
          // global execution context
                    
          function makeAdder(x) {
                      
            // function execution context
                      
            // 반환되는 익명함수에는 Scope Chain 매커니즘에 의해 부모 계층의 모든 VO 가 포함되어있다.
            // 부모 계층의 AO(VO)({x: 5})
            return function(y) {
                      
              // function execution context
              // Scope Chain 의 식별자 검색을 통해 부모 계층의 Vo.x 속성을 사용한다.
                        
              // anonymousFunctionExecutionContext.scopeChain.makeAdderFunctionExecutionContext.AO.x
              // anonymousFunctionExecutionContext.scopeChain.AO.y
              return x + y;
            };
          }
                    
          var add5 = makeAdder(5);
                    
          print(add5(2));  // 7
               
          ```
          
        • anonymousFunction(= add5) 함수 [[Scope]] 속성 내부

           ```javascript
           anonymousFunction(= add5): {
            [[Scope]]: [
              <makeAdder> functionExecutionContext.AO(VO): {
                arguments: {
                  0: 5                
                },
                x: 5
              },
              globalExecutionContext.VO: {
                makeAdder: < reference to function >,
                add5: undefined
              }
            ]
          }
          ```
          
        • anonymousFunction(= add5) 함수 Scope Chain 내부

           ```javascript
           anonymousFunction(= add5): {
            Scope(Scope Chain): [
              AO: {
                arguments: {
                  0: 2                
                },
                y: 2                
              },
              <makeAdder> functionExecutionContext.AO(VO): {
                arguments: {
                  0: 5                
                },
                x: 5
              },
              globalExecutionContext.VO: {
                makeAdder: < reference to function >,
                add5: < reference to function >
              }
            ]
          }
          ```    
          
    • Eval Execution Context 의 Scope Chain

      • eval execution context 의 Scope Chain 은 calling context 의 Scope Chain 을 따라간다.

      • eval 함수가 포함된 execution context 의 Scope Chain 을 갖는다.

          evalExecutionContext.Scope === callingContext.Scope;
        
      • 실행 코드

        
          // global execution context
          var x = 1;
        
          // global execution context 의 Scope Chain 을 갖는다.
          eval('console.log(x)'); // 1
        
          function A(){
        
            // function execution context
        
            var y = 2;
        
            // A function execution context 의 Scope Chain 을 갖는다.
            eval('console.log(x, y)'); // 1 2
        
            function B(){
        
              // B function execution context 의 Scope Chain 을 갖는다.
        
              var z = 3;
        
              eval('console.log(x, y, z)'); // 1 2 3
            }
        
            B();
          }
        
          A();
        
    • Function 생성자 함수

      • Function 생성자 함수를 통한, 함수 생성 시 생성되는 [[Scope]] property 에는 Global Execution Context 의 VO 만 포함된다.

        
        // global execution context
        var x = 1;
        var y = 2;
        
        var fn = Function('console.log(x, y);'); // 1, 2
        
        fn();
        
      • globalExecutionContext 의 x 변수에 접근

        
        // global execution context
        var x = 1;
        
        function A(){
        
          // function execution context
        
          var y = 2;
        
          var fn = Function('console.log(x);'); // 1
        
          fn();
        }
        
        A();
        
        • functionExecutionContext 의 y 변수에 접근

          
            // global execution context
          
            var x = 1;
          
            function A(){
          
              // function execution context
          
              // AFunctionExecutionContext.AO.y
              var y = 2;
          
              var fn = Function('console.log(y);'); // Uncaught ReferenceError: y is not defined
          
              fn();
            }
          
            A();
          
    • Prototype Chain 확장 검색

      • 만약 Scope Chain 내부 VO 에 원하는 식별자 이름이 없을경우, VO 객체의 원형인 Object.prototype 까지 검색 후 찾아낸다.

        • Global Execution Context 내부 VO 는 그 원형인 Object.prototype 객체를 참조할 수 있다.

                    
            // global execution context
                  
            function A(){
                  
                // 어떤 execution context 의 VO 에도 x 속성이 선언하지 않았다.
                  
                // function execution context
                // globalExecutionContext.VO.__proto__.x 속성을 참조한다.
                console.dir(x); // 1
            }
                                    
            // Object 객체의 원형을 확장한다.
            Object.prototype.x = 1;
                  
            A();
          
        • ECStack 내부

              var ECStack = [
                functionExecutionContext: { // A function object
                  AO(VO): {
                  },
                  Scope(Scope Chain): [
                    AO(VO): {
                    },              
                    globalExecutionContext.VO: {
                      A: < reference to function >,
                      __proto__: {
                        x: 1
                      }
                    }
                  ]
                },
                globalExecutionContext: {
                  VO: {
                    A: < reference to function >,
                    __proto__: {
                      x: 1
                    }
                  },
                  Scope(Scope Chain): [
                    globalExecutionContext.VO
                  ]              
                }
              ];
          
    • Scope Chain확장하는 with 문 과 catch 절

      • with 문

        • with 문을 만나면, 해당 Execution Context 의 Scope Chain 은 전달된 인자로 확장된다.<p>

        • 실행 코드

                
            // global execution context
                      
            // globalExecutionContext.VO.person
            var person = {
                name: 'Nicholas',
                age: 30
            };
                      
            // globalExecutionContext.VO.num2
            var num2 = 10;
                      
            // globalExecutionContext.VO.displayInfo
            function displayInfo(){
                      
                // function execution context
                // displayInfoFunctionExecutionContext.AO.count
                var count = 5;
                      
                // with 문에 전달된 인자(person)로 해당 Scope Chain 이 확장된다.
                with ({name: 'Angel', age: 18}){
                      
                    // 확장된 Scope Chain 을 통해 객체 속성에 접근한다.
                    // displayInfoFunctionExecutionContext.scopeChain.withObject.name
                    console.log(name); // 'Angel'
                    // displayInfoFunctionExecutionContext.scopeChain.withObject.age
                    console.log(age); // 18
                }
            };
                      
            displayInfo();
          
        • ECStack 내부

              var ECStack = [
                <displayInfo> functionExecutionContext: {
                  AO(VO): {
                    arguments: {
                    },
                    count: 5
                  },
                  Scope(Scope Chain): [
                    // with 문으로 인해, Scope Chain 이 확장된다.(Scope Chain 의 가장 앞(ScopeChain[0])에 추가된다.
                    with(VO(person)): {
                      name: 'Angel',
                      age: 18
                    },    
                    AO(VO): {
                      arguments: {
                      },
                      count: 5
                    },                                  
                    globalExecutionContext.VO: {
                      person: {
                        name: 'Nicholas',
                        age: 30
                      },
                      displayInfo: < reference to function >,
                      num2: 10
                    }
                  ]
                },
                globalExecutionContext: {
                  VO: {
                    person: {
                      name: 'Nicholas',
                      age: 30
                    },
                    displayInfo: < reference to function >,
                    num2: 10
                  },
                  Scope(Scope Chain): [
                    globalExecutionContext.VO
                  ]                  
                }
              ];
          
      • catch 절

        • catch 절을 만나면, 해당 Execution Context 의 Scope Chain 은 전달된 인자(ex)로 확장된다.<p>

          ```javascript

          // global execution context
            
          try{
                    
            a; // error
          }
          catch(ex){
                    
            // globalExecutionContext.scopeChain.catchObject(ex).message
            console.log(ex.message);
          }
          ```
          
        • ECStack 내부

          ```javascript
            var ECStack = [
              globalExecutionContext: {
                VO: {
                },
                Scope(Scope Chain): [
                  catch(VO(<exception object>)): {
                    message: 'a is not defined',
                    ...
                  },                    
                  globalExecutionContext.VO: {
                  }
                ]					
              }
            ];
          ```	
          

    참고 URL

    Read more


  • javascript 

    1. 정의

    • JS 객체 속성은 Enumerable(열거자) 또는 Nonenumerable(비열거자) 로 정의할 수 있으며, 이들을 탐색, 검색, 반복 할 수 있는 built-in 수단(문 or 메서드)들을 제공하고있다.

      • 위에서 언급한바와같이, Object.defineProperty 메서드를 통해, 객체 속성을 Enumerable 또는 Nonenumerable 로 정의할 수 있다.

        
          var obj = {};
        
          // 객체의 x 속성을 enumerable 로 정의한다.
          Object.defineProperty(obj, 'x', {
              enumerable: true,
              configurable: false,
              writable: false,
              value: 1
          });
        
          // 객체의 y 속성을 non-enumerable 로 정의한다.
          Object.defineProperty(obj, 'y', {
              enumerable: false,
              configurable: false,
              writable: false,
              value: 2
          });
        
          // propertyIsEnumerable 메서드를 통해, 전달된 객체 속성의 enumerable 유/무를 반환한다.
          console.log(Object.propertyIsEnumerable.call(obj, 'x')); // true
          console.log(Object.propertyIsEnumerable.call(obj, 'y')); // false
          console.log(obj); // Object {x: 1, y: 2}
        
        
    • built-in 수단

      • 탐색(Detection)

        • Object.prototype.propertyIsEnumerable

          • 전달된 속성 이름(상속받은 속성이 제외된 속성 중)의 Enumerable 유/무를 반환한다.

            
              var obj = {};
            
              // 객체의 x 속성을 enumerable 로 정의한다.
              Object.defineProperty(obj, 'x', {
                  enumerable: true,
                  configurable: false,
                  writable: false,
                  value: 1
              });
            
              // 객체의 y 속성을 non-enumerable 로 정의한다.
              Object.defineProperty(obj, 'y', {
                  enumerable: false,
                  configurable: false,
                  writable: false,
                  value: 2
              });
            
              // 객체 원형을 확장시킨다.
              obj.constructor.prototype.z = 3;
            
              // enumerable
              console.log(Object.propertyIsEnumerable.call(obj, 'x')); // true
              // non-enumerable
              console.log(Object.propertyIsEnumerable.call(obj, 'y')); // false
            
              // 객체 원형(obj.constructor.prototype)을 확장하여, 상속된 z 속성에 대해서는 false 를 반환하게된다.
              console.log(Object.propertyIsEnumerable.call(obj, 'z')); // false
            
            
          • https://msdn.microsoft.com/ko-kr/library/adebfyya(v=vs.94).aspx

        • Object.prototype.hasOwnProperty

          • 전달된 속성 이름(상속받은 속성이 제외된 속성 중)의 정의 유/무를 반환한다.

          • propertyIsEnumerable 메서드와 달리, Nonenumerable 유/무를 판단하지 못한다.(즉 무조건 true 를 반환하게된다)

            
              var obj = {};
            
              // 객체의 x 속성을 enumerable 로 정의한다.
              Object.defineProperty(obj, 'x', {
                  enumerable: true,
                  configurable: false,
                  writable: false,
                  value: 1
              });
            
              // 객체의 y 속성을 non-enumerable 로 정의한다.
              Object.defineProperty(obj, 'y', {
                  enumerable: false,
                  configurable: false,
                  writable: false,
                  value: 2
              });
            
              // 객체 원형을 확장시킨다.
              obj.constructor.prototype.z = 3;
            
              // enumerable
              console.log(Object.hasOwnProperty.call(obj, 'x')); // true
              // non-enumerable
              console.log(Object.hasOwnProperty.call(obj, 'y')); // true
            
              // 객체 원형(obj.constructor.prototype)을 확장하여, 상속받은 z 속성에 대해서는 false 를 반환하게된다.
              console.log(Object.hasOwnProperty.call(obj, 'z')); // false
            
            
      • 검색(Retrieval)

        • Object.keys

          • 전달된 객체 속성 중(상속받은 속성이 제외된 속성 중) Enumerable 속성으로만 이루어진 배열을 반환한다.

            
              var obj = {};
            
              // 객체의 x 속성을 enumerable 로 정의한다.
              Object.defineProperty(obj, 'x', {
                  enumerable: true,
                  configurable: false,
                  writable: false,
                  value: 1
              });
            
              // 객체의 y 속성을 non-enumerable 로 정의한다.
              Object.defineProperty(obj, 'y', {
                  enumerable: false,
                  configurable: false,
                  writable: false,
                  value: 2
              });
            
              obj.constructor.prototype.z = 3;
            
              // enumerable 속성으로만 이루어진 배열을 반환한다.
              // 이전과 마찬가지로 상속받은 z 속성은 제외된다.
              console.log(Object.keys(obj)); // ["x"]
            
            
        • Object.getOwnPropertyNames

          • 전달된 객체 속성(상속받은 속성이 제외된 속성 중)으로 이루어진 배열을 반환한다.(Enumerable 또는 Nonenumerable 속성이 모두 포함된다)

            
              var obj = {};
            
              // 객체의 x 속성을 enumerable 로 정의한다.
              Object.defineProperty(obj, 'x', {
                  enumerable: true,
                  configurable: false,
                  writable: false,
                  value: 1
              });
            
              // 객체의 y 속성을 non-enumerable 로 정의한다.
              Object.defineProperty(obj, 'y', {
                  enumerable: false,
                  configurable: false,
                  writable: false,
                  value: 2
              });
            
              obj.constructor.prototype.z = 3;
            
              // enumerable or non-enumerable 속성 이름으로 이루어진 배열을 반환한다.
              // 이전과 마찬가지로 상속받은 z 속성은 제외된다.
              console.log(Object.getOwnPropertyNames(obj)); // ["x", "y"]
            
            
      • 반복(Iteration)

        • Object.keys

        • Object.getOwnPropertyNames

        • for…in 문

          • 전달된 객체 속성을(상속받은 속성이 모두 포함된) 모두 순회한다.

          • 이전 built-in 수단들과 달리, 상속받은 속성이 모두 포함된다.

            ```javascript
            
            var obj = {};
            
            // 객체의 x 속성을 enumerable 로 정의한다.
            Object.defineProperty(obj, 'x', {
                enumerable: true,
                configurable: false,
                writable: false,
                value: 1
            });
            
            // 객체의 y 속성을 non-enumerable 로 정의한다.
            Object.defineProperty(obj, 'y', {
                enumerable: false,
                configurable: false,
                writable: false,
                value: 2
            });
            
            // 객체 원형을 확장한 z 속성을 enumerable 로 정의한다.
            Object.defineProperty(obj.constructor.prototype, 'z', {
                enumerable: true,
                configurable: false,
                writable: false,
                value: 3
            });
            
            // 객체 원형을 확장한 t 속성을 non-enumerable 로 정의한다.
            Object.defineProperty(obj.constructor.prototype, 't', {
                enumerable: false,
                configurable: false,
                writable: false,
                value: 4
            });
            
            // 모든 enumerable 속성을 순회한다.(자신이 소유한 속성 및 상속받은 속성)
            for (var n in obj){
                // enumerable 속성만을 가져온다.
                // x: 자신이 소유한 enumerable 속성, z: 상속받은 enumerable 속성
                console.log(n); // x, z
            
            }
            
            ```
            
    • non-enumerable 속성만을 가져오기

      • 언급된 built-in 수단들의 특성을 이용하여, 자신이 소유한 속성 non-enumerable 속성만을 가져오는 예제를 만들어보았다.

        ```javascript
        
        var obj = {};
        
        // 객체의 x 속성을 enumerable 로 정의한다.
        Object.defineProperty(obj, 'x', {
            enumerable: true,
            configurable: false,
            writable: false,
            value: 1
        });
        
        // 객체의 y 속성을 non-enumerable 로 정의한다.
        Object.defineProperty(obj, 'y', {
            enumerable: false,
            configurable: false,
            writable: false,
            value: 2
        });
        
        // 확장할 객체 원형의 z 속성을 enumerable 로 정의한다.
        Object.defineProperty(obj.constructor.prototype, 'z', {
            enumerable: true,
            configurable: false,
            writable: false,
            value: 3
        });
        
        // 확장할 객체 원형의 t 속성을 non-enumerable 로 정의한다.
        Object.defineProperty(obj.constructor.prototype, 't', {
            enumerable: false,
            configurable: false,
            writable: false,
            value: 4
        });
        
        // getOwnPropertyNames 메서드를 통해 자신이 소유한 속성들을 순회한다.
        Object.getOwnPropertyNames.call(Object, obj).forEach(function(prop){
        
            // propertyIsEnumerable 메서드를 통해 해당 속성의 enumerable 유/무를 반환한다.
            if (!Object.propertyIsEnumerable.call(obj, prop)){
        
                // non-enumerable 속성만을 가져온다.
                console.log('non-enumerable property: ' + prop); // non-enumerable property: y
                // getOwnPropertyDescriptor 메서드를 통해 속성에 내용을 기술한다.
                console.log(Object.getOwnPropertyDescriptor(obj, prop)); // y property descriptor
            }
        });
        ```
        

    2. 참고 사이트

    • 아래 페이지에서 enumerability 관련 메서드들을 정리한 라이브러리를 제공하고 있다.(관련 소스 작성 시 반드시 참고하기 바란다)

    Read more


  • JavaScript 

    Selection

    • 정의
      • Selection 은 마우스 또는 키보드를 통해, 컨텐츠를 선택, 또는 선택된 영역을 제어 하기위한 모든 특성들을 제공한다.

    관련 용어

    • 정의

      • Selection 에 대해 공부할때, 가장 큰 장애물이 되는것은, 각 용어에 대한 이해도라 생각한다. 즉 용어에 대한 정확한 이해없이는, 제공되는 API 기능에 대해 이해하기 힘든 부분이 존재하기 때문이다.
    • 용어 설명

      • Selection / Selection object: 사용자가 마우스(드래그) 또는 키보드(키 조작)를 통해, 선택된 텍스트범위를 말한다.

      • anchor: 선택된 범위가 시작되는 지점을 말한다.

      • focus: 선택된 범위가 끝나는 지점을 말한다.

      • range: 선택된 범위의 문서 조각들을 말한다.

    특성

    • Properties

      이와같은 HTML 코드가 있다고 가정하고, 아래 나열된 속성들을 살펴보기로한다.

        <!-- container 객체 -->
        <div id="text-container" contenteditable="true">
            <span style="height: 20px">HELLO</span>
            <span style="height: 20px;padding-left: 5px;">WORLD</span>
            <span style="height: 20px;padding-left: 5px;">TEST</span>
            <span style="height: 20px;padding-left: 5px;">!!!!</span>
        </div>
      
      • selection.anchorNode:

        선택된 범위가 시작되는 지점의 노드를 반환한다.

      • selection.anchorOffset:

        anchorNode 범위내에 anchor(선택된 범위가 시작되는 지점) 이전의 문자열(HE(2개))의 갯수를 반환한다.

      • selection.focusNode:

        선택된 범위가 끝나는 지점의 노드를 반환한다.

      • selection.focusOffset:

        • focusNode 범위내에 focus(선택된 범위가 끝나는 지점)의 문자열(WORLD(5개)) 갯수를 반환한다.

      • anchorOffsetfocusOffset 이 좀 이해하기 힘들더라도(개인적일수도 있지만;;;) 그만큼 중요한 부분이니, 반드시 이해하고 넘어가길 바란다.

      • selection.isCollapsed:

        • 선택된 범위가 시작되는 지점(anchorOffset)과, 끝나는 지점(focusOffset)이 동일한지 여부를 반환한다.

        • 만약 첫 번째 문자와 두 번째 문자 사이를 클릭(드래그가 아닌)한 경우, anchorOffsetfocusOffset 은 각각 1을 반환하게 된다.

          • 선택이 시작된 지점 이전의 문자열(H) 갯수(anchorOffset): 1
          • 선택이 끝난 지점의 문자열(H) 갯수(focusOffset): 1

      • selection.rangeCount:

        • 선택된 범위의 range 크기를 반환한다.

        • 페이지 로드 후 사용자가 문서를 클릭하지 않았을경우, rangeCount0을, 클릭한 후에는 1을 반환하게 된다.

        • 사용자는 일반적으로 한번에 하나의 range 를 선택할 수 있다.

        • Gecko 브라우저에서는 Multiple range 를 선택할 수 있다.</u>(Ctrl or Command 키를 통해)

    • Methods

      • selection.getRangeAt(range index):

        • 선택된 하나의 range object 를 반환한다.
      • selection.collapse(parentNode, offset):

        • anchorNode 를 전달된 offset 으로 지정한다.
      • selection.extend(parentNode, offset):

        • focusNode 를 전달된 offset 으로 지정한다.

      • anchorNode 와 focusNode 를 각각의 메서드를 통해 새로 지정하면, 새로운 선택 범위를 만들어낼수 있다.

        
          selection.collapse(container, 0);
          selection.extend(container, 0);
        
          console.log(selection.anchorNode); // HELLO
          console.log(selection.focusNode); // HELLO
        
          selection.collapse(container, 1);
          selection.extend(container, 3);
        
          console.log(selection.anchorNode); // WORLD
          console.log(selection.focusNode); // TEST
        
          selection.collapse(container, 4);
          selection.extend(container, 4);
        
          console.log(selection.anchorNode); // !!!!
          console.log(selection.focusNode); // !!!!
        
      • selection.collapseToStart():

        • 선택된 범위가 시작되는 지점으로 커서를 이동시킨다.
      • selection.collapseToEnd():

        • 선택된 범위가 끝나는 지점으로 커서를 이동시킨다.
      • selection.selectAllChildren(parentNode):

        • 이전에 선택된 범위를 취소(삭제)한 후, 전달된 parentNode 의 모든 자식 노드들을 선택 한다.
      • selection.addRange(range):

        • 새로운 range(선택 범위의 문서 조각) 를 추가한다.

          
            var container = document.getElementById("text-container");
          
            // container 의 모든 자식 노드들에 대한 range object 를 생성 후 addRange 메서드를 통해, 선택 범위에 추가시킨다.
            for (var i = 1; i < container.childNodes.length; i++){
          
                var childNode = container.childNodes[i];
                var range = document.createRange();
          
                range.selectNode(childNode);
          
                // 새로운 range(선택 범위의 문서 조각) 를 추가한다.
                selection.addRange(range);
            }
          

      • selection.removeAllRanges():

        • 모든 range(선택된 범위의 문서 조각)를 삭제한다.
      • selection.deleteFromDocument():

        • 선택된 범위의 모든 text 를 삭제한다.
      • selection.toString():

        • 선택된 범위의 모든 컨텐츠를 반환한다.
      • selection.containsNode(aNode, aPartlyContained):

        • 전달된 노드(aNode)가 선택된 범위안에 있는지 여부를 반환한다.

        위 그림에서와 같이 helloNode선택된 범위에 포함되지 않기때문에, false반환하게 되며, worldNode 는 그 반대인 true반환하게 된다.

        
          var container = document.getElementById("text-container");
        
          // hello 문자열 노드
          var helloNode = container.getElementsByTagName('span')[0];
          // world 문자열 노드
          var worldNode = container.getElementsByTagName('span')[1];
        
          console.log(selection.containsNode(helloNode, true)); // false
          console.log(selection.containsNode(worldNode, true)); // true
        

    API 테스트

    아래 코드는 Chrome 브라우저에서만 테스트되었습니다.(소스 참조시 반드시 다른 브라우저에서도 테스트 하시기 바랍니다)

    Read more