[카테고리:] IT

다양한 IT 기술에 대한 내용을 공유합니다.

  • [JavaScript] 함수 메서드(call, apply, bind)

    [JavaScript] 함수 메서드(call, apply, bind)

    정의

    함수의 기본 메서드중 call, apply, bind 에 대해 보자.
    자바스크립트에서 상속개념을 자주 쓰다보면 불필요한 메서드에 프로토타입까지 상속받아 오기 때문에 메모리 낭비가 심해진다. 이때 다른 객체의 메서드를 가져와 쓸 수 있는데 그 기능을 가진 메서드가 call과 apply 이다. call 메서드 먼저 살펴보겠다.

    call

    func.call(obj, a, b);
    func = 가져올 메서드
    call = call 메서드
    obj = 메서드를 사용할(현재) 객체
    a, b = 메서드에 전달할 인자
    

    기본 정의는 위와 같으며 아래 코드에서 자세히 살펴볼 수 있다.

    var obj1 = {
        name: "obj1",
        funcThis: function() {
            return this;
        }
    };
    console.log(obj1.funcThis()); // Object {name: "obj1"}
    

    obj1 객체에 funcThis 메서드를 추가했다. funcThis 메서드는 자신을 감싼 obj1객체를 리턴하고 있다.

    var obj1 = {
        name: "obj1",
        funcThis: function() {
            return this;
        }
    };
    var obj2 = {
        name: "obj2"
    };
    console.log(obj1.funcThis.call(obj2)); // Object {name: "obj2"}
    

    call 메서드를 이용하여 obj1 의 funcThis 메서드를 obj2 객체에서 실행한다. obj2객체를 리턴하고 있다. 아무 값을 안넣으면(null) window를 반환한다. call 과 형제격인 apply 메서드는 call 메서드와 같지만 한가지 다른점이 있다. call은 인자값을 하나 하나 전달하지만 apply 메서드는 인자값을 배열로 전달한다.

    apply

    func.apply(obj, [arr]);
    func = 가져올 메서드
    apply = apply 메서드
    obj = 메서드를 사용할(현재) 객체
    arr = 메서드에 전달할 인자 목록
    

    call 과 apply 는 보통 함수 내 arguments 객체와 같이 사용하는 모습을 많이 볼 수 있다. 처음에는 어려워 보이지만, 단순하게 보면 결국 arguments 객체에 다른 메서드를 빌려와 쓰는것으로 보면 된다. arguments 객체는 배열처럼 보이지만 실제 배열이 아닌 유사배열객체이기 때문에 배열 메서드가 없다(length 제외). 아래 구문은 arguments에 없는 배열의 메서드를 가져와 쓰는 것이다.

    function func() {
        console.log(Array.prototype.slice.call(arguments, 0, 2));
    }
    func("눈", "누", "난", "나"); // ["눈", "누"]
    

    위 함수를 보면 배열의 프로토타입에 있는 slice 메서드를 arguments 객체에서 사용하는 것을 알 수 있다.

    slice 메서드 : 문자열의 일정 부분을 반환
    obj.slice(start, end);
    obj = 필수. 반환할 배열 객체
    start = 필수. 지정된 부분의 시작입
    start = 선택. 지정된 부분의 끝
    

    기본 정의는 위와 같으며 아래 코드에서 자세히 살펴볼 수 있다.

    function func() {
        console.log(Array.prototype.join.call(arguments));
    }
    func("눈", "누", "난", "나"); // 눈-누-난-나
    

    join 메서드 : 인자값으로 넘겨진 구문을 모든 배열 요소에 추가한다.

    obj.join("val");
    obj = 필수. 반환할 배열 객체
    val = 선택. 추가할 문자열 이다. 아무값도 안넣을 경우 쉼표(,)로 대체된다.
    

    bind

    bind 함수는 함수가 가르키는 this만 바꾸고 호출은 하지 않는다.

    var obj1 = {
        name: "obj1",
        funcThis: function() {
            console.log(this);
        }
    };
    var obj2 = {
        name: "obj2"
    };
    var func = obj1.funcThis.bind(obj2);
    func(); // Object {name: "obj2"}
    

    obj1 의 funcThis 메서드를 obj2 객체로 가져와서 func 변수에 할당했다. func 함수를 실행하면 obj2 객체가 출력된다.

  • [JavaScript] 생성자 함수에서의 This

    [JavaScript] 생성자 함수에서의 This

    정의

    객체를 생성하는 방법은 크게 객체 리터럴 방식과 사용자 정의 생성자 함수 방식, 객체 생성자 함수 방식이 있다.

    // 리터럴
    var obj = {};
    
    // 사용자 정의 생성자 함수
    var obj = new Func();
    
    // 객체 생성자 함수
    var obj = new Object();
    

    이 세가지 중 사용자 정의 생성자 함수 방식에서의 this를 알아보겠다.

    일반 함수의 this

    일반 함수에서의 this는 window를 가리킨다.

    function func() {
        console.log(this); // window
    };
    func();
    

    생성자 함수의 this

    생성자 함수는 기존 함수에 new 키워드를 붙여서 호출하면 해당 함수는 생성자 함수로 동작한다. 생성자 함수에서의 this는 생성자 함수를 통해 생성되어 반환되는 객체에 바인딩 된다. 때문에 new 키워드를 잘못 사용해 원치 않은 상황이 나타날 수 있는데, 이를 피하기 위해 생성자 함수 이름의 첫 글자는 대문자로 작성하기를 권고하고 있다.

    function Func(name) {
      this.name = name;
      console.log(this); // Func {name: "foo"}
      console.log(this.name); // foo
    };
    var obj = new Func("foo");
    

    References

    자바스크립트에서 사용되는 this에 대한 설명 1
    함수 호출과 this

  • [JavaScript] 자바스크립트 This

    [JavaScript] 자바스크립트 This

    정의

    this는 함수가 호출되면 함수 내부로 암묵적으로 전달 되며, 함수를 호출한 방식에 의해 this에 바인딩할 객체가 동적으로 결정된다. 다시 말해, 함수를 선언할 때 this에 바인딩할 객체가 정적으로 결정되는 것이 아니고, 함수를 호출할 때 함수가 어떻게 호출되었는지에 따라 this에 바인딩할 객체가 동적으로 결정된다.

    this가 만들어지는 경우

    • 일반 함수에서의 this
    • 중첩 함수에서의 this
    • 이벤트 리스너에서의 this
    • 메서드에서의 this
    • 메서드 내부의 중첩 함수의 this

    일반 함수에서의 this

    function func() {
      console.log(this); // window
    }
    func();
    

    일반 함수 내부에서는 this는 전역객체인 window가 된다.

    중첩 함수에서의 this

    function func() {
        function func2() {
            console.log(this); // window
        }
        func2();
    }
    func();
    

    일반 중첩 함수에서의 this 도 window 가 된다.

    이벤트 리스너에서의 this

    document.addEventListener('click', function() {
        console.log(this); // #document
    });
    

    이벤트 리스너에서의 this는 이벤트를 발생시킨 객체가 된다.

    메서드에서의 this

    var obj = {
        func: function() {
            console.log(this); // {func: ƒ};
        }
    }
    obj.func();
    

    메서드에서의 this는 메서드를 호출한 객체가 된다.

    메서드 내부의 중첩 함수의 this

    var obj = {
        func: function() {
            function func2() {
                console.log(this); // window
            }
            func2();
        }
    }
    obj.func();
    

    메서드의 this와는 다르게 window를 가리킨다. 메서드의 내부함수는 결국 중첩함수이기 때문에 window를 바라본다. 이를 방지하기 위해선 아래 방법으로 해결할 수 있다.

    var obj = {
      func: function() {
          var that = this;
          function func2() {
              console.log(that); // {func: ƒ}
          }
          func2();
      }
    }
    obj.func();
    

    메서드를 포함한 객체를 참조하도록 부모함수의 this를 내부함수가 접근 가능한 변수에 저장하면 된다. 보통 관례상 this 값을 저장하는 변수의 이름을 that 이라고 선언한다.
    자바스크립트는 위와 같은 바인딩의 한계를 극복하려고 this 바인딩을 명시적으로 할 수 있도록 call과 apply 메서드를 제공한다. 제이쿼리 등 자바스크립트 라이버리들의 경우 bind 메서드를 통해, 사용자가 원하는 객체를 this에 바인딩 하는 기능을 제공하고 있다.

  • [jQuey] IOS 스크롤 방지

    [jQuey] IOS 스크롤 방지

    모달 팝업에서 백그라운드 영역의 스크롤을 방지하고자 할 때 Android는 body에 overflow: hidden 속성만 줘도 스크롤이 방지되지만 IOS의 경우는 먹히지 않는다.
    이럴 경우 body를 position: fixed 로 고정시킨 뒤 스크롤, 터치, 마우스 휠 이벤트를 막아버리면 된다.

    CSS

    .scrollOff {
        position: fixed;
        overflow: hidden;
        height: 100%;
    }
    

    JavaScript

    // ios 스크롤 방지
    $('body').addClass('scrollOff').on('scroll touchmove mousewheel', function (e) {
        e.preventDefault();
    });
    
    // ios 스크롤 방지
    $('body').removeClass('scrollOff').off('scroll touchmove mousewheel');
    
  • [JavaScript] 배열 메서드

    [JavaScript] 배열 메서드

    실제 사용되는 배열 메서드는 훨씬 많지만 간략하게라도 아래에 정리하였다.

    concat

    배열을 하나로 합칠 때 사용한다. 인자로 들어온 배열의 순서대로 합쳐진다.

    var arr1 = [1, 2, 3];
    var arr2 = [4, 5, 6];
    var arr3 = [7, 8, 9];
    
    var wrap = arr1.concat(arr2, arr2, arr3);
    
    console.log(wrap);
    // (12) [1, 2, 3, 4, 5, 6, 4, 5, 6, 7, 8, 9]
    
    console.log(arr1);
    // (3) [1, 2, 3]
    

    forEach

    주어진 함수를 배열 요소 각각에 대해 실행한다.

    var arr = ['a', 'b', 'c'];
    
    arr.forEach(function (element) {
        console.log(element);
        // a
        // b
        // c
    });
    

    map

    배열을 반복하고, 콜백함수가 리턴한 값으로 새 배열을 반환한다.

    var users = [
        { name: '철수', age: 20 },
        { name: '영희', age: 25 },
        { name: '민수', age: 23 },
        { name: '주연', age: 27 }
    ];
    
    var usersName = users.map(function (element) {
        return element.name;
        // 각 요소의 nama 값만 반환
    });
    
    console.log(usersName);
    // (4) ["철수", "영희", "민수", "주연"]
    

    filter

    배열을 반복하고, 콜백함수의 리턴값이 true인 요소로만 구성된 새 배열을 반환한다. 아래 예제는 age가 23 초과인 요소만 반환하였다.

    var users = [
        { name: '철수', age: 20 },
        { name: '영희', age: 25 },
        { name: '민수', age: 23 },
        { name: '주연', age: 27 }
    ];
    
    var usersOld = users.filter(function (element) {
        return element.age > 23;
    });
    
    console.log(usersOld);
    // (2) [{…}, {…}]
    // 0: {name: "영희", age: 25}
    // 1: {name: "주연", age: 27}
    // length: 2
    // __proto__: Array(0)
    

    sort

    배열을 반복하고, 콜백함수의 리턴값이 true인 요소로만 구성된 새 배열을 반환한다. 아래 예제는 age가 23 초과인 요소만 반환하였다.

    var arr = [5, 2, 1, 3, 10, 4];
    
    arr.sort(function(a, b) {
        return a - b;
        // 오름차순
    });
    console.log(arr);
    // (6) [1, 2, 3, 4, 5, 10]
    
    arr.sort(function(a, b) {
        return b - a;
        // 내림차순
    });
    console.log(arr);
    // (6) [10, 5, 4, 3, 2, 1]
    

    indexOf

    인자로 전달된 요소와 매치되는 첫번째 요소의 인덱스를 반환한다. 일치하는 요소가 없으면 -1을 반환한다.

    var arr = ['a', 'b', 'c', 'd'];
    
    console.log(arr.indexOf('c')); // 2
    console.log(arr.indexOf('e')); // -1
    

    every

    함수의 리턴값이 false가 될 때 까지 배열 요소 각각에 대해 함수를 실행한다.

    var arr = [1, 2, 3, 4, 5];
    
    arr.every(function(element) {
        console.log(element);
        // 1
        // 2
        // 3
        return element < 3;
    });
    

    References

    JavaScript 배열 메서드 ( Array method )
    Array.prototype.forEach()
    알아두면 좋은 자바스크립트 배열 메서드

  • [Regexp] 정규표현식

    [Regexp] 정규표현식

    정의

    정규표현식은 문자열에 포함된 문자 조합을 찾기 위해 사용되거나 그 문자열을 다른 문자열로 치환해 주는 패턴이다. 예를 들면 회원가입 화면에서 사용자로 부터 입력받는 전화번호가 유효한지 체크할 필요가 있을 때 정규표현식을 사용하면 간단하게 처리할 수 있다.

    var tel = '0101234567팔';
    var regExp = /^[0-9]+$/;
    
    console.log(regExp.test(tel)); // false
    

    정규표현식은 하나의 언어라고 할 만큼 모든것을 다루기에는 너무 방대하다. 정규표현식 패턴은 zvon의 정규표현식 tutorials에서 확인할 수 있다.

    정규표현식 사용법

    정규표현식은 리터럴 표기법과 생성자 함수로 생성할 수 있다.

    리터럴 방식

    var re = /ab + c/;
    

    리터럴 방식은 스크립트가 불어와질 때 컴파일 된다. 정규직이 상수라면 이렇게 사용하는 것이 성능을 향상시킨다.

    생성자 함수 방식

    var re = new RegExp("ab + c");
    

    생성자 함수 방식은 정규식이 실행 시점에 컴파일 된다. 정규식의 패턴이 변경될 수 있는 경우, 혹은 사용자 입력과 같이 다른 출처로부터 패턴을 가져와야 하는 경우에 생성자 함수 방식을 쓴다. 정규 표현식 리터럴은 아래와 같이 표현한다.

    var re = /pa/i;
    

    / 는 시작, 종료기호 이며, pa는 패턴, i는 프래그 이다.

    정규표현식 메서드

    자바스크립트에서 정규표현식 패턴들은 RegExpexec 메서드와 test 메서드, 그리고 Stringmatch메서드, replace메서드, search메서드, split 메서드와 함께 쓰인다.

    메서드설명
    RegExp.exec()어떤 문자열에서 정규표현식과 일치하는 문자열 검색을 수행한다. 결과로 배열을 리턴하거나 null을 반환한다.
    RegExp.test()대상 문자열 속에 일치하는 문자열이 포함되어 있는지 검사하고 true 또는 false를 반환한다.
    String.match()문자열이 정규식과 매치되는 부분을 검색한다.
    String.replace()대응되는 문자열을 찾아 다른 문자열로 치환하는 String 메서드이다.
    String.search()대응되는 문자열이 있는지 검사하는 String 메서드 이다. 대응된 부분의 인덱스를 반환한다. 대응되는 문자열을 찾지 못했다면 -1을 반환한다.
    String.split()정규식 혹은 문자열로 대상 문자열을 나누어 배열로 반환하는 String 메서드이다.

    정규표현식 메서드 예시

    var str = 'this is a pen.';
    var regexr = /is/ig;
    
    // RegExp 객체의 메서드
    console.log(regexr.exec(str)); // ["is", index: 2, input: "this is a pen.", groups: undefined]
    console.log(regexr.test(str)); // true
    
    // String 객체의 메서드
    console.log(str.match(regexr)); // (2) ["is", "is"]
    console.log(str.replace(regexr, 'is')); // this is a pen.
    console.log(str.search(regexr)); // 2
    console.log(str.split(regexr)); // (3) ["th", " ", " a pen."]
    

    References

    정규표현식
    5.26 RegExp 정규표현식
    정규 표현식

  • [JavaScript] 중첩 반복문(별찍기)

    [JavaScript] 중첩 반복문(별찍기)

    중첩 반복문의 별찍기 예제이다. 알고리즘 기초 문제로 자주 등장하는 예제중 하나이다. 우선 하단의 별모양으로 찍어보자

    *
    **
    ***
    ****
    *****
    ******
    *******
    ********
    *********
    **********
    

    나는 프로그래밍을 처음 배울때 아래처럼 코드를 작성했었다.

    var star = '';
    for (var i = 0; i < 10; i++) {
        star += '*';
        console.log(star);
    }
    

    *이 저장될 star를 선언한뒤 하나씩 *을 한번씩 추가한뒤 반복문이 끝나지 않고 바로 콘솔창으로 출력이 된다. 콘솔창에 10번 출력되면 되는줄 알고 반복문을 왜 중첩으로 사용해야 되지? 라는 짧은 생각을 했었던 적이 있었다.
    화면에 출력하는 것도 함수를 호출하는 것이기 때문에 10번 출력한다는 것은 그만큼 비효율적이다. 반복문이 돌아갈때마다 출력하는 것이 아닌, 구해진 값을 담아 한번에 출력해야 좀 더 효율적인 방법이라 할 수 있다. 이렇게 하기 위해선 반복문이 중첩으로 사용되어야 한다.

    기본 별찍기

    var star = '';
    for (var i = 0; i < 10; i++) {
        for (var j = 0; j <= i; j++) {
            star += '*';
        }
        star += '\n';
    }
    console.log(star);
    

    코드를 살펴 보자

    외부 반복문에서 i값이 0인 상태로 내부 반복문으로 들어 간다. j가 0으로 선언되고 j와 i를 비교한다. j와 i값은 0과 0으로 같기 때문에 star에 *이 한번 대입되고 내부 반복문을 빠져 나온뒤 줄바꿈문자인 \n을 star에 대입시킨다. 외부 반복문이 다시 실행된다. i값이 +1 되어 1인 상태에서 다시 내부함수로 들어간다. 내부반복문의 조건은 j값이 i보다 같거나 클 때 실행이 된다. j값은 0으로 초기화되었기 때문에 i값이 1이므로 star에 *을 두번 대입하고 내부반복문을 빠져나오게 되고 다시 \n을 대입하여 줄바꿈이 이루어진다.

    이 과정을 반복하여 열번째 *을 대입하고 마지막으로 결과 값을 콘솔창에 뿌리게 된다. 콘솔창을 보면 제대로 출력된 결과물을 볼 수 있다.

    두개씩 별찍기

    여기서 응용을 하여 아래 모양으로 출력해 보자

    *
    ***
    *****
    *******
    *********
    

    별이 2개씩 추가되었다. 외부반복문의 i값이 0일때 별을 한번 찍고, 이후에는 j값이 2의 배수 일때만 내부 반복문을 실행하는 조건을 달아주면 된다.

    var star = '';
    for (var i = 0; i < 10; i++) {
        for (var j = 0; j <= (i * 2); j++) {
            star += '*';
        }
        star += '\n';
    }
    console.log(star);
    

    정상적으로 출력되는걸 확인할 수 있다.

    역삼각형 별찍기

    아래처럼 역삼각형으로 나타낼려면 어떻게 되는지 알아보자

    ***********
    **********
    *********
    ********
    *******
    ******
    *****
    ****
    ***
    **
    *
    
    var star = '';
    for (var i = 10; i >= 0; i--) {
        for (var j = 0; j <= i; j++) {
            star += '*';
        }
        star += '\n';
    }
    console.log(star);
    

    i값을 10으로 설정한 뒤 i값이 0과 같아질때까지 i값을 빼주면 된다. 정상적으로 코드가 출력되는걸 확인할 수 있다.

    가운데 정렬 삼각형 별찍기

    가운데 정렬 삼각형을 만들어 보자

        *
       ***
      *****
     *******
    *********
    

    5줄로 만들어진 삼각형 이다. 차근차근 살펴 보자, 왼쪽부터 공백이 들어가고 별은 처음 한번 찍힌 뒤 2개씩 늘어나면서 찍힌다. 이제 코드를 확인해 보자

    var star = '';
    for (var i = 0; i < 5; i++) {
        for (var j = 4; j > i; j--) {
            star += ' ';
        }
        for (var k = 0; k <= (i * 2); k++) {
            star += '*';
        }
        star += '\n';
    }
    console.log(star);
    

    별과 공백이 대입될 star를 선언한 뒤 외부 반복문이 실행 된다. 5줄인 삼각형을 만들계획이니 외부 반복문은 5번만 실행해야 된다. i값이 5보다 작을 때 까지만 조건을 달아주고 내부 반복문으로 들어가고, 공백이 들어갈 반복문이 첫번째로 나온다. 5줄인 삼각형이니 공백은 4칸 대입 다음에 별이 한개 대입되고, 다음에는 공백 3칸에 별 세개, 이런식으로 반복되서 마지막 5줄째에는 공백이 없고 별만 9개가 대입되도록 짜면 될 것이다. 첫번째 내부 반복문에서 공백을 star에 4번 대입하고 빠져 나온다. 두번째 내부 반복문에서는 처음 별 한개를 대입한 뒤 이후에는 2의 배수만큼 별을 대입한다. 두번째 내부 반복문을 빠져나오면 줄바꿈이 대입되고, 외부 반복문이 다섯번 반복되면 이제 콘솔창에 결과값을 출력하게 된다.

    다이아몬드 별찍기

    이제 위에 예제를 응용하여 다이아몬드 형태를 만들어 보도록 하자, 복잡한 예제인 만큼 차근 차근 해석해 보겠다.

        *
       ***  
      *****  
     *******
    *********
     *******
      *****
       ***
        *
    

    몇줄인지 보면 아홉줄이다. 9번 실행되는 반복문을 먼저 선언해 주자

    var star = '';
    for (var i = 0; i < 9; i++) {
    
    }
    

    다이아몬드 형태는 별이 다섯번째 줄까지 증가하다가 그 다음부터는 감소한다. i값이 4가 될 때까지만 별을 증가시켜 주는 조건문을 달고 내부 반복문을 선언해 주자

    var star = '';
    for (var i = 0; i < 9; i++) {
        if (i < 5) {
            for (var j = 4; j > i; j--) {
                star += ' ';
            }
            for (var k = 0; k <= i * 2; k++) {
                star += '*';
            }
            star += '\n';
        }
    }
    console.log(star);
    
        *
       ***
      *****
     *******
    *********
    

    위처럼 제대로 출력된다. 이전 예제에서 해봤기 때문에 어렵지 않게 작성할 수 있다. 이제 i값이 5가되면 별이 감소하는 반복문을 추가한다. 공백을 먼저 넣을 것이다. 눈으로 확인하기 쉽게 우선 공백을 – 로 표기하겠다.

    var star = '';
    for (var i = 0; i < 9; i++) {
        if (i < 5) {
            for (var j = 4; j > i; j--) {
                star += ' ';
            }
            for (var k = 0; k <= i * 2; k++) {
                star += '*';
            }
            star += '\n';
        } else {
            for (var j = 4; j < i; j++) {
                star += '-';
            }
            star += '\n';
        }
    }
    console.log(star);
    

    i값이 4까지만 첫번째 조건문이 실행되고 i값이 5부터는 아래 조건문이 실행된다. 2번째 조건문 반복문을 만나서 j와 i값을 비교한다. j는 4로 선언되고 i는 5 이다. j는 i보다 작다. 참이다. 공백이 한번 찍히고 다시 돌면서 비교를 한다. j는 i와 같은 값인 5가 된다. 거짓이다. 반복문을 빠져나오고 줄바꿈을 한뒤 다시 외부 반복문이 i값을 증감 한다. 이 과정을 반복하면서 아래 모양처럼 출력된다.

        *
       ***
      *****
     *******
    *********
    -
    --
    ---
    ----
    

    이제 – 을 공백처리 한 후 별을 찍는 반복문을 추가 해 보자

    var star = '';
    for (var i = 0; i < 9; i++) {
        if (i < 5) {
            for (var j = 4; j > i; j--) {
                star += ' ';
            }
            for (var k = 0; k <= i * 2; k++) {
                star += '*';
            }
            star += '\n';
        } else {
            for (var j = 4; j < i; j++) {
                star += ' ';
            }
            for (var k = 9; k > i; k--) {
                star += '*';
            }
            star += '\n';
        }
    }
    console.log(star);
    

    여기서 좀 복잡해진다. 우선 별 반쪽만 찍어보도록 해보자

        *
       ***
      *****
     *******
    *********
     ****
      ***
       **
        *
    

    아래 조건의 반복문은 i가 5부터 8까지만 4번 실행된다. i값이 5일때 k값과 비교를 한다. k는 9이므로 참이다. 별을 한번 찍고 k값은 차례로 내려간다. k값이 9, 8, 7, 6이 될때까지 별을 네번 찍고 반복문을 빠져 나온다. 이 과정을 거쳐 위 모양처럼 별이 출력된다. 하지만 별은 2개씩 찍혀야 된다. 조건문에 2를 곱해보자

    var star = '';
    for (var i = 0; i < 9; i++) {
        if (i < 5) {
            for (var j = 4; j > i; j--) {
                star += ' ';
            }
            for (var k = 0; k <= i * 2; k++) {
                star += '*';
            }
            star += '\n';
        } else {
            for (var j = 4; j < i; j++) {
                star += ' ';
            }
            for (var k = 9; k > i * 2; k--) {
                star += '*';
            }
            star += '\n';
        }
    }
    console.log(star);
    
        *
       ***
      *****
     *******
    *********
     
      
       
        
    

    출력되지 않는다. 이유는 i값이 배가 되어 k값보다 훨 씬 커지기 때문이다. 처음 비교할때 k값은 9인데 i값은 10이기 때문이다. i값의 최대치는 8인데 배가 되니 16보다 큰 수가 k값으로 대입되어야 한다. 17을 대입해 주자

    var star = '';
    for (var i = 0; i < 9; i++) {
        if (i < 5) {
            for (var j = 4; j > i; j--) {
                star += ' ';
            }
            for (var k = 0; k <= i * 2; k++) {
                star += '*';
            }
            star += '\n';
        } else {
            for (var j = 4; j < i; j++) {
                star += ' ';
            }
            for (var k = 17; k > i * 2; k--) {
                star += '*';
            }
            star += '\n';
        }
    }
    console.log(star);
    

    k값은 17, i는 5 * 2 = 10 이다. 참이다. 별을 한번 찍고, 다시 비교, 16 > 10 이다. 이과정을 네번 반복하고 계속 찍어 내려간다. 마지막 i값이 8일때 k는 17 i는 8*2 = 16 참이다. 다시 돌아서 k–은 16 이다. 16 < 16 거짓이다. 반복문을 빠져 나온다. 제대로 출력되는 것을 확인할 수 있다.

  • [JavaScript] 구구단 출력하기

    [JavaScript] 구구단 출력하기

    자바스크립트로 중첩 반복문을 활용한 구구단 출력하기 예제이다. 예전에 면접볼 때 코딩 테스트에서 해당 문제가 나와 적어본다. 우선 반복문으로 2단의 값을 출력해 보자

    for (var i = 1; i <= 9; i++) {
        console.log(2 * i);
    }
    
    2  
    4  
    6  
    8  
    ...
    

    정상적으로 출력된다. 이제 이 값을 하나의 변수에 넣어 보자

    var result = '';
    for (var i = 1; i <= 9; i++) {
        result += 2 * i + '\n';
    }
    console.log(result);
    

    result에 2단의 값을 차례대로 대입하였더니 정상적으로 출력된다. 이제 답 말고 구구단식도 같이 넣어 보자

    var result = '';
    for (var i = 1; i <= 9; i++) {
        result += '2' + 'x' + i + '=' + 2 * i + '\n';
    }
    console.log(result);
    
    2 x 1 = 2  
    2 x 2 = 4  
    2 x 3 = 6  
    2 x 4 = 8  
    2 x 5 = 10  
    2 x 6 = 12  
    2 x 7 = 14  
    2 x 8 = 16  
    2 x 9 = 18  
    

    제대로 출력된다. 이제 중첩 반복문을 활용하여 1단부터 9단까지의 모든 구구단을 출력해 보자

    var result = '';
    for (var i = 1; i <= 9; i++) {
        for (var j = 1; j <= 9; j++) {
            result += i + 'x' + j + '=' + i * j + '\n';
        }
    }
    console.log(result);
    

    로직을 살펴보면, result가 선언되고 외부 반복문이 시작된다. i가 1인 상태에서 내부 반복문으로 들어간다. result에 대입되는 값을 해석해 보면 i에 문자열 x을 더하고 j값을 더한뒤 문자열 = 을 더하고 값을 더한뒤 줄바꿈 처리를 했다. 내부 반복문이 다시 실행된다. i값이 1상태에서 j는 2에서 반복문이 실행된다. i는 몇단인지 구분되는 값이 된다. 이런식으로 중첩되어 값을 계산하고 마지막엔 콘솔창에 값을 출력하게 된다.

  • [JavaScript] 호출 스택(Call Stack)

    [JavaScript] 호출 스택(Call Stack)

    호출 스택

    호출 스택이란 함수의 호출을 기록하는 자료구조이다. 기본적으로 우리가 프로그램 안에서 위치한 곳이며, 만약 우리가 어떤 함수를 실행시킨다면, 우리는 스택 위에 무언가를 올리는(push) 행위를 하는 것이다. 그리고 우리가 함수로부터 반환을 받을 때, 우리는 스택의 맨 위를 가져오는(pop) 것이다.

    자료구조란 사전적인 의미는 자료(Data)의 집합의 의미하며, 각 원소들이 논리적으로 정의된 규칙에 의해 나열되며 자료에 대한 처리를 효율적으로 수행할 수 있도록 자료를 구분하여 표현한 것

    function func01() {
      throw new Error("Oops!");
    }
    function func02() {
      func01();
    }
    function func03() {
      func02();
    }
    func03();
    // Uncaught Error: Oops!
    // at func01 (index.html:27)
    // at func02 (index.html:30)
    // at func03 (index.html:33)
    // at window.onload (index.html:35)
    

    위 코드를 실행해보면 콘솔창에 빨간 애러 스택들이 나온다. 보통 그것들은 호출 스택의 현재 상태를 나타내며, 실패함 함수를 스택처럼 위에서 아래로 나타낸다.
    크롬 개발자 도구의 디버깅 기능을 활용하여 호출스택을 확인해 볼 수 있다.

    function func01() {
      console.log("hello");
    }
    function func02() {
      func01();
    }
    function func03() {
      func02();
    }
    debugger;
    func03();
    

    위 코드를 실행하면 디버깅 모드가 시작될 것이다. 개발자 도구에서 Sources 패널의 Call Stack을 확인해 보면 처음에는 (anonymous)이 출력되며, Step 버튼을 클릭하여 다음 함수를 들어갈 때 마다 스택이 점점 쌓이는 것을 볼 수 있다.

    func01
    func02
    func03
    (anonymous)
    

    그리고 함수를 빠져 나갈 때 마다 스택이 위에서 부터 빠져 나가는 것을 확인할 수 있다. 이러한 각 단계를 스택 프레임(Stack Frame) 이라고 한다.

    스택 오버플로우

    스택의 사이즈를 초과했을 때 발생하는 오류인데 보통 재귀를 호출했을 때 나타난다.

    function func() {
      func();
    }
    func();
    // Uncaught RangeError: Maximum call stack size exceeded
    // at foo (index.html:25)
    // at foo (index.html:26)
    // at foo (index.html:26)
    // at foo (index.html:26)
    // at foo (index.html:26)
    // at foo (index.html:26)
    // at foo (index.html:26)
    // at foo (index.html:26)
    // at foo (index.html:26)
    // at foo (index.html:26)
    

    엔진에서 이 코드를 실행할 때, func()에 의해서 func함수가 호출된다. 여기서 다시 func함수를 호출하고 반복적으로 함수를 호출하게 된다. 그러면 매번 실행할 때마다 호출스택에 func()가 쌓이며 최대 허용치를 넘으면 위처럼 에러를 발생시킨다.

    References

    Understanding Javascript Function Executions — Call Stack, Event Loop , Tasks & more
    자바스크립트 개발자라면 알아야 할 33가지 개념 #1 콜스택 (번역)
    자료구조 : 자료구조란? (Data Structure)
    자바스크립트의 동작원리: 엔진, 런타임, 호출 스택

  • [JavaScript] while 반복문

    [JavaScript] while 반복문

    정의

    while

    while문은 조건을 검사하여 true일경우 계속 구문을 실행시키는 반복문이다.

    while (조건) {
        구문
    }
    
    var i = 0;
    while (i < 3) {
        console.log(i);
        i++;
    }
    // 0
    // 1
    // 2
    

    do-while

    do-whilewhild문과 비슷하지만 처음은 조건과 상관없이 구문을 실행하고 이후에 조건을 검사하여 true일때 구문을 실행한다.

    do {
        구문
    } while (조건);
    

    처음 구문을 실행하여 0이 출력되고, i는 3이 아니므로 로직이 실행이 안된다.

    var i = 0;
    do {
        console.log(i);
    } while (i == 3);
    // 0