ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [JS] 함수 - 유효범위, 콜백, 클로저, arguments, 호출
    생활코딩/ 2020. 9. 3. 18:35

    [강의 출처] opentutorials.org/course/743/6583

     

    함수지향 - 생활코딩

    함수지향 카테고리의 하위 수업들은 함수형 언어로서 자바스크립트의 면모를 다룬다. 자바스크립트의 핵심적인 도구는 함수다. 자바스크립트의 함수는 매우 강력하다. 함수에 대한 이해 없이��

    opentutorials.org


     

    유효범위(Scope):

    -변수를 선언할 때 'var'는 해당 변수가 지역변수(local variable)임을 의미

    -한번 지역변수로 선언한 이후에는 해당 범위(지역) 내에서는 지역변수로 사용됨

    -자바스크립트는 함수 단위로 유효범위를 제공(if, for, while등의 블록 단위에는 제공 X)

    -유효범위는 선언 맥락에 따라 유효범위가 결정되는 Lexical Scoping 방식을 따른다

    var vscope = 'global';     // 전역변수
    function fscope(){
        var vscope = 'local';  // 지역변수
        document.write(vscope);
    };
    function fscope2(){
        document.write(vscope);
        vscope = 'local';
    };
    function fscope3(){
        document.write(vscope);
    };
    fscope();  // 'local': 가깝게 선언된 변수(지역변수) 우선 이용
    fscope2(); // 'global': 지역변수가 없기 때문에 전역변수 호출
    fscope3(); // 'local'; fscope2()에서 vscope 재정의 했기 때문에 현재 vscope 값은 'local'

    유효범위 관련하여 발생할 수 있는 이슈

     -예상치못한 다른 맥락에서 변수가 변경되어 무한루프 등의 상황 발생 가능

    function a (){
        i = 0;
    };
    for(var i = 0; i < 5; i++){
        a();
        document.write(i);
    };
    /* 무한루프
     * for문은 외부에 있기 때문에 for문의 i는 전역변수로 선언됨
     * 함수 a()는 (var가 없기 때문에) 전역변수 i의 값을 0으로 재할당함
     * 계속 i = 0 상태이므로 루프가 완료되지 않음 */

    전역변수를 사용해야 할 때 중복을 피하려면

     -전역변수를 생성 후 나머지 변수를 해당 변수의 프로퍼티 형태 생성하거나

     -변수가 필요한 경우 익명함수 내에 선언해서 전역변수를 만들지 않기

    (function(){
      var MYAPP = {};               /* 전역변수 하나를 생성해서 */
      MYAPP.calculator = {          /* 나머지 변수를 그 변수의 소속으로 넣어주거나 */
          'left': null,
          'right': null
      }
      MAPP.coordinate = {
          'left': null,
          'right': null
      }
      MYAPP.calculator.left = 10;
      MYAPP.calculator.right = 20;
      function sum(){
          return MYAPP.calculator.left + MYAPP.calculator.right;
      }
      document.write(sum));
    }())                            /* 아예 해당 변수를 익명함수 내에 선언해서 전역변수를 만들지 않거나 */

    JS는 Static Soping/Lexical Scoping (↔Dynamic Scoping)을 따르기 때문에

    함수를 호출한 위치는 해당 함수가 참조하는 지역변수에 영향을 주지 않는다 [참고]

    var i = 5;
    function a(){
        var i = 10;
        b();
    }
    function b(){
        document.write(i); // 정의될 때의 i는 var i = 5이므로
    }
    a();                   // 5: 사용시점에 따라 영향을 받지 않는다(Lexical Scoping)

     

    값으로서의 함수와 콜백

    -JS에서 함수는 객체로 처리되므로, 다른 객체나 배열에 포함되거나, 다른 함수의 인자나 리턴값으로 전달될 수 있다.

    -이런 특성을 first-class citizen or first-class object, first-class entity, first-class value라고 부른다.

    -콜백(callback): 함수를 호출하는 작업을 콜백이라고 하고, 그렇게 호출한 함수를 콜백함수라고 한다.

    function cal(mode){
        var funcs = {
            'plus': function(left, right){return left + right},
            'minus': function(left, right){return left - rigth}
        }
        return funcs[mode];
    }
    console.log(cal('plus')(2,1)); // 3
    /* cal('plus')는 funcs[mode]를 리턴하므로,
     * cal 내부의 프로퍼티인 funcs에 mode로 입력된 'plus'를 전달하여
     * funcs['plus']가 실행되고, 인자로 (2,1)이 전달된다 */

    -콜백은 비동기처리에 유용하게 사용된다. 자바스크립트의 경우 Ajax를 주로 이용한다.

    • 비동기처리(Asynchronous): To-do처럼, 특정 연산을 바로 진행하지 않고 모아뒀다가 여건이 될 때 처리하는 방식

     

    클로저(Closure)

    -내부함수가 외부함수의 맥락(Context: 지역변수 등) 에 접근가능한 특성

    -내부함수란 함수 내에 선언된 함수를 뜻함. 이때 내부함수를 갖고 있는 함수를 외부함수라고 부름

    function outter(){            // 외부함수
        var word = 'hello world';
        function inner(){         // 내부함수
             alert(word);
        }
        inner();
    }
    outter();
    
    /* 특정한 함수 내에서만 사용되는 함수가 있을 경우,
     * 따로 떨어뜨려놓으면 군집성이 떨어지므로 내부함수 형태로 선언하기도 함 */
     
    /* 위 함수의 경우 변수 word는 inner()의 입장에서는 전역변수이기 때문에
     * word가 outter()의 지역변수이더라도 inner() 안에서 접근이 가능함 */

    -특이하게 외부함수가 종료되더라도 내부함수가 외부함수의 맥락에 접근 가능한데, 이런 특성을 클로저라고 부름

    function outter(){
        var word = 'hello world';
        return function(){
            alert(word);
        }
    }
    inner = outter();
    inner(); // 'hello world'라고 정상 출력됨

    -클로저를 통해 아래와 같은 (python class 스타일) 객체 구현이 가능

    function bread(element){
        return {
            get_element: function(){  // 함수 bread는 종료되었으나
                return element;       // get_element는 bread의 맥락에 접근가능
            },
            set_element: function(_element){
                element = _element    // 함수 set_element는 bread의 지역변수 element를 변경함
            }
        }
    }
    
    console.log(cookie.get_element()); // 'Cheese'
    console.log(scone.get_element());  // 'Cheese'
    
    scone.set_element('Chocolate');
    
    console.log(cookie.get_element()); // 'Cheese'
    console.log(scone.get_element());  // 'Chocolate'
    
    /* 외부함수인 bread는 return 시점에서 종료되었으므로
     * 변수 element에 대한 접근은 메소드 get_element, set_element를 통해서만 가능하고,
     * 따라서 데이터 접근 채널이 한정되므로 안전해짐 */

    -클로저 관련하여 발생할 수 있는 이슈

    var arr = [], arr2 = []
    for(var i = 0; i < 5; i++){
      arr[i] = function(){      // 이 맥락에서 i는 외부변수에서 정의된 지역변수가 아니기 때문에 
        console.log(i);         // 이 구문 이후로도 값이 변할 수 있음
      },
      arr2[i] = function(id){
        return function(){     // function(id)는 function()를 리턴하는 시점에서 종료되어
         return id;            // id는 해당 시점의 i값을 가리키는 외부변수로 고정됨
        }
      }(i);
    }
    // 만약 여기에서 i = 7;처럼 i값을 재할당한다면 arr[index]()는 7을 리턴함
    for(var index in arr){
      console.log(arr[index]());   // 5 5 5 5 5
      console.log(arr2[index]());  // 0 1 2 3 4
    }

     

    arguments

    -함수의 매개변수(parameter)를 정의하지 않고 사용하거나, 인자의 수가 정의한 것과 달라도 에러가 나지는 않음

    -함수 내부에서 arguments라는 객체를 통해 인자에 접근 가능

    -함수.length -> 함수 정의시에 입력한 인자의 개수

    -arguments.length -> 호출시 입력받은 인자의 개수

    function sum(){
        var _sum = 0;
        for(var i = 0; i < arguments.length; i++){
            document.write(i+' : '+arguments[i]+'<br />');
            _sum += arguments[i];
        }
        return _sum;
    }
    document.write('result : ' + sum(1, 2, 3, 4, 5));
    
    
    function one(arg1){
        console.log(
            'one.length: ', one.length,
            'arguments.length: ', arguments.length
        );
    }
    one('val1', 'va12'); // one.length : 1 arguments.length: 2

     

    함수를 호출하는 다른 방법

    -함수는 function 객체의 인스턴스이므로, function의 apply 메소드로도 호출이 가능

    o1 = {val1:1, val2:2, val3:3}
    o2 = {v1:10, v2:50, v3:100, v4:25}
    function sum(){
        var _sum = 0;
        for(name in this){      // 이 경우 this의 맥락은 호출할 때 정해짐
            _sum += this[name];
        }
        return _sum;
    }
    console.log(sum.apply(o1)) // 6
    console.log(sum.apply(o2)) // 185
    
    /* apply 메소드의 첫번째 인자에 입력된 o1, 혹은 o2는
     * var this = o1; 처럼 정의된 것과 같은 효과,
     * 함수가 마치 o1.sum, o2.sum처럼 적용된 것과 같은 효과를 냄 */

     

    댓글

Designed by Tistory.