'Javascript'에 해당되는 글 112건
- 2012.04.04 자바스크립트 재귀함수(호출)
- 2012.03.24 DOM Interface
- 2012.03.20 DOM 노드 및 탐색
- 2012.03.16 자바스크립트로 공백 버그 해결하기 4
- 2012.03.14 자바스크립트 압축툴 소개 및 사용법
- 2012.03.13 자바스크립트 접근자
- 2012.03.13 자바스크립트 함수 타입 검사
- 2012.03.12 자바스크립트 문자열 타입에 대해서...
- 2012.03.12 자바스크립트 배포 및 패키징
- 2012.03.09 HTML 조건부 주석
자바스크립트 재귀함수(호출)
재귀함수(호출)란?
정의:
1. 자기 자신을 재호출하는 함수를 말한다.
2. 보통 트리 구조에 대한 알고리즘을 작성할 때 유용하며, 그 외에 여러 가지로 활용할 수 있습니다.
-
function reflection(base, i)
-
{
-
var r = ''
-
-
if (i === 0){
-
r = 1;
-
}
-
else{
-
r = base * reflection(base, i - 1);
-
}
-
-
return r;
-
}
-
-
-
reflection(2, 2); // 4
아래는 작성된 재귀함수의 실행 순서를 나타냅니다.
1. reflection() 함수를 호출합니다.
2. reflection() 함수의 매개변수 base와 i의 값으로 각 각 2 가 정의 됩니다.
3. 최초 매개변수 i의 값이 0이 아니므로 조건문 내부의 else 블록({}) 9번라인의 표현식이 실행됩니다.
4. 자바스크립트의 표현식은 오른쪽에서 왼쪽으로 평가되므로 i가 2일 때 재귀호출 reflection(base, i - 1)의 반환 값은 2를 반환하며, 그에 따라 ((base(2)) * (재귀호출 반환 값(2))) 4를 최종 반환합니다.
앞에서도 말했듯이 재귀함수는 트리 구조 탐색에 유용하게 쓰일 수 있다고 하였습니다.
아래는 그에 대한 예제 소스입니다.
- // 해당 엘리먼트에 포함된 모든 textNode.nodeValue를 가져온다.
-
function text(elem, txt)
-
{
-
-
var t = [];
-
-
if (!txt)
-
{
-
elem = elem.childNodes || elem;
-
-
for (var i = 0, length = elem.length; i < length; i++){
-
-
var e = elem[i];
-
t.push(e && e.nodeType !== 1 ? e.nodeValue : text(e.childNodes));
-
}
-
}
-
else
-
{
-
t.push(createTextNode(elem, txt));
-
}
-
-
return t.join('').replace(/(^[\s]*)|([\s]*$)/g, '');
-
};
-
-
-
// text 노드 생성
-
function createTextNode(elem, txt){
-
-
elem = elem || document.documentElement;
-
-
txt = txt || '';
-
-
var $t = document.createTextNode(txt);
-
-
elem.appendChild($t);
-
-
return text(elem);
-
};
-
-
alert(text(null, 'test')); // ....test
-
alert(text(document.body, 'test')); // ....test
-
alert(text(document.getElementById('test1'), 'test')); // ....test
작성된 코드에 대해 설명하자면, 함수 매개변수로 해당 엘리먼트와 동적으로 삽입시킬 text를 정의하여 호출할수 있습니다.
또한, 매개변수가 한 개만 있는 경우는 해당 엘리먼트에 포함된 모든 text를 가져오며, 두 번째 매개변수인 text를 정의하면 해당 text가 정의된 text 노드를 반환하게 됩니다.
DOM Interface
IE를 제외한 대부분의 표준 브라우저(파폭, 사파리, 크롬, 오페라)에서는 HTMLElement 라는 인터페이스가 존재합니다.
즉, 자바스크립트를 통해 생성된 엘리먼트는 고유의 인터페이스를 제공받게 되는 것입니다."
-
if (HTMLElement)
-
{
-
// 각 엘리먼트들은 object를 취하며, 고유의 인터페이스를 제공받습니다.
-
-
alert(document.createElement('div')); // object HTMLDivElement && HTMLDivElement interface
-
alert(document.createElement('img')); // object HTMLImageElement && HTMLImageElement interface
-
alert(document.createElement('input')); // object HTMLInputElement && HTMLInputElement interface
-
}
존재합니다.
또한, 아래 그림은 DOM 탐색 시 쓰이는 함수 목록이며, 노란색 부분은 그중에서도 자주 쓰이는 함수를 나타낸 것입니다.
그럼 이제부터 HTMLElement 인터페이스를 활용하여 문서의 모든 엘리먼트에 객체 맴버를 추가 시킬 수 있는 방법에 대해
알아보도록 하겠습니다.
- HTML 소스
- 공통 함수
-
// 객체 상속 함수
-
function extend(){
-
-
var target = this
-
, opts = []
-
, src = null
-
, copy = null;
-
-
for (var i = 0, length = arguments.length; i < length; i++) {
-
-
opts = arguments[i];
-
-
for (var n in opts) {
-
src = target[n];
-
copy = opts[n];
-
-
if (src === copy) continue;
-
if (copy) target[n] = copy;
-
}
-
}
-
}
-
-
-
// 각 맴버가 추가 되어있는 객체
-
var nodes =
-
{
-
prev: function(){
-
-
elem = this;
-
-
do{
-
elem = elem.previousSibling;
-
}
-
while(elem && elem.nodeType !== 1)
-
-
return elem;
-
},
-
-
next: function(){
-
-
elem = this;
-
-
do{
-
elem = elem.nextSibling;
-
}
-
while(elem && elem.nodeType !== 1)
-
-
return elem;
-
}
-
}
아래는 HTMLElement 인터페이스를 지원하는 표준 브라우저 방식의 코드입니다.
-
// 표준 브라우저 방식
-
// HTMLElement 하위의 모든 엘리먼트에 nodes객체의 모든 맴버를 추가 시킵니다.
-
if (window.HTMLElement){
-
-
extend.call(window.HTMLElement.prototype, nodes);
-
-
alert(document.getElementById('test3').prev().prev().next().id); // test2
-
-
}
위에서 언급한 바와 같이 IE 브라우저는 HTMLElement 객체를 제공하지 않습니다.
하지만 IE 브라우저 에서도 아래와 같이 유사 기능을 구현할 수 있습니다.
-
// 비표준 브라우저 방식
-
if (!window.HTMLELement){
-
-
var fnCreate = document.createElement
-
, fnId = document.getElementById
-
, fnTags = document.getElementsByTagName;
-
-
/*
-
document.createElement = function(tag){
-
var o = fnCreate(tag);
-
o && extend.call(o, nodes);
-
return o || undefined;
-
};
-
-
document.getElementsByTagName = function(tag){
-
var o = fnTags(tag);
-
o && extend.call(o, nodes);
-
return o || undefined;
-
};
-
-
*/
-
-
-
// document 객체의 맴버함수를 오버라이하여 구현합니다.
-
document.getElementById = function(id){
-
var o = fnId(id);
-
o && extend.call(o, nodes);
-
return o || undefined;
-
};
-
-
alert(document.getElementById('test2').prev().id); // test1
-
alert(document.getElementById('test1').next().id); // test2
-
-
}
아래 코드는 이전에 구현되지 않았던 함수 체인방식이 적용된 코드입니다.
-
var nodes =
-
{
-
prev: function(){
-
-
elem = this[0] || this;
-
-
do{
-
elem = elem.previousSibling;
-
}
-
while(elem && elem.nodeType !== 1)
-
-
this[0] = elem;
-
-
// 이전 코드처럼 elem를 반환하지 않고 getElementById 함수 호출시 nodes객체의 모든 맴버가 추가 되어있는 'test2' 객체를 반환하여 체인방식을 적용시킵니다.
-
-
return this;
-
},
-
-
next: function(){
-
-
elem = this[0] || this;
-
-
do{
-
elem = elem.nextSibling;
-
}
-
while(elem && elem.nodeType !== 1)
-
-
this[0] = elem;
-
-
return this;
-
}
-
}
-
-
if (!window.HTMLELement){
-
var fnCreate = document.createElement
-
, fnId = document.getElementById
-
, fnTags = document.getElementsByTagName;
-
-
/*
-
document.createElement = function(tag){
-
var o = fnCreate(tag);
-
o && extend.call(o, nodes);
-
return o || undefined;
-
};
-
-
document.getElementsByTagName = function(tag){
-
var o = fnTags(tag);
-
o && extend.call(o, nodes);
-
return o || undefined;
-
};
-
-
*/
-
-
document.getElementById = function(id){
-
var o = fnId(id);
-
o && extend.call(o, nodes);
-
return o || undefined;
-
};
-
-
alert(document.getElementById('test2').prev().next()[0].id); // test2
-
-
}
지금까지 문서의 모든 엘리먼트에 객체 맴버를 추가 시킬 수 있는 방법에 대해 알아보았습니다. 하지만 이 방법은 실무에서 거의 사용되지 않는 것이 사실입니다.
즉, DOM 인터페이스를 학습하기 위한 코드이며, 더 많은 학습을 원하고 실무에서 사용 가능한 코드를 작성하고 싶다면 cssQuery 나 jQuery 의 내부 로직 중 DOM 탐색에 대한 부분을 분석하는 것을 추천합니다.
참고 사이트:
DOM Interface in javascript- @Rhio.Kim's blog http://rhio.tistory.com/198
DOM 노드 및 탐색
이번 포스트에서는 공백버그에 대한 내용과 유사한 DOM 노드에 대해 간단히 설명하고 노트 탐색에 필요한 속성값을 알아 보도록 하겠습니다.
먼저 DOM에 대해 간단히 정리하자면, XML 구조를 탐색 가능한 트리 형태로 표현하며, 그 최상위 객체로는 문서 엘리먼트
(document documentElement)라고 부르는 단일 노드가 있습니다.
또한, 각 노드에는 타입을 반환하는 속성인 node.nodeType이 존재하며, 대표적인 타입으로는 Element, #text, document Element 가 있습니다.
그리고 자신의 직접적인 관계를(부모, 자식, 형제) 가리키는 포인터들과 또 그에 대해 접근할 수 있는 속성들은 아래와 그림과 같이 존재 합니다.
#DOM 트리 구조 관계도
#노드들에 직접 접근할 수 있는 속성들입니다.
-
document: 문서 자체
-
-
document.documentElement: 문서의 최상위 루트 엘리먼트 (HTML)
-
-
parentNode: 부모 엘리먼트
-
-
previousSibling: 이전 형제 엘리먼트
-
-
nextSibling: 다음 형제 엘리먼트
-
-
firstChild: 첫번째 자식 엘리먼트
-
-
lastChild: 마지막 자식 엘리먼트
-
-
childNodes: 자식 엘리먼트 집합
그럼 위에 내용을 바탕으로 아래 HTML 코드에 대한 브라우져별 생성 노드에 대해 알아보도록 하겠습니다.
#IE 기준
#표준 브라우저(FF, Chrome, Safari)
#Chrome
#FF
다른점을 알아보기 위해 각 엘리먼트를 nodeType으로 분류한 표를 보시겠습니다.
위에서도 볼 수 있듯이 엘리먼트에 대한 계산 여부 또한 브라우저별로 다르다는 것을 한눈에 알 수 있습니다.
또한, 노드타입은 총 12가지로 나뉘지만 실제로 HTML 상에서 다룰 수 있는 타입은 몇 개 되지 않으며, 그중에서도
보통 1번 타입인 Element 노드와 3번 타입인 #text 노드를 노드 탐색 시 주로 다루게 됩니다.
아래 작성된 코드는 노드 탐색에 필요한 함수를 모듈화한 소스입니다.
-
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
-
<HTML>
-
<HEAD>
-
<TITLE> New Document </TITLE>
-
<META NAME="Generator" CONTENT="EditPlus">
-
<META NAME="Author" CONTENT="">
-
<META NAME="Keywords" CONTENT="">
-
<META NAME="Description" CONTENT="">
-
</HEAD>
-
<BODY>
-
<script>
-
var nodes = (function(){
-
-
return new (function()
-
{
-
function t(){
-
return this;
-
}
-
t.prototype = {
-
first: first,
-
last: last,
-
prev: prev,
-
next: next,
-
chlid: chlid,
-
parent: parent,
-
get: get
-
}
-
return t;
-
}());
-
-
-
function first(elem){
-
elem = elem && elem.firstChild || document.documentElement.firstChild;
-
this[0] = elem && elem.nodeType !== 1 ? this.next(elem).get(0) : elem;
-
return this;
-
};
-
-
function last(elem){
-
elem = elem && elem.lastChild || document.documentElement.lastChild;
-
this[0] = elem && elem.nodeType !== 1 ? this.prev(elem).get(0) : elem;
-
return this;
-
};
-
-
function prev(elem){
-
elem = elem || document.documentElement;
-
do{
-
elem = elem.previousSibling;
-
}
-
while(elem && elem.nodeType !== 1)
-
this[0] = elem;
-
return this;
-
};
-
-
function next(elem){
-
elem = elem || document.documentElement;
-
do{
-
elem = elem.nextSibling;
-
}
-
while(elem && elem.nodeType !== 1)
-
-
this[0] = elem;
-
return this;
-
};
-
-
function chlid(elem){
-
elem = elem || document.documentElement;
-
var childs = [];
-
for (var i = 0, length = elem.childNodes.length; i < length; i++){
-
var child = elem.childNodes[i];
-
if (child && child.nodeType === 1) childs.push(child);
-
}
-
this[0] = childs;
-
return this;
-
};
-
-
function parent(elem){
-
elem = elem || document.documentElement;
-
var parents = [];
-
do{
-
elem = elem.parentNode;
-
if (elem.nodeType === 1) parents.push(elem);
-
}
-
while(elem && elem.nodeType === 1)
-
this[0] = parents;
-
return this;
-
};
-
-
function get(index){
-
index = index || 0;
-
return this[0] && this[0].length > 0 ? this[0][index] : this[0];
-
};
-
}());
-
window.onload = function()
-
{
-
alert(nodes.first(document.body).get(0).nodeName); // script
-
alert(nodes.last(document.body).get(1).nodeName); // div
-
alert(nodes.prev().get()) // null
-
alert(nodes.prev(document.body).get(0).nodeName); // head
-
alert(nodes.next(document.getElementById('test1')).get(0).nodeName); // div
-
alert(nodes.last(document.body).get(2).nodeName); // div
-
alert(nodes.chlid().get(1).nodeName); // body
-
alert(nodes.parent(document.getElementById('test3')).get(1).nodeName); // html
-
};
-
</script>
-
<div id="test1">
-
<div id="test2">test2</div>
-
</div>
-
<div id="test3">test3</div>
-
</BODY>
-
</HTML>
Node 모듈의 각 함수들의 기능은 아래와 같습니다.
-
#first: 해당 엘리먼트의 첫번째 엘리먼트 반환
-
-
#last: 해당 엘리먼트의 마지막 엘리먼트 반환
-
-
#prev: 해당 엘리먼트의 이전 형제 엘리먼트 반환
-
-
#next: 해당 엘리먼트의 다음 형제 엘리먼트 반환
-
-
#chlid: 해당 엘리먼트의 자식 엘리먼트 집합 반환
-
-
#parent: 해당 엘리먼트의 부모 엘리먼트 집합 반환
자바스크립트로 공백 버그 해결하기
이 악명 높은 공백 버그 때문에 노드 탐색 시 많은 어려움이 따를수 있으며, 이를 위한 해결하기 위한 방법으로 해당 엘리먼트의 노드 타입을 구분하여 공백 엘리먼트를 제거하는 코드를 작성할 수 있습니다.
-
function clearWhiteElement(elem){
-
-
elem = elem || document;
-
-
var childs = elem.childNodes
-
, length = childs.length;
-
-
for (var i = 0; i < length; i++){
-
-
var child = childs[i];
-
-
if (child)
-
{
-
if (child.nodeType === 3 && !/\S/.test(child.nodeValue)){
-
elem.removeChild(child);
-
}
-
else if(child.nodeType === 1)
-
{
-
clearWhiteElement(child);
-
}
-
}
-
else{
-
var n = next(elem);
-
if (n) clearWhiteElement(n);
-
}
-
}
-
}
-
-
function next(elem){
-
-
do{
-
elem = elem.nextSibling;
-
}
-
while(elem && elem.nodeType !== 1)
-
-
return elem;
-
-
}
-
-
window.onload = function(){
-
-
alert(document.body.firstChild.nodeName); // #text
-
clearWhiteElement();
-
alert(document.body.firstChild.nodeName); // DIV
-
}
위 코드를 이용하여 공백 엘리먼트를 제거한 후의 모습입니다.
되었습니다.
엘리먼트를 탐색해야 하는 경우는 되도록 쓰지 않는 것이 좋을 것입니다.
자바스크립트 압축툴 소개 및 사용법
자바스크립트 압축툴 소개 및 사용법
1. JSMin
불필요한 문자(탭, 줄 끝 문자, 모든 주석)는 모두 제거합니다.
-
var Priviledge = (function(){
-
-
// private
-
var p1 = 'p1';
-
-
function t(){
-
return this;
-
};
-
-
t.prototype.get = function(){
-
return p1;
-
};
-
-
return t;
-
-
})();
-
-
-
alert(new Priviledge().get());
압축후: (jsmin_test.min.js(151 bytes))
-
var Priviledge=(function(){var p1='p1';function t(){return this;};t.prototype.get=function(){return p1;};return t;})();alert(new Priviledge().get());
- 사용법:
사이트 URL: http://www.crockford.com/javascript/jsmin.html
툴 URL:http://www.crockford.com/javascript/jsmin.zip
2. Packer
다르게 base64를 통한 압축 및 지역변수 길이를 짧게 유지시켜 용량을 줄일 수 있는 옵션도 제공합니다.
아래는 압축 전/후 코드 상태입니다.
-
var Priviledge = (function(){
-
-
// private
-
var p1 = 'p1';
-
-
function t(){
-
return this;
-
};
-
-
t.prototype.get = function(){
-
return p1;
-
};
-
-
return t;
-
-
})();
-
-
-
alert(new Priviledge().get());
압축후: (144 bytes)
-
var Priviledge=(function(){var a='p1';function t(){return this};t.prototype.get=function(){return a};return t})();alert(new Priviledge().get());
사이트 URL: http://dean.edwards.name/packer/
자바스크립트 접근자
-
function Private(){
-
-
// private 변수
-
var p1 = 'p1';
-
-
this.p2 = 'p2';
-
-
return this;
-
}
-
-
// public 메서드
-
Private.prototype.getP2 = function(){
-
return this.p2;
-
}
-
-
alert(new Private().p1); // undefined(정의되지 않았음)
-
alert(new Private().getP2()); // p2
2. Public
-
function Public(){
-
-
// public 변수
-
this.p2 = 'p2';
-
-
return this;
-
}
-
-
// public 메서드
-
Public.prototype.getP2 = function(){
-
return this.p2;
-
}
-
-
alert(new Public().getP2()); // p2
-
var Priviledge = (function(){
-
-
// private
-
var p1 = 'p1';
-
-
function t(){
-
return this;
-
};
-
-
t.prototype.get = function(){
-
return p1;
-
};
-
-
return t;
-
-
})();
-
-
-
alert(new Priviledge().get()); // p1
자바스크립트 함수 타입 검사
자바스크립트는 언어 특성상 데이터 타입에 대해 까다롭지 않은 언어입니다.
조차 없다는 말입니다.
-
function strict(types){
-
-
var args = strict.caller.arguments;
-
-
if (!types || types.constructor !== Array) return false;
-
if (typeof args !== 'object') return false;
-
-
var tlen = types.length
-
, alen = args.length
-
, k = 1;
-
-
-
if (tlen !== alen){
-
throw new Error('전달인수 갯수가 일치하지 않습니다.');
-
}
-
-
for (var i = 0; i < tlen; i++){
-
-
if (!isDataType(types[i], args[i])){
-
throw new Error('호출 함수에 가장 일치하는 오버로드된 메서드의 ' + k + '(' + types[i] + ')' + ' 번째 인수에 잘못된 인수가 있습니다.');
-
}
-
-
k++;
-
}
-
-
return this;
-
}
-
-
function isDataType(type, arg){
-
-
type = type.toLowerCase();
-
-
var types = {
-
'object': Object,
-
'array': Array,
-
'function': Function,
-
'string': String,
-
'number': Number,
-
'boolean': Boolean
-
};
-
-
if (type !== 'newobject')
-
{
-
-
if (types[type]){
-
if (arg.constructor === types[type]) return true;
-
else return false;
-
}
-
}
-
else
-
{
-
// typeof 연산자만 가지고는 new Function() 타입을 가려낼 수 없습니다.
-
if (arg.constructor !== Object && arg.constructor !== Array && typeof arg === 'object') return true;
-
else return false;
-
}
-
};
-
-
function fn(obj, obj, arr, fn, str, num, bls){
-
strict(['object', 'newobject', 'array', 'function', 'string', 'number', 'boolean']);
-
};
-
-
-
// 아래는 각각 다른 타입의 전달인자를 넣어 호출 하는 예입니다.
-
-
var a = function() { ; };
-
fn({}, new a(), [], function(){ ; }, 'str', 1, true);
-
fn({}, {}, [], function(){ ; }, 'str', 1, true);
-
fn({}, [], [], function(){ ; }, 'str', 1, true);
자바스크립트 문자열 타입에 대해서...
이전 포스트 내용 중 문자열 타입에 관해 얘기 했던 적이 있습니다. 포스트 내용에 핵심은 문자열 타입이 "값"과 "참조" 중
어디에 속하는지에 대한 얘기였으며, 이번 포스트에서 그에 관한 얘기를 더 나눠보도록 하겠습니다.
메모리를 가진 변수에는 직접 저장할 수 없다는 말이며(값 타입처럼), 대신, 변수에 저장되는 것은 이런 값에 대한 "참조 값"입니다.
그렇다면, 문자열은 "참조타입"이라는 걸까요?
아래 예제를 통해 더 자세히 알아보도록 하겠습니다.
-
// 참조타입의 기본 예
-
-
// r 배열객체는 참조타입이므로 r1 배열객체와 같은 값을 참조하며, r 배열객체와 r1 배열객체는 같습니다.
-
var r = [];
-
r[0] = 'hello';
-
-
var r1 = r;
-
r1[1] = 'world';
-
-
alert(r === r1); // true
-
-
// 문자열이 값타입이라는 예
-
-
// v 문자열 객체는 값타입이므로 새로 생성된 문자열 객체 v1과 다릅니다.
-
var v = 'hello';
-
var v1 = v + 'world';
-
-
alert(v === v1); // false
위 결과에서 말하는 바와같이 자바스크립트 문자열은 내부적으로 참조타입의 효율성을 갖도록 구현된 기본타입입니다.
자바스크립트 배포 및 패키징
-
var NAVER = window.NAVER || {};
-
window.NAVER.ajax = window.NAVER.ajax || {};
-
-
-
window.NAVER.ajax = (function(doc, win){
-
-
var xhr = getXhr()
-
, version = '1.0.x'
-
, agent = window.navigator.userAgent.toLowerCase();
-
-
return new (function(){
-
-
var t = function(){
-
-
this.version = version;
-
-
return this;
-
};
-
-
t.prototype = {
-
get: get,
-
post: post
-
};
-
-
return t;
-
-
}())();
-
-
function getXhr(){
-
-
if (!window.XMLHttpRequest) return new ActiveXObject(agent.indexOf('msie 5') > -1 ? 'Microsoft.XMLHTTP' : 'Msxml2.XMLHTTP');
-
else if (window.XMLHttpRequest) return new window.XMLHttpRequest;
-
else return null;
-
}
-
-
-
function get(){
-
return this;
-
}
-
-
-
function post(){
-
return this;
-
}
-
-
-
})(document, window);
-
-
-
alert(window.NAVER.ajax.get().version); // 1.0.x
위와 같은 방법으로 패키징하며, 사용자들에 배포하게 됩니다.
HTML 조건부 주석
위와 같은 방법을 통해 정리 되지 않는 CSS 및 자바스크립트 코드를 버전별로 구분하여 해당 구성요소들을 로드시킬수
있습니다.