eval vs. Function

프로그래밍 2008. 1. 24. 00:06
구글그룹스의 jquery 포럼을 메일로 받아보는데  재미있는 질문이 올라왔다.

어떤 친구의 질문은  jquery을 보면서 공부하고 있는데  fn = eval("false||function(elem){return " + fn + "}");
이런 문법이 있다. 왜 n = Function('elem', 'return '+fn)  이렇게 하지 않고 위와 같이 했냐라는 질문이였다.

처음에 이 질문을 보고 갑자기 나도 왜 위와 같이 했을까 라는 고민을 했다.

eval("false||function(elem){return " + fn + "}"); 이 문장을 보며 의아해 했던 것은 두가지 인데
첫번째왜 Function을 사용하지 않았나?
두번째앞부분에 false||을 사용했는가? 이다.

그래서 jquery를 받아서 eval("false||function(elem){return " + fn + "}"); 이 부분을 찾아봤다.
솔직히 이해를 잘 못했다.(내공 부족)-_-;;
그러다 포럼의 John Resig을 비롯하여 다른 사람들이 올린 글을 보니  이해하기 쉬웠다.

결론은
첫번째Function으로 만들면 scope가 local scope가 아니라 global scope가 되기 때문이라고 한다.
즉 eval("false||function(elem){return " + fn + "}");는 함수 scope안에서 있는것들을 사용할수 있는데 Function은 global 변수를 사용해야 하기 때문이다.

두번째는 약간의 브라우져의 특성때문이다.
우리가 json을 string으로 받아서 보통 eval("("+json+")") 이렇게 처리하는 이유와 비슷하다. 그래서 처음에는 John Resig 위에 방법으로 하다가 ie,opera에서 안되서  어쩔수 없이 eval("false||function("+....+"}") 이와 같은 방법을 했다고한다.

하여간 오랫만에 건진 팁!





Posted by 전용우
,
난 dojo에 관심이 없어. 그냥 있었는데 오늘 보니 dojo에서 새로운 자바스크립트 압축기를 내놨다는 글을 봤다.
이건 일반적인 압축방법과 달라서 더 흥미롭게 봤다.

기존의 압축방식과 틀린 이유는 정규표현식으로 압축하지 않고 Rhino의 실제 파서를 이용하여 압축을 한다고 한다.

해당 페이지를 보면 압출률 또한 좋고 더욱 좋은것은 변수명을 변경해주므로서 보안상의 문제를 어느정도 해결한듯. 구글의 js를 보면 변수명이 변경되있는데 ShrinkSafe도 비슷한 효과를 내는것 같다.

음... 단점이자 장점일수 있는데 다른 압출툴과 달리 되돌릴 방법이 없다.

그리고 추후에는 Gzipping도 지원할것 같다.

그럼 배포의 최적화 조건인 Minifying, Obfuscating, Gzipping 이 3가지 조건이 다 만족하네.

다음은 압축된 모습 짤방.

사용자 삽입 이미지

짤방



Posted by 전용우
,
John Resig씨가 ecmascript4를 사용할수 있는 환경을 셋팅하고 간단한 사용법을 동영상으로 촬영을 했습니다.

아직까진 브라우져와 연결하여 사용할순 없고 (구현중 이랍니다.그래서 브라우져에서 지원하는 함수들은 사용할수 없습니다.)
일부 기능들 또한 사용할순 없지만 그래도 es4를 느끼기엔 충분한것 같습니다.

ecmascript4의 기능인 class, getter,setter, inheritance, interface, type annotation...등 을 직접 사용할수 있습니다.
그리고 아래 ppt을 보시면 좀더 자세한 es4의 내용들을 볼수 있습니다.
아래 ppt는 얼마전에 John Resig씨가 포스팅한 ECMAScript 4 Speaking Tour 중 하나입니다.아니면 이곳에 가셔서 es4의 스펙을 확인하고 연습하시면 재미있을 것 같습니다.^^;;


Posted by 전용우
,
오늘 Lazy Function Definition Pattern 이란 글을 읽었는데 도움이 될것 같아 정리해둔다.

Lazy Function Definition Pattern은 많은 언어도 있지만 늦은 초기화랑 비슷하다.
필요할때 생성하고 그 이후에는 재활용하는것을 말한다.

피터씨는  이와 같은 패턴을 Lazy Function Definition Pattern이라고 한다.
자바스크립트에서 이 기법을 구현하는데는 많은 방법이 있는데 여기에서는 4가지 방법을 소개하고 최적의 방법을 알려 준다.

문제는, foo라는 함수는 호출을 하면 data객체를 반납하는데 처음에 호출시에만 date를 생성하고 다음 호출에는 재활용해야한다. 이러한 foo함수를 만드는것 문제이다.

1.고전 적인 방법.


var t;
function foo(){
    if(t){
       return t;
    }
    t = new Date();
    return t;
}

이 방법에는 두가지 문제가 있다.
첫번째는 여분의 t라는 글로벌 변수를 사용해야 한다.
두번째는 실행시 코드가 최적화 되어있지 않다. 즉 매번 if문을 실행 해야한다.

2.묘듈 패턴.(The Module Pattern)

var foo = (function() {
    var t;
    return function() {
        if (t) {
            return t;
        }
        t = new Date();
        return t;
    }
})();

묘듈 패턴으로 첫번째 문제는 해결할수 있다.클로우져(closure) 이용하여 글로벌 변수 t를 숨기고  오직 foo에서만 사용할수 있게 했다.
하지만 이 패턴으로는 두번째 문제를 해결할수 없다. 좋은 패턴이긴 하지만 여기에서는 유용하지 않다.

3.함수는 오브젝트(Functions are Objects)
function foo() {
if (foo.t) {
return foo.t;
}
foo.t = new Date();
return foo.t;
}
자바스크립트에서는 모든 함수는 오브젝트이다. 그렇기 때문에 프로퍼트를 가질수 있고 위와 같은 방법을 사용할수 있다. 이 방법은 매우 깔끔하고 보기는 좋은나 여전히 두번째 문제는 조건절을 해결하지 못했다.
(이 방법은 원래 있던 프로퍼티를 있는 듯 사용하고 있어서 나중에 모르는 사람이 봤을때 많이 헤갈리는것 같았다. 개인적으로 비추.)

4. Lazy Function Definition
이글의 저자인 피터씨가 추천하는 방법이다.

var foo = function() {
    var t = new Date();
    foo = function() {
        return t;
    };
    return foo();
};

이 방법의 원리는 클로우져를 이용한 방법이다.
처음 foo를 실행했을때 처음 t에 Date가 할당되고 foo함수에 클로우져가 할당되고 Date를 반환한다. 그 다음 호출할때는 foo에 할당된 클로우져를 호출하여 바로 Date를 반환한다.
이 방법이 이해가 되지 않는다면 이글을 참조하면 될것 같고 하지만 이글과 다른게 이 방법은 의도적으로 사용했다는 점이다.
Posted by 전용우
,
이미 좀 지난 이야기이긴 하다.
보기는 오래 전에 봤는데 이것에 대해 많은 사람들이 의견이 분분해서 그냥 잊고 있었는데
오늘 YUI Theater — Nicholas Zakas: "Maintainable JavaScript"을 봤는데 중간에 이에 대해
말을 하고 있다. 다시 한번 옛기억을 더듬으며 찾아봤다.

Object.prototype에 대해 찬성파 의견
Object확장을 하므로서 좀 더 편하게 프로그램밍을 할수 있다.
예를들면 object에 each같은 메소드를 만들면 편하게 array나 기타등등에서 편히 사용할수 있다.


Object.prototype에 대해 반대파 의견
자바스크립트의 특징인 오브젝트의 hash-table형식이 잘못된다.
예를 들면 내가 Object에 each라는걸 만들어 버리면 for..in 사용시 내가 생각했던것과 다르게 나타난다.그럼 따로 예외처리를 해야하는데 쉽지않다.

ps.하여간 Maintainable JavaScript을 보니깐 자바스크립트가 점차 대우받는 언어가 되는것 같아 좋네.(영어라 거의 못알아 들었습니다.ㅎㅎ;)

참고글:
Object.prototype is verboten
Object.prototype is erlaubt
Posted by 전용우
,

closures

프로그래밍 2007. 5. 17. 18:27
저번에 javajigi에서 발표할때가 있었다.
closure설명을 하다가 질문 중 하나가  "ajax in action에서 closure를 사용하면 메모리가 회수가 안된다.그래서 가능하면 사용하지 말라고 했다. 왜 그러냐?"는 질문이 있었는데.
나의 대답은 "순환관계일경우 회수가 안된다"라고 말했던것으로 생각한다.

그 당시 말 뜻을 잘 전달을 못한것 같아 다시 정리를 해보면 아래와 같은 경우를 말한것 같다.

function test(){
  var val = 5;
  function plusAlter(){
   alert(val);
  }
  return plusAlter;
}
var plusAlter = test();
plusAlter(); ->"5"

이와 같은경우 메모리 누수 현상이 발생된다.

이유는 함수(test)안에서 함수(plusAlter)를 선언하고 자신의 함수(plusAlter)의 context말고 상위(text)의 프로퍼티를 사용할수 있다.하지만 deep copy가 아니고 pointer를 가지고 있다.
그래서 plusAlter에서 상위(test)를 프로퍼트(val)을 사용(참조)하기 때문에 test()를 사용한 후에 GC에서 메모리를 회수를 안한다.그렇기 때문에 이런경우 매모리 누수가 일어난다.
(GC는 누군가 참조하고 있으면 회수안함.)

그래서 아래와 같이 prototype을 이용하여 수정해서 쓰기를 ajax in action에서 권장한다.

function test(){
  this.val = 5;
}
test.prototype.plusAlter = function(){
  alert(this.val);
}

var freeleak = new test();
freeleak.plusAlter(); ->"5"

Posted by 전용우
,

reverse do while

프로그래밍 2007. 5. 17. 02:01
전에 포스팅한 내용을 바탕으로 each문을 for문이 아닌 reverse do while으로 만들어봤습니다.
근데 생각보다 그렇게 많은 효과가 없습니다.
일반 for문과 비교하여 아주 약간 빠릅니다.

do while
Array.prototype.each = function(iterator){
        var size = this.length-1;
        if(size>-1){
            do{
                iterator(size,this[size]);
            }while(size--);
        }
};

for
Array.prototype.each = function(iterator){
        var size = this.length;
        for(var i=0;i<size;i++){
            iterator(i,this[i]);
        }
};

이유는 배열이기 때문에 그렇습니다.
확인 해볼결과 배열은 스택으로 구현되어있습니다.;;
Posted by 전용우
,
1.가능하면 간단하게 만들어라
function foo(){
   var i;
   ......
   i=5;
}
보다는
function foo(){
   var i=5;
   ......
}

2.보다 빠르게 dom을 찾기위해 정확하게 해라.
var link = location.href;
보다는
var link = window.location.href;

3.프로퍼티를 찾는것을 캐싱을 해라.(.이나 []을 사용을 최소화해라.)
for(var i = 0; i < 1000; i++)
  a.b.c.d(i);
보다는
var e = a.b.c.d;
for(var i = 0; i < 1000; i++)
  e(i);
----------------------------------------------------------------------
for (i=0; i<someArrayOrObject.length; i++)
보다는
for (i=0, var n=someArrayOrObject.length; i<n; i++)


4.이름으로 찾기보다는 index을 이용해라.
var form = document.f2;
보다는
var form = document.forms[1];

5.with 구문을 피해라.
with (document.formname) {
field1.value = "one";
field2.value = "two";...
}
보다는
var form = document.formname;
form.field1.value = "one";
form.field2.value = "two;

6.하위트리를 오프라인으로 처리한후 추가하거나 수정해라.
(부가 설명을 드리면 오프라인이란 추가하거나 수정할 노드가 현재 보여지는 페이지의
노드가 아닌것을 말합니다.그래서 매번 추가하거나 수정할때 오프라인이 아닌경우 페이지가
업데이트가 되기 때문에 느려집니다.코드를 보시면 좀더 쉽게 이해하실겁니다.
근데 제가 알기로는 이러한 방식은 ie에서 메모리 릭이 생기는걸로 알고 있습니다.)


var tableEl, rowEl, cellEl;
var numRows = 10;
var numCells = 5;
tableEl = document.createElement("TABLE");
tableEl = document.body.appendChild(tableEl);
for (i = 0; i < numRows; i++) {
  rowEl = document.createElement("TR");
  for (j = 0; j < numCells;j++) {
    cellEl = document.createElement("TD");
    cellEl.appendChild(document.createTextNode("[row "+i+" cell "+j+ "]"));
    rowEl.appendChild(cellEl);
  }
  tableEl.appendChild(rowEl);
}

보다는
var tableEl, rowEl, cellEl;
var numRows = 10;
var numCells = 5;
tableEl = document.createElement("TABLE");
for (i = 0; i < numRows; i++) {
  rowEl = document.createElement("TR");
  for (j = 0; j < numCells;j++) {
    cellEl = document.createElement("TD");
    cellEl.appendChild(document.createTextNode("[row " +i+ " cell "+j+"]"));
    rowEl.appendChild(cellEl);
  }
  tableEl.appendChild(rowEl);
 }
document.body.appendChild(tableEl);

----------------------------------------------------------------------------
var ul = document.getElementById("myUL");
for (var i = 0; i < 200; i++) {
  ul.appendChild(document.createElement("LI"));
}

보다는
var ul = document.getElementById("myUL");
var li = document.createElement("LI");
var parent = ul.parentNode;
parent.removeChild(ul);
for (var i = 0; i < 200; i++) {
  ul.appendChild(li.cloneNode(true));
}
parent.appendChild(ul);


8.오브젝트 리터럴을 사용해라.
car = new Object();
car.make = "Honda";
car.model = "Civic";
car.transmission = "manual";
car.miles = 1000000;
car.condition = "needs work";
보다는
car = {
  make: "Honda",
  model: "Civic",
  transmission: "manual",
  miles: 1000000,
  condition: "needs work"
}

9.자주사용되는 값은 캐싱해라.
var d=35;
for (var i=0; i<1000; i++) {
  y += Math.sin(d)*10;
}
보다는
var d=35;
var math_sind = Math.sin(d)*10;
for (var i=0; i<1000; i++) {
  y += math_sind;
}

10.로컬변수를 사용해라
(찾는순서가 로컬변수를 먼저찾기 때문.)
function MyInnerLoop(){
  for(i=0;i<1000;i++);
}
보다는
function MyInnerLoop(){
  for(var i=0;i<1000;i++);
}

11.if문보다는 switch문이 좋다.
(위의말은 정답이 아니다.일반적으로 작은수에 switch문 비교는 효율이 있지만 큰수나 문자간의 비교는 매우느리다고 합니다.그럴때 hash을 이용하라고 합니다.
그리고 추가로 일반적인 언어에도 if문과 switch문은 사용용도가 틀리다고 합니다.)
if(n==12)
  someBlock();
else if(n==26)
  someOtherBlock();
보다는
switch(a){
  case 12 :
    someBlock();
    break;
  case 26 :
    someOtherBlock();
    break;
}

12.변하지 않는 코드는 루프밖으로 빼라.
for (i=0;i<iter;i++) {
  d=Math.sqrt(y);
  j+=i*d;
}

보다는
d=Math.sqrt(y);
for (i=0;i<iter;i++) {
  j+=i*d;
}

13.감소루프를 이용해라.

function loopNormal() {
  for (var i=0;i<iter;i++) {
    // do something here
  }
}
보다는
function loopReverse() {
  for (var i=iter;i>0;i--) {
    // do something here
  }
}

14.for문 보다는 do while문 빠르다.[각주:1]
15.do while문보다는 감소하는 do while문이 빠르다.
젤 아래 루프문이 제일빠름
var k=num-1;
do {}
while (k--);

var k=num;
do{}          
while (--k);
(테스트 해본 결과 정말 마지막 do while이 일반적인 for문보다 약 2배 좀 넘게 빠른것 같습니다.ie6,7 ff2.0 opera9.2 모든 브라우저에서 빠릅니다.하지만 원글의 표에 나와있는 차이는 안나는것 같습니다.)

원글에서 짤라서 필요한것들만 적었습니다.
자세한글을 보시기 원하시면 원글을 읽어 보시기 바랍니다.

같이 보면 좋은글
IE+JScript Performance Recommendations Part 1
IE+JScript Performance Recommendations Part 2
IE+JScript Performance Recommendations Part 3
Posted by 전용우
,
난 이게 왜이렇게 헤갈리냐.ㅡㅡ;

사용자 삽입 이미지

via pro javascript
Posted by 전용우
,

엘리먼트에 메소드 추가하는것은 많은 자바스크립트 프래임워크에 많이 나와있습니다.
근데 다 조금씩 틀려서 제 취향에 맞추어서 만들었습니다.

만들때 목표를 잡은것은.

첫째.ie에서 매모리릭 방지하기.
두번째.Browser Sniffer 한번만 하기.
세번째.this scope사용하기.

생각하고 만들었는데.this scope사용하는것 아무래도 안될것 같아서 prototype에서 getElement을 사용했습니다.

문제가 attachEvent을 사용하면 scope가 바뀌는데 그것을 방지하기위해 apply를 사용했습니다.이렇게 하면 this의 scope가 잘되는데 문제는 detachEvent을 사용할때 작동이 안됩니다.
아무래도 detachEvent할때 scope문제가 있는것 같습니다.
(혹시 알고 계신분있으면 댓글 부탁드립니다ㅡㅡ;)

이렇게 해서 간단하게 만들어봤습니다.
거의 prototype아티클 짬뽕해서 만들었습니다.

사용법은
ManageEvent.remove(엘리먼트,이벤트명,함수);
ManageEvent.add(엘리먼트,이벤트명,함수);
ManageEvent.getElement(event);


Posted by 전용우
,