이번에는 Prototype을 사용하며 꼭! 알아야 하지만 많은 분들이 모르시고 필요성을 모르시는 분들께 정말 중요한 자료가 될것 같네요.
Prototype에서는 Function이라는 클래스가 있습니다. 정말 몰라도 될것 같은 이름이죠. 기껏해야 function으로 함수 만드는 방법을 조금 확장한게 아닐까 싶은 클래스입니다.
하지만! 이것을 알면 여러분의 개발이 확실히 편해지는 멋진 녀석입니다. 한번 알아보도록 하죠.
바인딩이란 무엇인가? ———————————————————————————————–
Prototype에서 function과 관련하여 가장 중점을 두는 부분은 Binding입니다. 바인딩은 기본적으로 함수(메서드)가 실행될 때 this 키워드가 그 함수 자체가 되거나 혹은 다른 객체가 되는것을 말합니다. 당신은 bind()라는 메서드만 알면 여기에 관련된 모든 문제가 해결됩니다.
Prototype은 두가지 개발상의 문제를 해결하였습니다. 하나는 모든 함수(메서드)에 대하여 바인딩을 할수 있도록 하였다는것과 다른 한가지는 이벤트 핸들러에서 호출되는 함수(메서드)에 특화된 바인딩을 구현하였다는 점입니다.
Function Methods ————————————————————————————————-
argumentNames : 메서드나 함수에서 인자(파라미터)를 정의한 경우 해당 인자의 이름을 반환합니다.
[code]someFunction.argumentNames() -> Array[/code]
예제)
[code]var fn = function(foo, bar) {
return foo + bar;
};
fn.argumentNames();
//-> [‘foo’, ‘bar’]
Prototype.emptyFunction.argumentNames();
//-> [][/code]
bind : 다른 객체로 함수(메서드)를 포장하여 실행 스코프를 고정합니다.
[code]bind(thisObj[, arg…]) -> Function[/code]
보통 bind를 말하기를 this를 바꾼다라고 표현하기도 합니다만, 정확하게는 실행되는 위치, 즉 Scope를 고정한다고 말하는 것이 맞습니다.
문제가 있는 예제)
[code]window.name = “the window object”
function scopeTest() {
return this.name
}
// 글로벌 Scope로 함수 호출
scopeTest()
// -> “the window object”
var foo = {
name: “the foo object!”,
otherScopeTest: function() { return this.name }
}
foo.otherScopeTest()
// -> “the foo object!”[/code]
[code]// test함수가 foo.otherScopeTest() 메서드를 참조
window.test = foo.otherScopeTest
// 함수 호출, Scope는 글로벌(window)
test()
// -> “the window object”[/code]
예제)
[code]var obj = {
name: ‘A nice demo’,
fx: function() {
alert(this.name);
}
};
window.name = ‘I am such a beautiful window!’;
function runFx(f) {
f();
}
var fx2 = obj.fx.bind(obj);
runFx(obj.fx);
// -> alerts ‘I am such a beautiful window!’
runFx(fx2);
// -> alerts ‘A nice demo'[/code]
또한, 다음과 같이 필요한 인자를 얼마든지 추가할 수 있습니다. arguments로 받아서 사용할 수 있습니다.
[code]var obj = {
name: ‘A nice demo’,
fx: function() {
alert(this.name + ‘\n’ + $A(arguments).join(‘, ‘));
}
};
var fx2 = obj.fx.bind(obj, 1, 2, 3);
fx2(4, 5);
// 객체의 name과 “1, 2, 3, 4, 5″가 출력[/code]
여기서 알 수 있듯이 바인딩 후 인자는 계속 추가되는 식으로 사용되는것을 알 수 있습니다.
아직도 바인딩이 이해가 잘 안되신다면 다음의 포스팅을 봐보세요. 정말 잘 만들어져있습니다. [ 글보기 ]
bindAsEventListener : 이벤트 리스너에 특화된 바인딩 메서드입니다.
[code]bindAsEventListener(thisObj[, arg…]) -> Function[/code]
이 메서드를 이해하기 전에 바인딩에 대해 제대로 이해하지 못하고 있다면 우선 bind()메서드부터 이해하도록 하세요. 이 메서드는 이벤트 핸들러에 맞춰 만들어진 메서드입니다. this 오브젝트를 변경할 수 있으며 Event.observe등에 맞추어 사용될 수 있습니다.
예제)
[code]var obj = { name: ‘A nice demo’ };
function handler(e) {
var tag = Event.element(e).tagName.toLowerCase();
var data = $A(arguments);
data.shift();
alert(this.name + ‘\nClick on a ‘ + tag + ‘\nOther args: ‘ + data.join(‘, ‘));
}
Event.observe(document.body, ‘click’, handler.bindAsEventListener(obj, 1, 2, 3));
// obj 객체의 name이 출력되고 태그명이 소문자로 변경되어 출력
// 또한 추가로 입력된 인자인 ‘1, 2, 3’이 출력[/code]
curry : bind()와 마찬가지로 scope를 유지하는 메서드입니다. 한개 이상의 인자를 미리 입력해 둘 수 있습니다.
[code]curry(arg…) -> Function[/code]
예제)
[code]String.prototype.splitOnSpaces = String.prototype.split.curry(” “);
“foo bar baz thud”.splitOnSpaces();
//-> [“foo”, “bar”, “baz”, “thud”][/code]
defer : JS를 해석하는 인터프리터가 쉴때 함수(메서드)를 실행합니다.
[code]defer(arg…) -> Number[/code]
반환되는 ID값을 가지고 window.clearTimeout으로 중단 시킬 수 있습니다.
예제)
[code]function hideNewElement() {
$(‘inserted’).hide();
};
function insertThenHide(markup) {
$(‘container’).insert(markup);
// 위의 명령이 완료되어 DOM 트리가 완성되었을때 다음이 실행되어야 함
hideNewElement.defer();
}
insertThenHide(“<div id=’inserted’>Lorem ipsum</div>”);[/code]
이 메서드는 인터프리터의 Call Stack이 쉴때 메서드를 수행하게끔 하는 명령입니다. 위와 같은 경우에는 DOM 트리에 추가가 끝나지 않은 상황에서 hide()를 하게 되면 에러가 생기겠지요. 그것을 방지할 수 있습니다.
delay : 함수(메서드)를 실행시에 실행 시간을 지연 시킬 수 있습니다.
[code]delay(seconds[, arg…]) -> Number[/code]
이 메서드는 window.setTimeout과 동일한 역할을 합니다. 인터프리터가 쉬는 시간에 실행시키기 위해서라면 defer()를 사용하시기 바랍니다. defer와 마찬가지로 반환되는 ID값을 가지고 window.clearTimeout으로 중단 시킬 수 있습니다.
예제)
[code]// 이전 :
window.setTimeout(function() {
Element.addClassName(‘foo’, ‘bar’);
}, 1000);
// 이후 :
Element.addClassName.delay(1, ‘foo’, ‘bar’);
// 타임아웃 제거, 아래는 수행되지 않음
var id = Element.hide.delay(5, ‘foo’);
window.clearTimeout(id);[/code]
methodize : 이 메서드는 어떤 함수던지 메서드 형식으로 만들어 버립니다. 첫번째 인자는 this가 됩니다.
[code]someFunction.methodize() -> Function[/code]
예제)
[code]// 간단한 target 객체에 foo값을 담는 함수
var fn = function(target, foo) {
target.value = foo;
};
var object = {};
// 기존의 함수 사용
function fn(object, ‘bar’);
object.value
//-> ‘bar’
// object에 methodize를 이용해 새로운 메서드를 생성함
// 첫번째 인자는 this가 되므로 인자의 수가 한개 줄음
object.fnMethodized = fn.methodize();
object.fnMethodized(‘boom!’);
object.value
//-> ‘boom!'[/code]
wrap : 기존의 함수에 새로운 함수를 래핑합니다.
[code]wrap(wrapperFunction[, arg…]) -> Function[/code]
자바의 스프링 프레임워크를 사용하고 있는 저를 흥분되게 하는 메서드였습니다. 조금 우스운 수준이긴 하지만 자바스크립트에서 관점지향프로그래밍(AOP)를 지원하게 하는군요. 특정 메서드가 수행되기 전에 내가 원하는 다른 일을 처리 할 수 있습니다. 잘 쓰면 정말 동적인 멋진 어플리케이션을 만들 수 있을 것 같습니다.
예제)
[code]String.prototype.capitalize = String.prototype.capitalize.wrap(
function(proceed, eachWord) {
if (eachWord && this.include(” “)) {
// 하나하나의 단어들에 대해 capitalize를 수행함
return this.split(” “).invoke(“capitalize”).join(” “);
} else {
// 기존의 메서드 수행
return proceed();
}
}
);
“hello world”.capitalize()
// “Hello world”
“hello world”.capitalize(true)
// “Hello World”[/code]
래핑할 함수는 항상 첫번째 인자로 기존에 수행할 함수(메서드)를 받아야 합니다. 이후의 인자들은 외부에서 받는 인자입니다. 여기서도 마찬가지로 this가 보존됩니다.