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
'Front-end > Javascript' 카테고리의 다른 글
[코어 자바스크립트] 06. 프로토타입(2) (1) | 2022.12.27 |
---|---|
[코어 자바스크립트] 06. 프로토타입(1) (0) | 2022.12.26 |
[코어 자바스크립트] 05. 클로저(2) (0) | 2022.12.21 |
[코어 자바스크립트] 05. 클로저(1) (0) | 2022.12.20 |
[코어 자바스크립트] 04. 콜백 함수(2) (0) | 2022.12.14 |