underscore 알아보기 (3)


안녕하세요.


계속해서 underscore 관련 포스팅을 이어나가겠습니다.


이번에 다룰 내용은 함수입니다.

bind, bindAll, partial, memoize 로 함수를 합성해보겠습니다.


1. _.bind( function, object, *arguments)

원본 function파라미터에 대한 새로운 wrapper함수를 반환합니다.



var accu = {
currentValue : 0,
add: function (val) {
this.currentValue += val;
},
substract: function(val) {
this.currentValue -= val;
}
};

accu.currentValue = 5;
accu.add(2);
accu.substract(3);
console.log('현재 값', accu.currentValue); // 현재값 4


var accTest1 = {
currentValue : 1
};


var accTest2 = {
currentValue : 2
};

var addFunction = _.bind(accumulator.add, accTest1);
var substractFunction = _.bind(accu.substract, accTest2);

addFunction(2);
substractFunction(3);

console.log('테스트1 ', accTest1.currentValue); // 테스트1값 3
console.log('테스트2 ', accTest2.currentValue); // 테스트 2값 -1


위의 코드를 보자.

5의 값을 설정해두고 2를 더하고 3을 빼서 4가 나오는 계산기 이다.


_.bind(사용할 함수, 바뀔 this값)인 셈이다.

this값을 바꿔주고 싶을때 사용하면 적절할것같다.


2. _.bindAll(object, methodNames)

여기에서 methodNames는 object 파라미터에 속한 함수명 파라미터 목록을 가지는 object 파라미터의 메소드명이다.

이 함수에서는 methodNames 파라미터에 명시된 메소드 전체가 object 파라미터에 설정되는 this값을 가지고 있다.

이는 이벤트 핸들러 컨텍스트에서 사용될 때 매우 유용하므로,

다음의 예제에서 accu 객체를 재사용하고 add1()과 substract1() 메소드를 ㅅ ㅐ롭게 정의하는 데 이벤트 핸들러로

사용하면 적합하다.

예제는 1번과 매우 비슷하다.


var accu = {
currentValue : 0,
add: function (val) {
this.currentValue += val;
},
substract: function(val) {
this.currentValue -= val;
},
add1 : function () {
this.currentValue += 1;
},
substract1 : function () {
this.currentValue -= 1;
}
};

accu.currentValue = 5;
accu.add(2);
accu.substract(3);
console.log('현재 값', accu.currentValue);


var addFunction = _.bindAll(accu, 'add1', 'substract1');


_.bindAll 이 되었다면 'add1'이나 'substract1'이 메서드가 호출될때 this.currentValue 값이 바뀔것이다.


3. _.partial(function, arguments)

_.bindAll과 비슷한데 this값을 미리 설정하지 않은 경우이다.

arguments 파라미터 중 _로 명시된 것들은 인수 값이 설정되지 않은 상태이고,

반환되는 함수가 사용 될때 사용되는 명시값으로 대체된다.



var propertyFormatter = (function() {
"use strict";

var extractDataPropertiesForDisplayAsArray = function(source, ignoreId) {
var propertiesForDisplay = [];
if (_.isNull(source) || _.isUndefined(source) ||
_.isEmpty(source) || (!ignoreId && !_.isNumber(source.id))) {
return propertiesForDisplay;
}

_.each(_.pairs(source), function(keyValue) {
var key = keyValue[0];
var value = keyValue[1];
if (_.isDate(value) || _.isBoolean(value) ||
_.isNumber(value) || _.isString(value)) {
propertiesForDisplay.push("Property '" +
key + "' of type: " + typeof value + " has value: " + value);
} else if (!_.isFunction(value)) {
propertiesForDisplay.push("Property: " + keyValue[0] + " cannot be displayed.");
}
});

return propertiesForDisplay;
};

var extractDataPropertiesForDisplayForAnyObject =
_.partial(extractDataPropertiesForDisplayAsArray, _, true);

return {
extractPropertiesForDisplayAsArray: function(source, ignoreId) {
var propertiesForDisplay = [];
if (_.isNull(source) || _.isUndefined(source) ||
_.isEmpty(source) || (!ignoreId && !_.isNumber(source.id))) {
return propertiesForDisplay;
}

_.each(_.pairs(source), function(keyValue) {
var key = keyValue[0];
var value = keyValue[1];
if (_.isDate(value) || _.isBoolean(value)
|| _.isNumber(value) || _.isString(value)) {
propertiesForDisplay.push("Property '"
+ key + "' of type: " + typeof value + " has value: " + value);
} else {
propertiesForDisplay.push("Property '" + key + "' cannot be displayed.");
}
});

return propertiesForDisplay;
},
extractDataPropertiesForDisplayAsArray: extractDataPropertiesForDisplayAsArray,
extractDataPropertiesForDisplayForAnyObject: extractDataPropertiesForDisplayForAnyObject,
extractPropertiesForDisplayAsString: function(source, ignoreId) {
if (_.isNull(source) || _.isUndefined(source)
|| _.isEmpty(source) || (!ignoreId && !_.isNumber(source.id))) {
return [];
}

return _.reduce(source, function(memo, value, key) {
if (memo && memo !== "") {
memo += "<br/>";
}
var isDate = typeof value === 'object' && value instanceof Date;
if (_.isDate(value) || _.isBoolean(value)
|| _.isNumber(value) || _.isString(value)) {
return memo + "Property: " + key +
" of type: " + typeof value + " has value: " + value;
}
return memo + "Property: " + key + " cannot be displayed.";
},
"");
}
};
}());


_.partial()의 파라미터를 보면 이 파라미터로 extractDataPropertiesForDisplayForAnyObject가 실행될 때

source값이 전달된다.

extractDataPropertiesForDisplayAsArray를 간단하게 대체할 수 있다.



4. _.memoize로 함수 합성하기

_.memoize(function, [hashFunction])은 최적화하는 방식을 제공한다.

이 함수를 사용하면 첫번째 함수 파라미터 값을 기본 key로 해서 function 호출의 결과를 메모리에 저장한다.

함수 _.memoize()를 사용하면 첫 번째 인수와 같은 값으로 호출 될때마다 저장된 결과를 반환하는

함수가 생성된다.

인수 목록이 더 복잡해지면, 선택적 파라미터인 [hashFunction]을 사용해 function 결과를 저장하는 데 사용되는 키를

현재 인수 목록 기반으로 정의 할 수 있다.



_.memoize(function, [hashFunction]) {

var fibonacci = _.memoize(function(n) {

return n < 2 ? n: fibonacci(n - 1) + fibonacci(n - 2);
});
}


피보나치를 생각하면 된다.


5. _.wrap(function, wrapper)

이 함수는 function 파라미터가 실행되기 전이나 실행된 후에 실행할 코드에 대한 함수를 생성한다.


_.wrap(함수, wrapper)

var hello = function (name) {

return "hello" + name
};
hello = _.wrap(hello, function (func) {
return "before, " + func("moe") ", after";
});
hello();




이처럼 내가 원하는 내용으로 감쌀수 있다.

jquery의 wrap 함수와 비슷한 구성을 가지고 있다.



후속 포스팅에서 계속 이어나가도록 하겠습니다.








 



New Multi-Channel Dynamic CMS