3) 부분 적용 함수

  -인자를 n개 받는 함수에 m개의 인자만 넘겨 기억시켰다가 나중에 나머지 (n-m)개의 인자를 넘기면 비로소 함수의 실행 결과를 얻을 수 있게끔 하는 함수

  -예시(bind 메서드)

var add = function () {
  var result = 0;
  for (var i = 0; i < arguments.length; i++) {
    result += arguments[i];
  }
  return result;
};
var addPartial = add.bind(null, 1, 2, 3, 4, 5);
console.log(addPartial(6, 7, 8, 9, 10));  //55

   →addPartial 함수는 인자 5개를 미리 적용(첫번째 인자 null은 this에 대한 것)

   →추후 나머지 인자들을 전달하면 모든 인자를 모아 원래의 함수가 실행

   →add 함수는 this를 사용하지 않아 bind 메서드만으로 구현되었지만 this 값을 변경할 수 밖에 없어, 메서드에 사용하려면 this에 관여하지 않도록 만들어야 함

 

  -예시(부분 적용 함수 구현 - 1)

var partial = function () {
  var originalPartialArgs = arguments;
  var func = originalPartialArgs[0];
  if (typeof func !== 'function') {
    throw new Error('첫 번째 인자가 함수가 아닙니다.');
  }
  return function () {
    var partialArgs = Array.prototype.slice.call(originalPartialArgs, 1);
    var restArgs = Array.prototype.slice.call(arguments);
    return func.apply(this, partialArgs.concat(restArgs));
  };
};

var add = function () {
  var result = 0;
  for (var i = 0; i <arguments.length; i++) {
    result += arguments[i];
  }
  return result;
};

var addPartial = partial(add, 1, 2, 3, 4, 5);
console.log(addPartial(6, 7, 8, 9, 10)); // 55

var dog = {
  name: '강아지',
  greet: partial(function(prefix, suffix) {
    return prefix + this.name + suffix;
  }, '왈왈, ')
};
dog.greet('입니다!');  // 왈왈, 강아지입니다!

   →addPartial에 partial 함수에 적용할 원본 함수와 미리 적용할 인자들을 전달

   →이후 addPartial에 나머지 인자들을 partial 함수가 반환하는 함수(부분 적용 함수)에 넘겨주어 이들을 한데 모아(concat) 원본 함수(add) 호출(apply)

   →실행 시점의 this를 그대로 반영하여 this에는 아무런 영향을 주지 않음

 

  -예시(부분 적용 함수 구현 - 2): 추후에 넘길 인자의 위치를 정하기

Object.defineProperty(window, '_', {
  value: 'EMPTY_SPACE',
  writable: false,
  configurable: false,
  enumerable: false
});

var partial2 = function () {
  var originalPartialArgs = arguments;
  var func = originalPartialArgs[0];
  if (typeof func !== 'function') {
    throw new Error('첫 번째 인자가 함수가 아닙니다.');
  }
  return function () {
    var partialArgs = Array.prototype.slice.call(originalPartialArgs, 1);
    var restArgs = Array.prototype.slice.call(arguments);
    for (var i = 0; i < partialArgs.length; i++) {
      if(partialArgs[i] === _) {
        partialArgs[i] = restArgs.shift();
      }
    }
    return func.apply(this, partialArgs.concat(restArgs));
  };
};

var add = function () {
  var result = 0;
  for (var i = 0; i < arguments.length; i++) {
    result += arguments[i];
  }
  return result;
};

var addPartial = partial2(add, 1, 2, _, 4, 5, _, _, 8, 9);
console.log(addPartial(3, 6, 7, 10)); // 55

var dog = {
  name: '강아지',
  greet: partial2(function(prefix, suffix) {
    return prefix + this.name + suffix;
  }, '왈왈, ')
};
dog.greet(' 배고파요!');  // 왈왈, 강아지 배고파요!

   →전역객체에서 Object.defineProperty를 이용해 '_'를 비워놓음의 의미로 준비(삭제, 변경 등 접근에 대한 방어 차원에서 여러 프로퍼티 설정 해둠)

   →또, partial2 함수 내에서 if 문을 통해 인자가 _인 부분을 골라내어 restArgs의 자리인 것을 명시적으로 나타냄

※ '_'문자를 '비워놓음'의 의미로 바꿀 때, Object.defineProperty 외에 Symbol.for을 사용해도 됨

(function () {
  var EmptySpace = Symbol.for('EMPTY_SPACE'); // 기존 전역 심볼공간에 'EMPTY_SPACE'라는 심볼을 새로 생성
  console.log(EmptySpace);
})();

(function () {
  console.log(Symbol.for(EmptySpace)); // 기존 전역 심볼공간에 'EMPTY_SPACE'라는 심볼이 있으므로 해당 값을 참조
})();

return function () {
  // ...
  for (var i = 0; i < partialArgs.length; i++) 
    if(partialArgs[i] === Symbol.for('EMPTH_SPACE')) { // 바뀐 부분
      partialArgs[i] = restArgs.shift();
    }
  }
  // ...
};

// ...

var _ = Symbol.for('EMPTY_SPACE'); // 추가된 부분, '_'의 심볼을 'EMPTY_SPACE'로 설정
var addPartial = partial(add, 1, 2, _, 4, 5, _, _, 8, 9);
console.log(addPartial(3, 6, 7, 10));

 

  -디바운스: 짧은 시간동안 동일한 이벤트가 많이 발생할 경우 모두 처리하지 않고 가장 처음 또는 마지막 이벤트에 대해 한 번만 처리

   →scroll, wheel, mousemove, resize 등에 적용

   →실무에서 부분 적용 함수 사용에 적합

  -예시(디바운스)

var debounce = function (eventName, func, wait) {
  var timeoutId = null;
  return function (event) {
    var self = this;
    console.log(eventName, 'event 발생');
    clearTimeout(timeoutId);
    timeoutId = setTimeout(func.bind(self, event), wait);
  };
};

var moveHandler = function (e) {
  console.log('move event 처리');
};
var wheelHandler = function (e) {
  console.log('wheel event 처리');
};
document.body.addEventListener('mousemove', debounce('move', moveHandler, 500));
document.body.addEventListener('mousewheel', debounce('wheel', wheelHandler, 700));

   →디바운스 함수는 어떤 이벤트가 발생했는지 출력하기 위한 eventName, 이벤트에 따라 실행할 함수인 func, 마지막으로 발생한 이벤트인지 판단하기 위한 대기시간인 wait(ms)를 인자로 받음

   →timeoutID의 초기값은 null로 주며 선언

   →EventListener에 의해 호출될 함수를 return

   →self에 this 객체를 미리 저장해둠

   →함수 내부에서는 어떤 이벤트가 발생했는지 eventName을 출력

   →clearTimeout()을 사용하여 대기큐를 무조건 초기화해주는데, 처음 이벤트 발생 후 함수를 실행시킬 대기 시간 이전에 반복해서 이벤크가 발생한다면, 여기서 대기큐를 초기화시켜 다시 대기 시간을 계산할 수 있게 해줌

   →마지막으로 setTimeout()을 사용하여 대기시간(wait)만큼 지연시킨 후 func 호출, 대기시간 이전에 동일한 이벤트 발생 시 반환할 함수가 다시 실행되며 윗 줄에서 clearTimeout되고 다시 대기시간 계산

   →디바운스 함수에서 클로저로 처리되는 변수는 eventName, func, wait, timeoutId

 

 

 4) 커링 함수

  -커링 함수: 여러 인자를 받는 함수를 하나의 인자를 받는 함수로 나눠 순차적으로 호출될 수 있도록 체인 형태로 구성한 것

  -예시

var curry3 = function (func) {
  return function (a) {
    return function (b) {
      return func(a, b);
    };
  };
};

var getMaxWith10 = curry3(Math.max)(10);
console.log(getMaxWith10(8));      // 10
console.log(getMaxWith10(25));    // 25

var getMaxWith10 = curry3(Math.min)(10);
console.log(getMaxWith10(8));      // 8
console.log(getMaxWith10(25));    // 10

   →인자 하나를 더 받아 10과 비교하여 더 큰 값을 찾는 함수

   →curry3에 인자로 Math.max함수를 주고, curry3 함수 내의 첫번째 함수의 인자로 10을 주어 저장

   →이후, 10과 비교할 값을 두번째 함수의 인자로 줌과 동시에 원본 함수(Math.max) 실행

   →마지막에 반환되는 원본 함수에서 curry3 함수 내의 다른 함수 두 개에서 a와 b 변수를 참조하여 사용하므로 변수 a, b는 클로저로 처리됨

 

  -커링 함수에서 마지막 인자가 전달되기 전까지 원본 함수는 실행되지 않음

  -커링 함수의 단점으로 가독성이 떨어짐

var curry5 = function (func) {
  return function (a) {
    return function (b) {
      return function (c) {
        return function (d) {
          return function (e) {
            return func(a, b, c, d, e);
          };
        };
      };
    };
  };
};

var getMax = curry5(Math.max);
console.log(getMax(1)(2)(3)(4)(5));

   →다행히 화살표 함수로 같은 내용을 한 줄에 표시 가능

var curry5 = func => a => b => c => d => e => func(a, b, c, d, e);

 

  -커링 함수는 당장 필요한 정보만 받아서 전달하고, 또 필요한 정보가 들어오면 전달하는 식으로 원하는 시점까지 지연시켰다가 실행하고 싶을 때 유용

var getInformation = baseUrl => path => id => fetch(baseUrl + path + '/' + id);

   →HTML5의 fetch 함수는 url을 받아 해당 url에 HTTP 요청을 함

   →이때 baseUrl은 몇 개로 고정되지만 path와 id는 많은 값을 가지므로 baseUrl만 기억시킨 후 특정 path와 id만으로 서버 요청을 수행하도록 하면 효육적이고 가독성 높아짐

 

  -url 요청에서 커링 함수 활용 예시

var imageUrl = 'http://imageAddress.com/';
var productUrl = 'http://productAddress.com/';

// 이미지 path 설정
var getImage = getInformation(imageUrl);         // http://imageAddress.com/
var getEmoticon = getImage('emoticon');          // http://imageAddress.com/emoticon
var getIcon = getImage('icon');                        // http://imageAddress.com/icon

// 제품 path 설정
var getProduct = getInformation(productUrl);   // http://productAddress.com/
var getFruit = getProduct('fruit');                     // http://productAddress.com/fruit
var getVegetable = getProduct('vegetable');     // http://productAddress.com/vegetable

// id까지 설정하여 실제 url 불러오기
var emoticon1 = getEmoticon(100);                // http://imageAddress.com/emoticon/100
var emoticon2 = getEmoticon(102);                // http://imageAddress.com/emoticon/102
var icon1 = getIcon(205);                              // http://imageAddress.com/icon/205
var icon2 = getIcon(234);                              // http://imageAddress.com/icon/234
var fruit1 = getFruit(300);                             // http://productAddress.com/fruit/300
var fruit2 = getFruit(400);                             // http://productAddress.com/fruit/400
var vegetable1 = getVegetable(456);             // http://productAddress.com/vegetable/456
var vegetable2 = getVegetable(789);             // http://productAddress.com/vegetable789

+ Recent posts