• book  cording  technique 

    1장 효율적으로 언어 배우기

    1.1 비교를 통한 배움

    여러분이 지금 x라는 언어를 배우고 있고, 무엇이 중요하고 무엇이 그렇지 않은지 고민하고 있다고 가정해보자.
    
    또 같은 시기에 다른 언어인 y를 학습하게 된다면 깨닫게 되는 것이 있다. 무엇이 언어에 따라 다른 것인지, 무엇이 이 언어 x와 언어 y에서 공통적으로 사용되는 것인지,
    그리고 무엇이 언어 x만을 위한 규칙인지 등이다.
    
    많은 언어에서 공통적으로 사용되는 개념이야말로 중요한 지식이다. 그 지식을 습득하게 되면 또 다른 언어 z를 배우는 것도 매우 수월할 것이다.
    

    각 언어들은 그만이 추구하는 철학과, 해결하고자 하는 문제들을 갖고 있다.

    여러 언어를 접하다 보면, 언어들끼리의 유사성(공통된 규칙 등)을 발견하게 되고, 이런 지식들을 익히다 보면, 새로운 언어를 배울 때 좀 더 수월할 수 있다. 또 이 점은 언어뿐 아닌, 프레임워크, 라이브러리 등을 익히면서도 발견할 수 있는 부분이라 생각한다.

    규칙은 언어마다 다르다

    복수의 언어를 비교해서 학습할 때 알게 되는 것이 있다. 그것은 `규칙은 언어마다 다르다`는 것이다.
    
    어떤 언어의 교과서에 `이런 규칙입니다`라고 쓰여있다 해도, 그것은 `이 언어에서는 이런 규칙을 씁니다`라는 의미일 뿐이다.
    
    예를 들어, C언어에서의 정수 0은 `거짓`, 루비에서는 `참`, 자바에선 `참, 거짓`을 위한 별도(데이터) 형이 존재하기 때문에 판정 시, `컴파일 에러`가 발생하게 된다.
    

    특정 언어(프레임워크, 라이브러리 등)의 규칙들이 모든 언어들에서 일반화될 수는 없다.

    또 그 언어라 할지라도 버전에 따라, 그 규칙은 언제든 달라질 수 있다.

    1.2 역사를 통한 배움

    언어 설계자의 의도를 이해하자

    프로그래밍 언어도 사람이 만든 것이다. 언어 설계자는 어떤 문제를 해결하기 위해 그 언어를 만든 것일까?
    그 언어가 어떤 흐름에 따라 만들어졌는지 알게 되면 그 기능이 왜 필요한지 납득할 수 있게 된다.
    

    언어(솔루션)가 갖는 모든 규칙(네이밍 등)들은, 설계자의 수많은 고민 끝에 특정 문제를 해결하기 위해 만들어지는 것이다. 그러기에 설계자 관점에서 언어를 이해해보는 것도 매우 좋은 학습 방법이라 생각한다.

    어떤 언어를 배워야 하는지는 아무도 모른다

    `프로그래밍을 배우고 싶은데 어떤 언어를 배우면 좋을까요?`라는 질문은 무의미하다. 많은 사람들이 `많이 사용되는 언어 x를 배우면 안전하다` 또는
    `이 후는 이 분야가 발전할 테니까 지금 언어 y를 배우는 게 유리하다` 등의 조언을 한다. 하지만 앞날은 아무도 알 수 없다....
    

    현재 트렌디한 언어라 할지라도, 그 언어의 지식이 5년 또는 10년 후에도 도움이 될지는 아무도 알 수 없다.

    그렇기에 특정 언어만의 지식이 아닌, 언어가 갖는 보편적 특징(공통된 규칙)들을 깊게 이해하는 것이 더 중요하다고 생각한다.

    2장 프로그래밍 언어를 조감하다.

    2.1 프로그래밍 언어 탄생의 역사

    많은 것이 과거의 것을 발전시켜 만들어졌다. 즉 새로운 것은 과거의 것을 알고 난 후에야 만들어지는 것이다. 지금은 `당연하다`고 여기는 것도
    과거에는 아직 발견되지 않은 것들이었다. 그러므로 옛날 사람 시점에서 생각하는 방법은 새로운 것을 배우는 사람에겐 매우 유리하게 작용한다.
    

    이미 만들어진 바퀴를 또다시 만들 필요는 없지만, 새로운 바퀴를 만들고자 할 때, 그것의 기반 지식(또는 역사)을 이해하는 것은 매우 중요한 부분이라 생각한다. 보통 그 지식을 통해 더욱 완고한 결과물을 만들 수 있기 때문이다.

    FORTRAN의 등장

    1954년, 드디어 지금 여러분이 사용하고 있는 것과 비슷한 프로그래밍 언어가 고안되었다. 바로 포트란이다.
    포트란이란 이름은 Formula Translating System(수식 변환 시스템)을 의미한다. 지금은 프로그래밍 언어로 `x 곱하기 y 더하기 z`를 x * y + z라고 표현할 수 있는 것을
    너무나 당연히 여기지만, 그것을 최초로 표현한 것이 포트란이다.
    
    ...
    
    실제로 초기 포트란 컴파일러가 출력하는 기계어는 능숙한 프로그래머가 짠 기계어에 비교하면 효율이 매우 떨어졌다. 그러나 코드량이 눈에 띄게 줄었고, 코드를 읽기 쉬워짐에 따라 많은 사용자 층을 얻을 수 있었다.
    

    이 부분은 근래의 파서들이 발전해 나가는 흐름도 크게 다르지 않다고 생각한다.

    2.2 프로그래밍 언어 탄생의 목적

    `프로그램은 어떤 것을 편하게 하기 위해 고안된 것이다.` 단, 편하게 하는 것은 부실하게 하는 것과는 다르다.
    ...
    

    언어에 따라 다른 “편리함”의 의미

    프로그래밍 언어의 목적은 `편리함`이라고 말했지만, 그럼 세상에는 왜 수많은 언어가 존재하는 것일까?
    
    그것은 편하다는 의미가 사람에 따라 다르기 때문이다. 어떤 `편리함`을 목표로 했는지, 언어 설계자의 의도를 파악해보도록 하자
    
    ...
    
    예를 들어 C++은 빠른 실행 속도를 중시하고 있는 언어이다. C로 만든 코드보다 빠른 속도가 날 수 있도록 고안했지만, 결과적으로 언어 사양이 더 복잡해지고 말았다.
    
    ...
    
    

    모든 언어(솔루션)는 그만의 분명한 정체성(목표)을 반드시 가져야 한다고 생각한다. 그것이 곧, 앞으로 나아가야 할 지표가 되기 때문이다. 또 새로운 규칙을 설계하기 위한 모든 의사 결정 시에도, 이 정체성이 기준이 되어야 한다고 생각한다.

    어떤 프로그램을 편하게 만들고 싶은가?

    예를 들어 PHP는 웹 서비스를 쉽게 만들 수 있도록 해준다. 그러나 문장 처리를 편하게 하지는 못한다.
    그러나 하스켈 등의 ML(Meta-Language) 처리 계통 언어는 ML이라는 이름이 나타내고 있듯이 문장 처리를 쉽게 만들어 준다.
    
    올바른 설계는 사용하는 사람이 무엇을 목적으로 하고 있는지에 따라 달라진다.
    
    다양한 설계 언어가 있는 것은 사람에 따라 다양한 목적이 존재하기 때문이다.
    
    즉 빠른 속도를 위해 만들어진 C++과 쉬운 코드 해석을 위해 설계된 파이썬을 비교해서 C++이 읽기 어렵다느니, 파이썬이 느리다느니 하는 것은 유익하지 못한 논쟁이다.
    

    모든 언어(솔루션)는 다양한 목적(사용자 타깃 등)에 따른 설계 방향을 갖고 있다. 그렇기에 상황에 따라 자신의 목적과 맞는 언어(솔루션)를 선택하면 그만이라 생각한다.

    3장 문법의 탄생

    3.1 문법이란?

    프로그래밍 언어에는 여러 가지 규칙이 있다.
    
    예를 들면 `덧셈보다는 곱셈이 우선순위가 높다. 1 + 2 * 3이라고 쓰면 2 * 3이 먼저 계산된다.`도 규칙(연산자 우선순위) 중 하나이다.
    
    즉 문법이란, 프로그래밍 언어 설계자가 만든 `이렇게 쓰면 이런 의미로 해석된다`라고 정한 규칙이다.
    

    규칙이란, 결국 문법을 나타내며, 또 그 문법은 언어에 따라 다르다.

    연산자 우선순위

    만약 1 + 2 * 3이라는 소스 코드가 있다면 이것이 `1에 2를 더해서 그 결과에 3을 곱하는 것` 또는 `2에 3을 곱해서 그 결과에 1을 더한 것` 중 어떤 규칙이 적용되는 것일까?
    
    그것은 사람이 (이해하기) 편한 쪽으로 정하고 있다.(예를 들어 옛날 계산기 중에는 1 + 2 * 3의 계산 결과가 9가 되는 것이 있었다)
    
    ...
    
    이는 프로그래밍 설계자가 `+ 보다 *가 우선순위가 높기 때문에 먼저 계산한다`는 규칙을 정했기 때문이다. 또 `그렇게 해야지 사칙연산과 같아서 이해하기 쉽다`는 생각이다.
    
    

    제품(솔루션) 사양(규칙)을 정의할 때도 크게 고려했던 것 중 하나가 사용자가 부담해야 할 초기 진입장벽이었다. 즉 사용자가 받아들이기 쉬운 규칙들을 정의하는 것이 중요하다고 생각한다.

    다만, 이 부분에 있어서는 제품과 언어 설계는 다소 차이가 있을 거라 생각한다.

    3.2 스택 머신과 FORTH

    계산 순서

    1 2 +
    
    위 코드가 어떻게 실행되는지 살펴보자. FORTH의 가장 큰 특징은 `스택(Stack)`, 즉 `값을 쌓아 두는 장소`를 사용한다는 것이다.
    
    1. 1이라는 워드와 만난다. 그리고 스택에 1이라는 값을 담아둔다.
    
    2. 2라는 워드와 만난다. 그리고 스택에 2라는 값을 담아둔다.
    
    3. 마지막으로 +라는 워드와 만난다. 이 워드는 `스택에서 두 개의 값(1, 2)을 꺼내와 그것을 더한 결과를 다시 새로운 스택에 담아`라고 하는 명령과 연결되어 있다.
    
    4. 즉 스택에서 1과 2를 꺼내서 더한 결과인 3을 또 다른 스택에 담는다.
    

    위와 같은 스택 기반의 언어를 직접 입력하는 경우는 거의 없지만 자바, 파이썬, 루비 등의 언어가 이 같은 스택 머신형의 VM을 사용하고 있다. 즉 자바로 프로그램을 짜면 그 프로그램은 내부적으로 FORTH와 같은 프로그램으로 변환(컴파일) 되어 동작한다는 말이다.

    현재도 살아있는 스택 머신

    파이썬에서는 표준으로 장착되어있는 dis 라이브러리를 사용해 VM이 실행할 명령열을 출력할 수 있다.

    아래 순서는 (x + y) * z라는 소스 코드명령열로 컴파일 되는 과정을 보여주고 있다.

    1. x를 스택에 담는다.
    
    2. y를 스택에 담는다.
    
    3. 스택에서 x와 y를 꺼내어 더한 후, 그 결과를 새로운 스택에 담는다.
    
    4. z를 스택에 담는다.
    
    5. 스택에 있는 상위 두 개를((x + y)의 결과, z) 곱한다는 명령열로 컴파일된다.
    

    3.3 구문 트리와 LISP

    현재도 살아있는 구문 트리

    `구문 트리`가 사용되고 있는 것은 최신 언어도 마찬가지다.
    
    파이썬에 기본으로 장착되어 있는 AST(추상 구문 트리) 라이브러리를 사용하면 특정 코드가 어떤 구문 트리로 구성되어 있는지 알 수 있다.
    

    링크를 통해, 각 언어에서 사용되는 구문 트리 결과를 확인할 수 있다.

    3.4 중위 표기법

    `1 더하기 2를` LISP는 `+ 1 2`라 표현하고, FORTH는 `1 2 +` 일반 수식에서는 `1 + 2`로 표현한다.
    
    이와 같이, 모든 항 앞에 연산자가 오는 경우를 `전위 표기법`, 뒤에 오는 경우를 `후위 표기법`, 중간에 오는 경우는 `중위 표기법`이라한다.
    
    다만, 프로그래밍 언어가 탄생하기 이전부터 사람들은 중위 표기법에 익숙했다.
    

    전위 표기법: + 1 2

    후위 표기법: 1 2 +

    중위 표기법: 1 + 2

    구문 해석기

    구문 해석기(파서, Parser)는 소스 코드를 문자열로 읽어 들여 해석하고, 그것을 구문 트리로 만드는 프로그램이다.
    
    FORTRAN에서 프로그램이 컴파일될 때도 이 구문 해석기가 소스 코드를 문자열로 읽어 들여 구문 트리로 변환하는 작업을 하고 있다.
    
    ...
    
    문법 설계와 구문 분석기 구현은 프로그래밍 언어의 외관을 결정하는 중요한 요소이다. 언어 설계자는 문법을 설계할 때 무엇을 쉽게 쓸 수 있도록 할 것인지,
    
    어떤 실수를 줄이도록 할 것인지 등 프로그래밍 언어가 사용자에게 어떤 가치를 줄 수 있을지 생각한다.
    

    언어에서 문법과 구분 분석기를 구현하는 것은, 그 언어의 목적을 구현하는 가장 중요한 작업이다.

    칼럼

    ...
    
    `무엇을 배울지, 무엇을 배우면 좋을지`를 정할 때 필연적으로 지표가 될 수 있는 것이 `무엇을 만들고 싶은가?`. 즉 목적을 명확히 하는 것이다.
    
    ...
    
    또 `무엇을 만들까?`를 결정할 수 없다면, 혹시 처음부터 완벽한 것을 만들려 하거나, 모두를 만족시키는 것을 만들려고 생각하고 있지는 않은가?
    
    처음부터 굉장한 것을 만들려고 생각해서 오히려 손도 대지 못하고 있다면, 시간이 흘러도 굉장한 것을 만들 수 없다. 우선은 간단한 것도 괜찮으니, 무엇이라도 만들어보는 것이 중요하다.
    

    처음부터 무엇을 만들지에 대해 너무 고민하지 말자. 평소 만들어보고 싶었던 것이 있거나, 내부 구현이 궁금했던 것들부터 시작해보는 것도 나쁘지 않다..

    또 그 목적의 결과가 너무 크다면, 그것을 위한 작은 부속들부터 하나하나 만들어가는 것도 하나의 방법이 될 수 있다. 너무 많은 것을 한 번에 다하려 하다, 시작조차 못하는 경우가 많기 때문이다.

    3.5 정리

    ...
    현재 대부분의 프로그래밍 언어는 FORTRAN 식의 `다가가기 쉬운 작성법`을 목표로 하고 있다.
    
    하지만 모순 없이 해석할 수 있는 문법을 만들어내는 것은 어려운 작업이다.
    
    특히 나중에 새로운 문법을 추가할 때 기존 문법과 마찰되지 않도록 하는 것은 더욱 그렇다.
    
    이 때문에 현실의 프로그래밍 언어에는 이해하기 어려운 작성법이 여전히 존재하는 것이다.
    

    현대 언어들은 자신만의 철학을 기반으로, 타 언어들의 장점을 흡수해나가는 것이 추세인듯하다.

    Read more


  • javascript 

    1. 글에 대해

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

    2. object initializer

    • 객체 초기화(or 생성)를 위한 표현법(or 식)으로 아래와 같은 것들이 존재한다.

    3. ES5 & Object Initializer

    // 'use strict';
    {
        var o = {x: 1, y: 2, z: function(){}};
    
        var x = o.x, y = o.y, z = o.z;
    
        console.log(x, y, z); // 1 2 function(){}
    
        // 변수 선언(및 할당)과 동시에, 객체 property 값을 초기화할 수 있다.
        var o2 = {x: _x = 3, y: _y = 4};
        // 단 "Strict Mode" 에서는 아래와 같은 예외가 발생하게된다.(주의!!)
        // Uncaught ReferenceError: _x is not defined
        console.log(o2.x, o2.y); // 3 4
    
        // ES5 에서는 Object.defineProperty 를 통해서만, 접근자를 생성할 수 있다.
        var o4 = {};
    
        // id 접근자를 생성한다.
        Object.defineProperty(o4, 'id', {
            value: '',
            // 접근자 property 에 수정 권한을 준다.(기본 값: false)
            writable: true,
            get function(){
                return id;
            },
            set function(v){
                id = v;
            }
        });
    
        console.log(o4.id); // ''
    
        o4.id = 'yanione';
        console.log(o4.id); // yanione
    
        console.log(o4);
    }
    

    4. ES6 & Object Initializer

    • 기본적인 사용 방법

        {
            // 선언된 변수명과 동일한 객체 property 명을 사용할 경우, 아래와 같이 표현할 수 있다.
            let x = 1, y = 2, z = function(){}, __proto__ = null;
      
            // property 할당 문법이 보다 간결해졌다.
            let o = {x, y, z, __proto__};
      
            console.log(o.x, o.y, o.z); // 1 2 function(){}
            console.log(o.__proto__); // null
      
            // ES6 에서는 객체 초기화 시, 접근자를 생성할 수 있다.
            let o3 = {
                // 접근자 사용을위해, 접근자 property(__id__) 를 선언한다.
                // 접근자 property
                __id__: '',
                // getter
                get id(){
                    return this.__id__;
                },
                // setter
                set id(v){
                    this.__id__ = v;
                }
            };
      
            console.log(o3.id); // ''
            console.log(o3.__id__); // ''
      
            o3.id = 'yanione';
      
            console.log(o3.id); // yanione
            console.log(o3.__id__); // yanione
      
            console.log(o3);
      
            let o4 = {
                // 메서드를 생성한다.
                method(){
                    return 1;
                },
                // 문자열을 통해, property 명 조합할 수 있다.
                ['me'+ 'thod1'](){
                    return 2;
                },
                // generator 를 생성할 수 있다.
                * generator(length = 10){
      
                    // 일반적인 동기화 구문
                    for (var i = 0; i < length; i++){
                        yield i;
                    }
                },
                // 전달된 generator 를 비동기로 호출하는 함수
                asyncAction(generator, cb = function(){}, time = 1000){
      
                    if (!generator) return;
      
                    let v = generator.next();
      
                    if (!v.done){
                        window.setTimeout(() => {
                            this.asyncAction(generator, cb, time);
                            cb.call(this, v.value);
                        }, time);
                    }
                }
            };
      
            console.log(o4.method()); // 1
            console.log(o4.method1()); // 2
      
            let __generator__ = o4.generator();
      
            // 동기화 구문을 포함한 generator 를 (재귀 호출을 위한)setTimeout 함수를 통해, 비동기로 호출한다.
            // 전달된 callback 함수를 1초 마다 호출하게된다.
            o4.asyncAction(__generator__, function(v){
                console.log(this); // o object
                console.log(v); // 0 ~ 9
            });
        }
      
    • super 키워드를 통해, 상속받은 객체 메서드를 호출할 수 있다.

        {
      
            let o1 = {
                method1(){
                    // super 키워드를 통해, 상속받은 객체 메서드를 호출할 수 있다.
                    super.method2();
                }
            };
      
            let o2 = {
                method2(){
                    console.log('method2');
                }
            };
      
            Object.setPrototypeOf(o1, o2);
      
            o1.method1();
      
        }
      
    • ES5 의 __proto__ 내부 속성을 통해, super 키워드를 구현할 수 있다.

        {
            let o1 = {
                method1(){
                    // getPrototypeOf 메서드는 chrome 5, IE 9 이상에서 지원한다(대부분의 브라우저에서 지원한다고 볼 수 있다)
                    // getPrototypeOf 메서드를 통해, super 키워드를 구현할 수 있다.
                    // Object.getPrototypeOf(this).method2() === this.__proto__.method2()
                    Object.getPrototypeOf(this).method2();
                }
            };
      
            let o2 = {
                method2(){
                    console.log('method2');
                }
            };
      
            // setPrototypeOf 메서드는 chrome 34, IE 11 이상에서만 지원한다.
            // Object.setPrototypeOf(o1, o2) === o1.__proto__ = o2;
            Object.setPrototypeOf(o1, o2);
      
            o1.method1();
      
        }
      

    관련 URL

    Read more


  • javascript 

    1. 글에 대해

    • 모든 결과는 Chrome 브라우저(ver 49)를 통해 테스트된 결과입니다.

    2. Destructuring assignment(해체 할당)이란?

    • 해체 할당은, 배열 또는 객체 리터럴과 유사한 문법으로, 변수에 값을 할당하는 JS 표현식중 하나이다.

    3. Array destructing assignment(배열 해체 할당)

    • 배열 해체 할당을 통해, iterator 객체의 요소값을, 변수에 할당시킬 수 있다.

      • Array in ES5

          {
            // ES5 에서, 배열 객체 요소를 변수에 할당시키는 기본적인 방법이다.
            var arr = [1, 2];
            
            var x = arr[0];
            var y = arr[1];
            
            console.log(x, y); // 1 2
          }
            
        
      • Iterator in ES6

        • 기본적인 할당 방법

            {
              let arr = [1, 2];
                      
              // (블록 스코프)변수 선언과, 해체 할당을 한줄로 표현할 수 있다.
              let [x, y] = arr;
                      
              console.log(x, y); // 1 2
                      
              // 변수 선언이 생략된 x, y 속성은, 실행 코드 처리 시, globalEC.VO 의 속성으로 추가된다.
              [_x, _y] = arr;
                      
              // globalEC.VO.x, globalEC.VO.y
              console.log(this._x, this._y, window._x, window._y); // 1 2 1 2
            }
          
        • 변수 할당을 건너뛰는 방법

            {
              let arr = [1, 2, 3, 4];
          
              // 아래와 같은 표현식을 통해, 변수 할당을 건너뛸 수 있다.
              let [x, , , z] = arr;
          
              console.log(x, z); // 1 4
            }
          
        • 나머지 연산자를 통한 해체 할당

          ```javascript
          {
              let arr = [1, 2, 3];
          
              // y 변수에는, (할당될)배열 객체의 나머지 요소(2, 3)가 포함된 새로운 배열 객체([2, 3])가 할당된다.
              let [x, ...y] = arr;
          
              console.log(x, y); // 1 [2, 3]
          
              let [_x, ..._y] = [1];
          
              // _y 변수에는, (할당될)배열 객체의 나머지 요소가 존재하지 않으므로, 빈 배열 객체가 할당된다.
              console.log(_x, _y); // 1, []
          }
          ```
          
          • (할당될)변수의 기본값을 지정할 수 있다.

              {
                  let arr = [1];
            
                  // y 변수에 기본값 2 를 지정한다.
                  let [x, y = 2] = arr;
            
                  // y 변수에 기본값 2 가 할당된다.
                  console.log(x, y); // 1 2
            
                  // _x 변수에 기본값 1 을 지정한다.
                  let [_x = 1] = [undefined];
            
                  // _x 변수에 기본값 1 이 할당된다.
                  console.log(_x); // 1
            
                  // 함수 표현식을 기본값으로 사용할 수 있다.
                  let [__x = (() => {
                      return 1;
                  })()] = [];
            
                  // __x 변수에 함수 표현식을 통해 반환받은 기본값 1이 할당된다.
                  console.log(__x); // 1
            
                  // ___x, ___y 변수에 각각 기본값 3, ___x 변수를 지정한다.
                  let [___x = 3, ___y = ___x] = [];
            
                  // ___x, ___y 변수에 각각 기본값 3, ___x 변수값이 할당된다.
                  console.log(___x, ___y); // 3 3
            
                  // ____x, ____y 변수에 각각 기본값 3, ____x 변수를 지정한다.
                  let [____x = 3, ____y = ____x] = [7];
            
                  // ____x, ____y 변수에 각각 배열 요소값 7, ____x 변수값이 할당된다.
                  console.log(____x, ____y); // 7 7
              }
            
          • 중첩 배열에 대한, 해체 할당

             {
               // 중첩 배열을 통한 해체 할당
               let arr = [1, [3, 4, 5]];
            
               let [x, [y, ...z]] = arr;
            
               // z 변수에는 (할당될)변수 객체의 나머지 요소(4, 5)가 포함된 새로운 배열[4, 5]이 할당된다.
               console.log(x, y, z); // 1 3 [4, 5]
            
               let [_x, ...[_y, _z]] = [1, 2, 3];
            
               // (나머지 연산자의)피 연산자는 꼭 변수가 아니여도 된다.
               // 즉 나머지 연산자를 통해, 할당된 피연산자를 한번 더, 해체 할당하면 아래와 같은 결과가 반환된다.
               console.log(_x, _y, _z); // 1 2 3
             }
            
          • 함수 인자값을 통한, 해체 할당

             {
            
                 let A = ([x, y = 2]) => {
            
                     // y 변수에는 지정된 기본값인 2가 할당된다.
                     console.log(x, y); // 1 2
                 };
            
                 A([1]);
            
                 let B = ([x, y] = [1, 2]) => {
            
                     // x, y 변수에는 지정된 기본값인 1, 2 가 할당된다.
                     console.log(x, y); // 1 2
                 };
            
                 B();
            
                 let C = ([x = 1, y = 2] = []) => {
                     console.log(x, y);
                 };
            
                 // 전달된 인자값이 없는경우, 그에 대한 기본값인 "빈 배열" 객체가 할당되며,
                 // 또 그 배열에 대한 기본값인 x = 1, y = 2 가 다시 할당되게된다.
                 C(); // 1 2
            
                 // 지정된 기본값인 1, 2 가 할당된다.
                 C([]); // 1 2
            
                 // 전달된 인자값이 할당된다.
                 C([3, 4]); // 3 4
            
            
                 // block scope
                 let x = 'outer';
            
                 let D = function(_x = x) {
            
                     // x 변수 선언 후, 2를 할당한다
                     let x = 'inner';
                     console.log(_x); // outer
                 }
            
                 // 전달된 인자값이 없는 경우, 지정된 기본값인 x 변수가 할당된다.
                 D();
            
                 // args 변수에는, 전달된 인자값이 모두 포함된 새로운 배열이 할당된다.
                 let E = function(...args) {
            
                     // x, y 변수에 할당한다.
                     let [x, y] = args;
            
                     console.log(x, y); // 1 2
                 }
            
                 E(1, 2);
             }
            
            
        • 그 밖의 표현식

          • 문자열 할당

              {
            
                  // 문자열이 할당될 경우에는, 해당 문자열을 암묵적으로 iterator 객체로 변환한다.
                  let [x, y, z] = 'xyz';
            
                  console.log(x, y, z); // x y z
            
                  // 아래 코드는 동일한 결과를 반환한다.
                  // let [_x, _y, _z] = new String('xyz')[Symbol.iterator]();
              }
            
            
          • new Set([])

              {
                  // 생성된 set 객체를 통해, iterator 객체를 할당한다.
                  let [__x, __y] = new Set([1, 2]);
            
                  console.log(__x, __y); // 1 2
            
                  // let [__x, __y] = new Set([1, 2]).values();
              }
            
          • generator 할당

              {
                  // generator 를 선언한다.
                  function* $$() {
                      for (let i = 1; ; i++) {
                          yield i;
                      }
                  }
            
                  // 생성된 generator 를 할당한다.
                  let [x, y, z] = $$();
            
                  console.log(x, y, z); // 1 2 3
            
                  // 객체 내부에 생성된 generator 를 할당한다.
                  let [_x, _y] = {
                      // generator 생성한다
                      * [Symbol.iterator]() {
                          for (let i = 1; i < 10; i++){
                              yield i;
                          }
                      }
                  };
            
                  console.log(_x, _y); // 1 2
            
                  // iterator 인터페이스를 구현한, iterator 객체를 할당한다.
                  let [__x, __y] = {
                      i: 0,
                      [Symbol.iterator]() {
                          // iterator 객체를 반환
                          return this;
                      },
                      next(){
                          return {value: ++this.i, done: this.i > 10};
                      }
                  };
            
                  console.log(__x, __y); // 1 2
              }
            
          • ETC

              {
            
                  // 정규식을 통해, 반환된 iterator 객체를 할당한다.
                  let [, x, y, z] = /(\d+)\/*(\d+)\/*(\d+)/.exec('2016/03/01');
                  console.log(x, y, z); // 2016 03 01
            
            
                  // map 객체를 생성한다.
                  let m = new Map([
                      [1, 'a'],
                      [2, 'b'],
                      [3, 'c']
                  ]);
            
                  // map 객체를 통해, 새로운 배열 객체를 생성한다.
                  let _m = new Map([...m].map(([k, v]) => {
                      return [k * 2, '_' + v];
                  }));
            
            
                  let urls = [
                      'http://example.com/foo.html',
                      'http://example.com/bar.html',
                      'http://example.com/baz.html'
                  ];
            
                  // Promise.all 메서드를 통해, 전달된 url 집합을 할당한다.
                  Promise.all(urls.map((url) => {
                      return url;
                  })).then(([url1 = top.window.location.href, url2 = top.window.location.href, url3 = top.window.location.href]) => {
            
                      // 전달받은 배열 객체(['http://example.com/foo.html', 'http://example.com/bar.html', 'http://example.com/baz.html'])가 해체 할당된다.
                      console.log(url1, url2, url3);
                  });
              }
            

    4. Object destructing assignment(객체 해체 할당)

    • 객체 해체 할당을 통해, 객체 속성값을, 변수에 할당시킬 수 있다.

      - Object in <span style="color:#c11f1f">ES5</span>
        
          ```javascript
            
          {
              // ES5 에서, 객체 속성을 변수에 할당시키는 기본적인 방법이다.
              let o = {x: 1, y: 2};
                
              let x = o.x;
              let y = o.y;
        
              console.log(x, y); // 1 2
          }                    
          ```                                           
                       
      - Object in <span style="color:#c11f1f">ES6</span>
            
          - **기본적인** 할당 방법
            
              ```javascript
              {
                  let o = {x: 1, y: 2};
            
                  // (블록 스코프)변수 선언과, 해체 할당을 한줄로 표현할 수 있다.
                  let {x, y} = o;
            
                  // (할당될)변수 이름을 객체 속성 이름과 다르게 생성하고 싶을때에는, 아래와 같은 표현식을 사용할 수 있다.
                  let {x: _x, y: _y} = o;
            
                  console.log(x, y, _x, _y); // 1 2 1 2
              }
              ```   
                                                
          - (할당될)변수의 **기본값**을 지정할 수 있다.
            
              ```javascript
              {
                  let o = {x: 1};
            
                  // y 변수에 기본값 2 를 지정한다.
                  let {x, y = 2} = o;
            
                  console.log(x, y); // 1 2
            
                  // _y 변수에 기본값 2 를 지정한다.
                  let {x: _x, y: _y = 2} = o;
            
                  console.log(_x, _y); // 1 2
            
            
                  // 배열 요소가 존재하지 않을 경우, 그에 대한 기본값인 "빈 객체"가 할당되며,
                  // 또 그 객체에 대한 기본값인 __y = 2 가 다시 할당된다.
                  let [{x: __x, y: __y = 2} = {}] = [];
            
                  console.log(__x, __y); // undefined 2
            
            
                  // 배열 요소가 존재하지 않을 경우, 그에 대한 기본값인 "빈 객체"가 할당되며,
                  // 또 그 객체에 대한 기본값인 ___x = {x: 1}, ___y = 2 가 다시 할당된다.
                  let [{x: ___x, y: ___y = 2} = {x: 1}] = [];
            
                  console.log(___x, ___y); // 1 2
              }
              ```
                                                                 
          -  **중첩 객체**에 대한, 해체 할당
            
              ```javascript
              {
                  // 중첩된 객체를 생성한다.
                  let o = {x: 1, y: {_x: 2, _y: {__x: 3}}};
                    
                  // 중첩된 객체가 해체 할당된다.
                  let {x, y: {_x, _y: {__x}}} = o;
            
                  console.log(x, _x, __x); // 1 2 3
              }
              ```  
                                                                             
          -  **함수 인자값**을 통한, 해체 할당
            
              ```javascript
              {
                  let A = ({x = 1, y = 2}) => {
                      console.log(x, y); // 1 2
                  };
            
                  // y 변수에는 기본값인 2가 할당된다.
                  A({x: 1});
            
                  let B = ({x, y} = {x: 1, y: 2}) => {
                      console.log(x, y); // 1 2
                  };
            
                  // 인자값이 전달되지않은 경우에도, 기본값인 {x: 1, y: 2} 가 할당된다.
                  B();
            
                  let C = ({x = 1, y = 2} = {}) => {
                      console.log(x, y); // 1 2
                  };
            
                  // 인자값이 전달되지않은 경우에는, 그에 대한 기본값인 빈 객체가 할당되며,
                  // 또 그 객체에 대한 기본값인 x = 1, y = 2 가 다시 할당된다.
                  C();
                    
                // 해체 할당을 통해 이와 같은 패턴이 적용될듯 하다.
                function ajax({url = top.location.href, method = 'GET', headers = {}} = {}){
          
                    return {
                        url: url,
                        method: method,
                        headers: headers
                    };
                };
          
                console.log(ajax()); // {url: "http://localhost:8080/sourceTest/es6/destructuring_assignment.html", method: "GET", headers: Object}
                  
                console.log(ajax({url: 'myUrl', method: 'POST', headers: {contentType: ''}})); // {url: "myUrl", method: "POST", headers: Object}                  
              }
              ```                                                                             
                         
      - 그 밖의 표현식     
                                       
          - (할당)패턴 없이, 값을 할당할 경우에는 그 값을 **객체**로 변환 후, 해당 속성값을 할당시킨다
            
              ```javascript
              {
            
                  let {length: x} = [1] // [].length
                  console.log(x); // 1
            
                  let {toString: xx} = {} // {}.toString
                  console.log(xx); // toString function object
                    
                  // 할당될 값에 (할당)패턴이 적용된 경우...
                  let {toString: _xx} = {toString: 1} // {}.toString
                  console.log(_xx); // 1                  
            
                  let {length: xxx} = 'mohwa'; // === new String('mohwa').length
                  console.log(xxx); // 5
            
                  let {toString: xxxx} = 'mohwa'; // === new String('mohwa').toString
                  console.log(xxxx); // toString function object
            
                  let {toFixed: xxxxx} = 1; // new Number(1).toFixed
                  console.log(xxxxx); // toFixed function object
            
                  let {valueOf: xxxxxx} = true; // new Boolean(true).valueOf
                  console.log(xxxxxx); // valueOf function object
            
                  let {length: xxxxxxx} = function(x){}; // function(x){}.length
                  console.log(xxxxxxx); // 1
            
                  let {x: xxxxxxxx} = 'mohwa'; // new String('mohwa').xx
                  console.log(xxxxxxxx); // undefined
            
            
                  // undefined 와 null 값은 예외가 발생한다.
            
                  try{
                      let {toString: x} = undefined; // === undefined.toString
                  }
                  catch(e){
                      console.log(e.message); // Cannot match against 'undefined' or 'null'.
                  }
            
                  try{
                      let {toString: x} = null; // === null.toString
                  }
                  catch(e){
                      console.log(e.message); // Cannot match against 'undefined' or 'null'.
                  }
            
              }
              ```
                
          - **Symbol** 을 통한 해체 할당
            
              ```javascript
              {
                  const k = Symbol();
                  let o = { [k]: 1 };
            
                  let {[k]: x} = o;
            
                  console.log(x); // 1
              }        
              ```                             
      

    관련 URL

    Read more