복잡한 JavaScript는 이제 가라!!!

Prototype LOGO
Prototype 프레임웍은 Samstephenson이 제작한 Javascript 프레임웍이다. 그 목적은 Web 2.0으로 가면서 복잡하게 엉킨 Javascript를 간소화시키기 위함이다. Prototype은 스크립트 기반의 언어의 제한을 뛰여넘어 일반 컴파일 언어 (예하면, C++, Java와 같은) 수준에서 지원하고 있는 상속 및 추상클래스까지도 구현가능하게끔 하였다.
prototype.js는 현재 웹어플리케이션 프로토타입 개발용으로 많은 관심을 받고있는 프레임워크인 RubyOnRails에 통합되어 있으며, script.aculo.us, Rico 등과 같은 다양한 자바스크립트 프레임워크에서도 라이브러리 형태로 사용되어지고 있다.
예제와 참조문서를 읽었을때, Ruby프로그래밍 언어에 친숙한 개발자는 Ruby의 내장 클래스와 이 라이브러리에 의해 구현된 많은 확장 사이의 의도적인 유사성을 알아차리게 될것이다.
그럼 아래에 prototype 을 좀 더 쉽게 사용할 수 있게 끔 가이드(?) 하기 위하여 그에 대한 간단한 설명을 첨부할가 한다.
1. 기초 유틸리티1.1. Prototype 객체속성 및 방법
Version : 문자열은 prototype 프레임웍의 버전을 명시해준다.
ScriptFragment : 스크립트의 정규모드 유효성 체크를 해준다.
emptyFunction(): 빈 함수를 나타낸다. Function() {}
K(x): K메소드, 파라미터 자신을 리턴한다.
1.5부터는 JSON을 지원하면서 기능이 막강해졌다.
소스 열기
var Prototype = {
Version: '1.5.1.1',
Browser: {
IE: !!(window.attachEvent && !window.opera),
Opera: !!window.opera,
WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
Gecko: navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1
},
BrowserFeatures: {
XPath: !!document.evaluate,
ElementExtensions: !!window.HTMLElement,
SpecificElementExtensions:
(document.createElement('div').__proto__ !==
document.createElement('form').__proto__)
},
ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
emptyFunction: function() { },
K: function(x) { return x }
}
1.2. Class 객체
Class객체는 오로지 하나의 작용밖에 없다. 그건 바로 클래스정의에 대한 지원이다.
소스 열기
var Class = {
create: function() {
return function() {
this.initialize.apply(this, arguments);
}
}
}
var Abstract = new Object();
Object.extend = function(destination, source) {
for (var property in source) {
destination[property] = source[property];
}
return destination;
}
Object.extend(Object, {
inspect: function(object) {
try {
if (object === undefined) return 'undefined';
if (object === null) return 'null';
return object.inspect ? object.inspect() : object.toString();
} catch (e) {
if (e instanceof RangeError) return '...';
throw e;
}
},
toJSON: function(object) {
var type = typeof object;
switch(type) {
case 'undefined':
case 'function':
case 'unknown': return;
case 'boolean': return object.toString();
}
if (object === null) return 'null';
if (object.toJSON) return object.toJSON();
if (object.ownerDocument === document) return;
var results = [];
for (var property in object) {
var value = Object.toJSON(object[property]);
if (value !== undefined)
results.push(property.toJSON() + ': ' + value);
}
return '{' + results.join(', ') + '}';
},
keys: function(object) {
var keys = [];
for (var property in object)
keys.push(property);
return keys;
},
values: function(object) {
var values = [];
for (var property in object)
values.push(object[property]);
return values;
},
clone: function(object) {
return Object.extend({}, object);
}
});
가장 보편적으로 사용하는 방식을 예로 들면 다음과 같다.
예제 열기
var Animal = Class.create();
Animal.prototype = {initialize:function (name, sound) {
this.name = name;
this.sound = sound;
}, speak:function () {
alert(name + " says: " + sound + "!");
}};
var snake = new Animal("Ringneck", "hissssssssss");
snake.speak();
// -> alerts "Ringneck says: hissssssssss!"
var Dog = Class.create();
Dog.prototype = Object.extend(new Animal(), {initialize:function (name) {
this.name = name;
this.sound = "woof";
}});
var fido = new Dog("Fido");
fido.speak();
// -> alerts "Fido says: woof!"
윗 예제를 보면 Animal이라는 클래스를 Class.create라는 메소드를 통하여 생성한다.
다음 initialize 생성자를 만들고 name, sound를 인자로 입력를 받는다.
밑에 보면 Dog라는 클래스도 생성해 주고 있는데 실제상 이 Dog클래스는 Animal 클래스를 상속받고 있는다.
Dog.prototype = Object.extend(new Animal(), 단순히 extend 메소드를 통하여 상위클래스를 쉽게 상속받을수가 있다.
1.3. Object 클래스에 대한 확장
Object는 모든 객체의 기초로 되는 클래스이다. 즉 모든 객체는 Object클래스를 상속받아서 구현이 되여진다. 1.5버젼으로 업그레드 되면서 Prototype은 Object클래스에 4개의 static 메소드를 추가했다.
static 메소드 그 첫번째:
syntax => Object.extend(destination, source)
설명 => source 파라미터의 모든 속성을 destination에 넘겨준다. 이렇게 함으로써 destination이 source상속 받은것과 같은 효과를 나타낸다.
static 메소드 그 두번째:
syntax => Object.inspect(object)
설명 => 파라미터로 지정된 오브젝트로 자신의 inspect메소드를 구현해야 한다.
static 메소드 그 세번째:
syntax => Object.clone(object)
설명=> 원 오브젝트의 모든 속성을 그대로 복사하여 새 오브젝트에 넣어둔다. 하지만 이런 복사는 deep복사인거 아니라 shallow복사이다.
Syntax => Object.keys(object)
설명=> 모든 오브젝트를 hash로 간주하여 처리하며 결과적으로 이런 속성들의 이름을 리턴한다. 하지만 추가된 요소들의 정열순서는 보장할수 없다. 사용자가 만약 각 요소가 정열이 되여질것을 원하면 자기 스스로 이부분의 코딩을 해야 한다.
Syntax => Object.value(object)
설명=> 모든 오브젝트를 hash로 간주하여 처리하며 결과적으로 이런 속성들의 값을 리턴한다.
Syntax => toJSON()
설명 => JSON 문자열을 리턴한다.
소스 열기
Object.extend = function(destination, source) {
for (var property in source) {
destination[property] = source[property];
}
return destination;
}
Object.extend(Object, {
inspect: function(object) {
try {
if (object === undefined) return 'undefined';
if (object === null) return 'null';
return object.inspect ? object.inspect() : object.toString();
} catch (e) {
if (e instanceof RangeError) return '...';
throw e;
}
},
toJSON: function(object) {
var type = typeof object;
switch(type) {
case 'undefined':
case 'function':
case 'unknown': return;
case 'boolean': return object.toString();
}
if (object === null) return 'null';
if (object.toJSON) return object.toJSON();
if (object.ownerDocument === document) return;
var results = [];
for (var property in object) {
var value = Object.toJSON(object[property]);
if (value !== undefined)
results.push(property.toJSON() + ': ' + value);
}
return '{' + results.join(', ') + '}';
},
keys: function(object) {
var keys = [];
for (var property in object)
keys.push(property);
return keys;
},
values: function(object) {
var values = [];
for (var property in object)
values.push(object[property]);
return values;
},
clone: function(object) {
return Object.extend({}, object);
}
});
Clone예제 열기
var o = {name:"Prototype", version:1.5, authors:["sam", "contributors"]};
var o2 = Object.clone(o);
o2.version = "1.5 weird";
o2.authors.pop();
o.version;
// -> 1.5
o2.version;
// -> '1.5 weird'
o.authors;
// -> ['sam'] // Ouch! Shallow copy!
Key예제 열기
Object.keys()
// -> []
Object.keys({ name: 'Prototype', version: 1.5 }).sort()
// -> ['name', 'version']
Value예제 열기
Object.values()
// -> []
Object.values({ name: 'Prototype', version: 1.5 }).sort()
// -> [1.5, 'Prototype']
1.4 Number 클래스 확장
Number은 모든 숫치유형의 기본 클래스이며 prototype에서는 그 인스턴스에 대하여 확장을 진행하였다.
syntax => toColorPart()
설명 => RGB색상을 16진법 색상 표시법으로 변환한다.
예 => var cRed=255, cGreen=100, cBlue=22;
var hexColor="#"+cRed.toColorPart() + cGreen.toColorPart() + cBlue.toColorPart();
syntax => succ()
설명 => 숫치에 1을 합하여 리턴한다.
syntax => times(iterator)
설명=> 여기서 iterator은 함수이며 iterator라는 함수를 여러번 수행하고 싶으로 이 메소드를 사용한다.
소스 열기
Object.extend(Number.prototype, {
toColorPart: function() {
return this.toPaddedString(2, 16);
},
succ: function() {
return this + 1;
},
times: function(iterator) {
$R(0, this, true).each(iterator);
return this;
},
toPaddedString: function(length, radix) {
var string = this.toString(radix || 10);
return '0'.times(length - string.length) + string;
},
toJSON: function() {
return isFinite(this) ? this.toString() : 'null';
}
});
1.5 Try 객체
이 객체는 오로지 하나의 메소드만 제공해준다. these메소드 사용시 넘겨받은 인수는 메소드 핸들러 여야만 한다. 그 작용은 맨 처음 성공적으로 실행된 메소드의 반환치를 리턴하는것이다.
소스 열기
var Try = {
these: function() {
var returnValue;
for (var i = 0, length = arguments.length; i < length; i++) {
var lambda = arguments[i];
try {
returnValue = lambda();
break;
} catch (e) {}
}
return returnValue;
}
}
2 기초 도구류
이부분에서는 prototype 1.4.0 프레임웍의 일부 핵심 툴들을 정의하고 있으며 전체 프레임웍의 중요한 구성 부분이다.
2.1 $() 함수
$() 함수는 매우 빈번하게 사용되는 DOM의 document.getElementById() 함수에 대한 편리한 단축함수이다. DOM 함수처럼, 이것은 인자로 던져진 id를 가진 해당 엘리먼트를 반환한다. 하지만 DOM함수와는 달리, 이것은 여러개의 id를 사용할수 있는데, 이 경우에 $()는 요청된 엘리먼트들을 가진 Array 객체를 반환하게 된다. 이 함수의 다른 장점으로 id 문자열이나 엘리먼트객체 자체를 인자로 가질 수 있는데, 이것은 인자 형태를 가질수 있는 다른 함수를 생성할 때 매우 유용하게 사용되어 질 수 있다.
소스 열기
function $() {
var elements = new Array();
for (var i = 0; i < arguments.length; i++) {
var element = arguments[i];
if (typeof element == 'string')
element = document.getElementById(element);
if (arguments.length == 1)
return element;
elements.push(element);
}
return elements;
}
예제 열기
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Sample</title>
<script src="prototype.js" type="text/javascript"></script>
</head>
<body>
<script>
//<![CDATA[
function test1() {
var d = $('myDiv1');
alert(d.innerHTML);
}
function test2() {
var divs = $('myDiv1','myDiv2');
for(i = 0; i < divs.length; i++) {
alert(divs[i].innerHTML);
}
}
//]]>
</script>
<div id="myDiv1">
<p>This is a paragraph</p>
</div>
<div id="myDiv2">
<p>This is another paragraph</p>
</div>
<input type="button" value="Test1" onclick="test1();" />
<input type="button" value="Test2" onclick="test2();" />
</body>
</html>
2.2 $F() 함수
$F() 함수는 또다른 유용한 단축함수로, 텍스트박스나 드랍다운 리스트와 같은 입력 컨트롤의 값을 반환한다. 이 함수는 엘리먼트 id나 엘리먼트객체 자체를 인자로 가질수 있다.
소스 열기
var $F = Form.Element.getValue;
예제 열기
<script>
function test3() {
alert($F('userName'));
}
</script>
<input type="text" id="userName" value="Joe Doe" />
<input type="button" value="Test3" onclick="test3();" />
2.3 $A() 함수
$A() 함수는 전달된 한 개의 인자를 배열로 변환한다. Array 클래스에 대한 확장으로 이 함수는 열거가능한 목록이라면 그것을 쉽게 배열로 변환할 수 있더. 예를 들면, DOM 객체의 NodeLists를 효과적으로 배열로 바꾸는데 유용하게 쓰일 수 있다.
소스 열기
var $A = Array.from = function(iterable) {
if (!iterable) return [];
if (iterable.toArray) {
return iterable.toArray();
} else {
var results = [];
for (var i = 0; i < iterable.length; i++)
results.push(iterable[i]);
return results;
}
}
예제 열기
<script>
function showOptions() {
var someNodeList = $('lstEmployees').getElementsByTagName('option');
var nodes = $A(someNodeList);
nodes.each(function(node) {
alert(node.nodeName + ': ' + node.innerHTML);
});
}
</script>
<select id="lstEmployees" size="5">
<option value="5">Buchanan, Steven</option>
<option value="8">Callahan, Laura</option>
<option value="1">Davolio, Nancy</option>
</select>
<input type="button" value="Show the options" onclick="showOptions();" />
2.4 $R() 함수
$R() 함수는 new ObjectRange(lowerBound, upperBound, excludeBounds)과 동일한 단축함수이다. 아래는 each 메소드를 통해 반복(iterators)의 사용법을 보여주는 간단한 예제이다.
소스 열기
var $R = function(start, end, exclusive) {
return new ObjectRange(start, end, exclusive);
}
예제 열기
<script>
function demoDollar_R(){
var range = $R(10, 20, false);
range.each(function(value, index){
alert(value);
});
}
</script>
<input type="button" value="Sample Count" onclick="demoDollar_R();" />
2.5 String 클래스에 대한 확장
Javascript에서 모든 문자열은 모두 String 클래스의 인스턴스들인바 이런 인스턴스들에 대한 오버레이션을 간편화하기 위하여 몇가지 메소드들을 제공하였다.
2.5.1 stripTags 메소드
태그를 없애는메소드로서 이정규 표현이라면 올바르게 태그를 없앨 수 없는 경우가 있다.
소스 열기
stripTags: function() {
return this.replace(/<\/?[^>]+>/gi, '');
}
2.5.2 stripScripts 메소드
캐릭터 라인(this)으로부터,script블록을 삭제하는 메소드이다. 전술 Prototype오브젝트의ScriptFragment 프롭퍼티로부터 script 블록의 정규 표현 캐릭터 라인을 취득하고 있다.
소스 열기
stripScripts: function() {
return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
}
2.5.3 extractScripts 메소드
캐릭터 라인(this)의 모든 script 블록의 내용을, 배열에 격납해 반환하는 메소드이다.
Prototype.ScriptFragment를 사용해, 글로벌 매치하는 정규 표현 오브젝트와 싱글 매치하는 정규 표현 오브젝트를 작성한다. m수식자는 무의미한 생각이 든다.
캐릭터 라인(this)으로부터 모든 script 블록을 추출해, 더욱 각각으로부터 script 블록의 내용(코드 부분)을 추출해 배열에 격납해, 반환하고 있다.
인수로 지정된 함수를, 배열 요소 모든 것을 차례로 인수로 지정해 실행해, 결과를 배열에 격납해 반환한다.
소스 열기
extractScripts: function() {
var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
return (this.match(matchAll) || []).map(function(scriptTag) {
return (scriptTag.match(matchOne) || ['', ''])[1];
});
}
2.5.4 evalScripts 메소드
캐릭터 라인(this)으로부터, extractScripts 메서드로 취득한 script코드 모든 것을, eval함수로 실행하고 있다.
요소의 innerHTML 프롭퍼티에 소스를 설정하면(자), 태그등이 eval되여 실행이 되여지겠지만 script 블록은 실행되지 않는다. 이때 이런 소스를 실행시키는데 evalScripts 메서드가 도움이 된다.
소스 열기
evalScripts: function() {
return this.extractScripts().map(eval);
}
2.5.5 escapeHTML 메소드
캐릭터 라인(this)의 HTML문 자를 이스케이프 해 반환한다. 즉 「text」(이)라고 하는 캐릭터 라인을 「<p>text</p>」로 변환한다.
예하면 "<"를 "<"로 변환한다.
소스 열기
escapeHTML: function() {
var self = arguments.callee;
self.text.data = this;
return self.div.innerHTML;
}
2.5.6 unescapeHTML 메소드
escapeHTML 메서드의 역.캐릭터 라인(this)의 태그를 없앤 다음, 안 이스케이프 해 반환한다.
예하면 "<"를 "<"로 변환해준다.
소스 열기
unescapeHTML: function() {
var div = document.createElement('div');
div.innerHTML = this.stripTags();
return div.childNodes[0] ? (div.childNodes.length > 1 ?
$A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
div.childNodes[0].nodeValue) : '';
}
2.5.7 toQueryParams 메소드
우선, 캐릭터 라인(this)을 쿠에리 캐릭터 라인으로 간주해, 선두 「?」보다 이후의 캐릭터 라인을 「&」으로 분할해 배열에 격납한다. inject 메서드는 Array 클래스의 추가 메서드이다. 제2인수의함수의 제2 인수(pairString)에 배열의 각 요소의 값이 들어온다.처리의 결과를 제일 인수(params)에 격납해 반환한다.이것을 반복해, 최종적인 결과를 격납한 것(params)를 반환합니다.inject의 제일 인수(은)는 반환치의 초기치이다.
즉 여기에서는, 배열의 각 요소의 「name=value」의name를 인덱스,value를 값으로 한 커스텀오브젝트가 반환되게 된다.
예를 들자면
var s = "sid=10&page=2&author=nate";
var qp = s.toQueryParams();
qp의 구조는
{sid:10,page:2,author:'nate'}
가 되여지겠다.
소스 열기
toQueryParams: function(separator) {
var match = this.strip().match(/([^?#]*)(#.*)?$/);
if (!match) return {};
return match[1].split(separator || '&').inject({}, function(hash, pair) {
if ((pair = pair.split('='))[0]) {
var key = decodeURIComponent(pair.shift());
var value = pair.length > 1 ? pair.join('=') : pair[0];
if (value != undefined) value = decodeURIComponent(value);
if (key in hash) {
if (hash[key].constructor != Array) hash[key] = [hash[key]];
hash[key].push(value);
}
else hash[key] = value;
}
return hash;
});
},
2.5.8 toArray 메소드
캐릭터 라인(this)을, split로 1 문자씩 배열에 격납해 반환한다. 단락 캐릭터 라인에 공문자열을 지정하는 것으로 1 문자씩에 분할한다.
예를 들면
var t = "abc";
var s = t.toArray();
결론적으로 s에 ['a','b','c']이라는 배열로 만들어 진다.
소스 열기
toArray: function() {
return this.split('');
}
2.5.9 camelize 메소드
캐릭터 라인(this)이 「aaa-bbb」형식일 때, 「aaaBbb」형식으로 변환해 반환한다.CSS프롭퍼티를 오브젝트 모델 구문으로 변환하는 경우에 편리하다. 실제, 후술 하는 Element 오브젝트의 getStyle 메서드로 사용되고 있다.
소스 열기
camelize: function() {
var parts = this.split('-'), len = parts.length;
if (len == 1) return parts[0];
var camelized = this.charAt(0) == '-'
? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
: parts[0];
for (var i = 1; i < len; i++)
camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
return camelized;
},
2.5.10 capitalize 메소드
문자열중 맨 처음 캐릭터만 대문자로 표시하고 나머지는 모두 소문자로 변환시켜준다.
예하면 기입된 문자열이 "yust"라면 capitalize를 거치면 "Yust"로 된다.
소스 열기
capitalize: function() {
return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
}
2.5.11 startsWith 메소드문자열 첫 시작부분에 특정된 문자패턴이 있는지를 체크하여 있으면 true을 반환하고 아니면 false를 반환한다.
var str = "CIM campus integrity movement";
str.startsWith('CIM');
이럴경우 true를 반환하게 된다.
소스 열기
startsWith: function(pattern) {
return this.indexOf(pattern) === 0;
}
2.5.12 endsWith 메소드문자열 마지막부분에 특정된 문자패턴이 있는지를 체크하여 있으면 true을 반환하고 아니면 false를 반환하다.
var str = "CIM campus integrity movement";
str.endsWith('ment');
이럴경우 true를 반환하게 된다.
소스 열기
endsWith: function(pattern) {
var d = this.length - pattern.length;
return d >= 0 && this.lastIndexOf(pattern) === d;
}
2.5.13 dasherize 메소드문자열중 "_"를 일괄적으로 "-"로 바꿔준다.
예하면
'border_bottom_width'.dasherize();
// -> 'border-bottom-width'
소스 열기
dasherize: function() {
return this.gsub(/_/,'-');
}
2.5.14 blank 메소드
문자열이 공백이거나 공백만 있을경우 true를 반환한다.
''.blank();
//-> true
' '.blank();
//-> true
' a '.blank();
//-> false
소스 열기
blank: function() {
return /^\s*$/.test(this);
}
2.5.15 empty 메소드문자열이 공백이면 true를 반환한다.
''.empty();
//-> true
' '.empty();
//-> false
소스 열기
empty: function() {
return this == '';
}
본 포스트는 계속 업뎃이 되여집니다...