'Javascript'에 해당되는 글 112건

  1. 2012.01.29 체이닝 패턴
  2. 2012.01.29 자바스크립트에서 static메서드 구현.
  3. 2012.01.29 객체 상속을 이용한 객체유효범위를 생성하는 샌드박스 만들기. 1
  4. 2012.01.29 모듈패턴 Ⅱ
  5. 2012.01.28 모듈패턴 Ⅰ
  6. 2012.01.01 자바스크립트 코딩 컨벤션(관습, 관례) 4
  7. 2011.12.27 자바스크립트 최적화 문법정리 4
  8. 2011.12.21 new Object()는 Factory일까? 1
  9. 2011.12.21 자바스크립트 호이스팅(hoisting)이란?
  10. 2011.12.21 자바스크립트 call by value or call by reference 1

체이닝 패턴

자바스크립트 체이닝 패턴이란?

메서드 호출 시 함수 내부에 명백한 반환값 이 존재하지 않는다면, 객체 자신을(this)을
반환시켜
메서드를 마치 하나의 문장처럼 사용할 수 있게 작성하는 패턴입니다.

체이닝 패턴을 가장 잘 활용한 공개 프레임웍으로는 대표적으로 Jquery를 들 수 있습니다.

  1. var train = (function(){
  2.    
  3.     function instance(){
  4.         this.index = 0;
  5.         return this;
  6.     };
  7.  
  8.  
  9.     instance.prototype = {
  10.         get: get,
  11.         set: set
  12.     };
  13.  
  14.     function get(){
  15.         return this.index;
  16.     };
  17.  
  18.     function set(){
  19.         this.index++;
  20.        
  21.         // 함수 고유의 명백한 반환값이 존재하지 않는다면, 객체 자신인 this를 반환 시킨다.
  22.         return this;
  23.     };
  24.  
  25.     return new instance();
  26.  
  27. })();
  28.  
  29. alert(train.set().set().get()); // 2

앞서 말했듯이 train객체의 메서드 사용이 간결해 지는 것을 볼 수 있습니다.

자바스크립트에서 static메서드 구현.


static 속성과 메서드란?

- 함수 객체의 인스턴스 맴버와는 달리 각 인스턴스에 따라 달라지지 않는 맴버 그룹을
가리키며,
인스턴스를 생성하지 않고 사용할 수 있어야 한다.

자바스크립트에서의 static 구현은 아래와 같이 함수 객체에 속성을 추가하는 방법으로
구현한다.


또한, 이 방법은 자바스크립트 디자인 패턴중 하나인 메모제이션 패턴에서도 활용된다.



1. 공개 static 맴버.

  1. // 일반적인 함수 객체의 인스턴스 맴버
  2. var constructor_fn = (function(doc, win){
  3.        
  4.         return function(x, y){
  5.        
  6.             this.x = x;
  7.             this.y = y;
  8.  
  9.             return this;
  10.         }
  11.  
  12. })(document, window);
  13.  
  14. var obj1 = new constructor_fn(1, 2);
  15. alert(obj1.x + ' '  + obj1.y); // 1, 2
  16.  
  17. var obj2 = new constructor_fn(3, 4);
  18. alert(obj2.x + ' '  + obj2.y); // 3, 4
  19.  
  20.  
  21. // 인스턴스 맴버에 따라 맴버 그룹이 달라지지 않으며, 인스턴스를 생성하지 않고 사용할 수 있다.
  22. var static_fn = (function(doc, win){
  23.        
  24.         return function(){
  25.             return this;
  26.         }
  27.  
  28. })(document, window);
  29.  
  30. static_fn.x = 1;
  31. static_fn.y = 2;
  32.  
  33. alert(static_fn.x + ' '  + static_fn.y); // 1, 2


2. 비공개 스태틱 맴버.

동일한 생성자 함수로 생성된 객체들이 공유하는 맴버이며, 생성자 외부에서는
접근할 수 없다.


즉, 비공개 메서드로 구현한다.

자바스크립트 클로져 및 비공개 맴버 작성법을 활용하여 비공개 static 맴버를 구현한다.




  1. // 동일한 생성자 함수(constructor_fn())로 생성된 객체들이 공유하는 맴버이며, 생성자 외부에서는 접근할 수 없다.
  2. var constructor_fn = (function(doc, win){
  3.        
  4.         var key = 0;
  5.  
  6.         return function(x, y){
  7.        
  8.             this.x = x;
  9.             this.y = y;
  10.             this.getKey = getKey;
  11.  
  12.             return this;
  13.         }
  14.  
  15.         function getKey(){
  16.             return key++;
  17.         }
  18.  
  19. })(document, window);
  20.  
  21.  
  22. var obj1 = new constructor_fn(1, 2);
  23. alert(obj1.x + ' '  + obj1.y +  ' ' + obj1.getKey() ); // 1, 2, 1
  24.  
  25. var obj2 = new constructor_fn(1, 2);
  26. alert(obj2.x + ' '  + obj2.y +  ' ' + obj2.getKey() ); // 3, 4, 2
  27.  
  28.  
  29.  
  30. // 위와 같이 유일키 반환 알고리즘에 쓰일 수 있다.


객체 상속을 이용한 객체유효범위를 생성하는 샌드박스 만들기.


자바스크립트 객체 상속(얕은 복사)을 이용하여 callback 함수 내부에서는 새롭게 할당(배열 인자값)된 객체 만을 사용할 수 있습니다.

공개 메서드(생성자 함수)에 이 패턴을 적용하여, 타 모듈에 대한 접근 권한을 둘 수 있습니다.

  1. var Sendbox = (function (doc, win) {
  2.     return function (opt, callback) {
  3.  
  4.         var parent = {};
  5.  
  6.         function init() {
  7.  
  8.             this.module = null;
  9.             this.boxs = [];
  10.  
  11.             marge.call(this, opt);
  12.  
  13.             var module = this.module
  14.               , boxs = this.boxs;
  15.  
  16.             if (boxs) {
  17.                 var hasOwnProperty = Object.prototype.hasOwnProperty;
  18.                 if (boxs.constructor === Array) {
  19.                     for (var i = 0, length = boxs.length; i < length; i++) {
  20.                         if (hasOwnProperty.call(module, boxs[i])) {
  21.                             parent[boxs[i]] = module[boxs[i]];
  22.                         }
  23.                     }
  24.                 }
  25.                 else if (boxs.constructor === String && boxs === '*') {
  26.                     for (var n in module) {
  27.                         if (hasOwnProperty.call(module, n)) {
  28.                             parent[n] = module[n];
  29.                         }
  30.                     }
  31.                 }
  32.             }
  33.  
  34.  
  35.             if (typeof callback === 'function') {
  36.                 callback.call(null, parent);
  37.             }
  38.  
  39.         };
  40.  
  41.         return new init();
  42.     };
  43.  
  44.     function marge() {
  45.  
  46.         var target = this
  47.           , opts = []
  48.           , src = null
  49.           , copy = null;
  50.  
  51.         for (var i = 0, length = arguments.length; i < length; i++) {
  52.  
  53.             opts = arguments[i];
  54.  
  55.             for (var n in opts) {
  56.  
  57.                 src = target[n];
  58.                 copy = opts[n];
  59.                 target[n] = copy;
  60.             }
  61.         }
  62.  
  63.         return target;
  64.     }
  65. })(document, window);
  66.  
  67.  
  68.  
  69. Sendbox({
  70.     module: modules,
  71.     boxs: ['module1', 'module2']
  72. }, function (box) {
  73.     for (var n in box) {
  74.         alert(n); // module1 module2
  75.     }
  76. });



모듈패턴 Ⅱ


이전 포스트에서 설명드렸던 모듈패턴에 대해 이번 포스트에서는 좀더 다향한 패턴으로 작성해 보도록 하겠습니다.


  1. // 객체 리터널을 사용하여 작성하는 패턴입니다.
  2. var constructor_fn = (function(doc, win){
  3.     // 객체 리터널 형식을 사용하는 작성법
  4.    
  5.     var x = 0
  6.       , y = 0;
  7.  
  8.     return {
  9.         getX: function(){
  10.             return x;
  11.         },
  12.         getXfn: function(){
  13.             return this.getX;
  14.         }
  15.     };
  16.  
  17. })(document, window);
  18.  
  19. // 외부에 노출된 getX 메소드를 변경시키면 메소드 원본이 변경되었으므로 더이상 사용할수 없게 되었습니다.
  20. constructor_fn.getX = null;
  21. alert(constructor_fn.getXfn()); // null
  22.  
  23.  
  24.  
  25.  
  26. // 이번 패턴은 따로 비공개 메서드를 두어 공개할 메서드만 골라서 노출시키는 패턴입니다.
  27. var constructor_fn = (function(doc, win){
  28.     // 객체 리터널 형식을 사용하는 작성법
  29.    
  30.     var x = 0
  31.       , y = 0;
  32.    
  33.     // 비공개 메서드 
  34.     function getX(){   
  35.         return x;
  36.     };
  37.  
  38.     function getY(){
  39.         return y;
  40.     };
  41.  
  42.     function setX(x){
  43.         x = x;
  44.     };
  45.  
  46.     function setY(y){
  47.         y = y;
  48.     };
  49.  
  50.  
  51.     return {
  52.         getX: getX,
  53.         getX1: getX,
  54.         getY: getY
  55.     };
  56.  
  57. })(document, window);
  58.  
  59.  
  60. constructor_fn.getX = null;
  61. // 이전처럼 외부에 노출된 getX 메서드를 변경 하여도 메서드 원본이 비공개 함수로 정의되어 있으므로
  62. // getX 메소드는 공개된 메서드인 getX1를 호출하여도 잘 돌아가게 되는 것입니다.
  63. alert(constructor_fn.getX); // null
  64. alert(constructor_fn.getX1); // function(){ ; }

모듈패턴 Ⅰ


이번 포스트 부터는 자바스크립트에서의 모듈패턴에 대해 알아 보도록 하겠습니다.

우선 생성자 함수 모듈패턴의 기본 구조인 "객체 생성"과 관련된 내용에 대해서는 이전 포스트를 참고 하시길 바랍니다.

 

이번 포스트에서는 모듈패턴에 관한 내용만 설명해 드리도록 하겠습니다.

 

먼저 생성자 함수를 사용하여 객체를 생성하면 인스턴스 맴버 와 프로토타입 맴버라는 그룹을
가지게 됩니다.

 

먼저 인스턴스 맴버에 대해 설명하면, 기본 구조는 생성된 빈 객체(this)에 포함되는 맴버 그룹이며, 각 객체 생성 시마다 새로운 맴버그룹이 생성(맴버 초기화)됩니다.

 

프로토타입 맴버는 함수객체 속성인 "prototype" 객체속성을 사용하여 생성할 수 있는 맴버 그룹이며, 인스턴스 맴버와는 달리 함수 생성 시에만 생성(맴버 초기화)됩니다.

 

이와 같은 이유로 인스턴스는 맴버는 각 객체에 종속된 맴버를 가져야 할 때 사용되며, 프로토타입 맴버는 그 종속된 맴버들을 재 사용하기 위한 메소드들을 포함합니다.

 

또한, 이렇게 작성하는 것이 모듈패턴에 대한 최적화 문법이기도 합니다.


  1. function constructor_fn()
  2. {
  3.     this.instance_memeber1 = 'instance_memeber1';
  4.     this.instance_memeber2 = 'instance_memeber2';
  5. };
  6.  
  7. constructor_fn.prototype.prototype_memeber1 = function()
  8. {
  9.     return this.instance_memeber1;
  10. };
  11.  
  12. constructor_fn.prototype.prototype_memeber2 = function()
  13. {
  14.     // 함수 내부의 "this" 는 자신을 호출한 객체가 되며, 또 객체로 인한 호출이 아닌 경우 window 전역객체가 된다.
  15.  
  16.     // 여기서 "this"는 "constructor_fn.prototype" 객체속성이 된다.
  17.     if (this === constructor_fn.prototype){
  18.         return 'constructor_fn.prototype';
  19.         // true
  20.     };
  21.        
  22.     // 여기서 "this"는 "constructor_fn.prototype" 객체속성이 된다.
  23.     if (this === obj){
  24.         return this.instance_memeber2;
  25.     };
  26. };
  27.  
  28. // constructor_fn.prototype 객체 속성으로 호출한다.
  29. alert(constructor_fn.prototype.prototype_memeber2()); // constructor_fn.prototype
  30.  
  31.  
  32. var obj = new constructor_fn();
  33. // 객체의 인스턴스 맴버에 접근한다.
  34. alert(obj.instance_memeber1); // instance_memeber1
  35. // 객체의 프로토타입 맴버에 접근한다.
  36. alert(obj.prototype_memeber2()); // instance_memeber2
  37.  
  38.  
  39. // 즉시 실행 함수 패턴을 응용한 생성자 함수 모듈패턴
  40. // 일단은 이런식의 구현이 가능하다는 것만 알아두도록 하자!!!
  41. var constructor_fn = (function(doc, win){
  42.     return (function()
  43.     {      
  44.         function init(){           
  45.             this.instance_memeber1 = 'instance_memeber1';
  46.             this.instance_memeber2 = 'instance_memeber2';
  47.         };
  48.  
  49.         init.prototype = {
  50.             prototype_memeber1: function(){
  51.                 return this.instance_memeber1;
  52.             },
  53.             prototype_memeber2: function(){
  54.                 return this.instance_memeber2;
  55.             }
  56.         };
  57.  
  58.         return new init();
  59.     })();
  60. })(document, window);
  61.  
  62. alert(constructor_fn.prototype_memeber1()); // instance_memeber1
  63. alert(constructor_fn.prototype_memeber2()); // instance_memeber2

자바스크립트 코딩 컨벤션(관습, 관례)


"프로그래머들은 보통 자신이 작성한 코드를 맹신하는 경향이 있습니다."

자신은 완벽한 프로그래머이며, 자신이 작성한 코드는 완벽한 코딩 스타일과 최적화를 통해
작성되어 
있다고 생각하고 그렇게 맹신해 버리는 프로그래머들이 생각보다 많습니다.

이런 생각은 자신의 자신감을 PR 할 수 있는 표현으로만 쓰는 것이 좋지 않을까요?


이런 생각 때문에 자신이 작성한 코드의 품질이 낮아지고 또 그 당시 작성된 그런 코드 때문에
훗날 유지보수로 말미암아 괜한 골머리를 써야 하는 것 보다는 말입니다.


그럼 프로그램을 유지보수 하기 위해 드는 비용에는 어떤 것들이 있을까요?

1) 문제를 다시 학습하고 이해하는 데 걸리는 비용.
2) 문제를 해결하는 코드를 다시 이해하는 데 걸리는 비용.


위의 내용처럼 자신이 작성한 프로그램 읽지라도 작성된 시간이 얼마간 지나 코드를
유지보수 할 시에는 그것을 작성하지 않은 타인과 크게 다르지 않은 비용이 소요된다는

것을 알 수 있습니다.

"코드는 작성하는 것만큼 읽고 이해하는데 더 많은 시간과 비용이 소요되기
때문입니다."


위와 같은 문제를 최소화하려는 방법의 하나가 이번 포스트에서 말하려 하는 내용인
내부적으로 코딩 규칙을 만들어 일정한 규칙을 세우는 것입니다.


- 아래는 먼저 코딩 규칙에 대한 장점을 나열한 것입니다.


1) 읽기 쉽다.
2) 직관적이며 일관성이 지켜진다.
3) 예측 가능하고 이해하기 쉽다.
4) 한 사람이 작성한 것처럼 보인다.
5) 문서화 되어 있다.

- 위의 장점을 한 단어로 표현하자면 코드의 가독성이 향상된다는 말입니다.



Q: 여기서 가독성이란?


A: 일관성 있는 규칙을 세워 코드의 이해도를 높임으로써 협업 시 야기되는 개발자 간의

혼란을 줄이며 그에 따라 코드의 유효기간이 연장됩니다.


많은 회사(구글, YAHOO, NHN)나 단체(비영리 단체)에서 개발자들이 지켜야 할 수많은

규칙에 관한 메뉴얼를 내부적으로 배포하고 있으며 이 부분의 논쟁 또한 끊이지 않고 있는

현실입니다.


예를 들어 "효율적인 들여쓰기 방법" 이라던지 "세미콜론의 존재 여부"와 같은 내용입니다.

생각해보면, 별거 아니라고 생각할 정도의 아주 사소한 문제에 대해서도 현재 팽팽히

논쟁 되고 있으며, 이 같은 내용을 하나하나 따져가며 코드를 최적화하는 것이 최종

목표라 할 수 있을 것입니다.




1. 전역 변수 최소화


자바스크립트에서의 함수는 크게 두 가지 특성이 있습니다.

하나는 함수는 객체라는 것이며, 다른 하나는 함수는 유효범위(Scope)를

제공한다는 것입니다.


(여기서 유효범위를 제공한다는 말은 프로그램 설계상 함수는 개별적으로 각자의

유효범위를 가진다는 말로 해석됩니다.)


함수 내부에서 var로 선언된 변수는 해당 함수의 지역 변수(Private variable)가

되며 기본적인 방법으로는 함수 외부(window)에서 접근할 수 없습니다.


(함수 외부에서 접근 할
수 있는 우회적인 방법으로는 비공개 구성원(공개 메소드)을

공개 방법으로 노출하는 방법이 있습니다.

하지만 상세 방법에 대해서는 이 장에서는 다루지 않겠습니다.)


반대로 함수 내부에서 var로 선언하지 않은 변수는 전역 변수(Global variable and Window property)가 되어 코드 상 어디에서든지 사용할 수 있습니다.


또한, 함수 내부에서 전역변수를 선언하는 것은, 전역객체(window)의 프로퍼티를 만드는

것과 동일 합니다.


"브라우저에는 전역객체(Window) 자기 자신을 참조하는 프로퍼티인 window

프로퍼티가 존재합니다."


  1.     (function(){
  2.         return function(){
  3.            
  4.             var private_variable = null; // 지역 변수
  5.             alert(private_variable === window['private_variable']); // false
  6.      
  7.             global = null; // 전역 변수 && 전역 프로퍼티
  8.             alert(global === window.global); // true
  9.      
  10.             alert(this === window); // true (Window 전역객체)
  11.             alert(this.window === window.window); // true (Window.window property)
  12.         };
  13.     })()();



- 전역 변수의 장/단점


"전역 변수의 장/단점은 코드 상 어디서든지 사용될 수 있으며 공유된다는 점입니다."


즉, 모든 전역변수는 같은 전역객체(Window)의 프로퍼티로 존재하기 때문에,

코드 상 어디서든지 접근할 수 있으며 같은 이름으로 재정의할 경우 이전의 전역변수를

덮어쓰게 된다는 단점이 있습니다.


  1.     var test = 'test';
  2.      
  3.     alert(window.test) // test
  4.      
  5.     alert(this.test === window.test); // true (Window.test === Windown.test)
  6.     alert(window.test === window.window.test); // true (Window.test === Window.window.test);
  7.     window.test = 'test1';
  8.      
  9.     alert(window.test) // test1
  10.      
  11.      
  12.     var global = null;
  13.      
  14.     function fn(){
  15.         // 선언된 전역변수의 값을 덮어쓴다.
  16.         global = 'global'
  17.     }
  18.      
  19.     fn();
  20.      
  21.     alert(global) // 'global'


또한, 내부에 있는 자바스크립트 코드가 아니더라도 외부 코드와 밀접하게 커플링 되어

있다면 해당 문제에 부딪힐 수 있는 가능성이 큽니다.




- 관련사례 리스트


1) 서드파티 자바스크립트 라이브러리
2) 광고 제휴 업체의 스크립트
3) 사용자를 추적하고 분석하는 서드파티 스크립트 코드
4) 다양한 위짓, 배지, 버튼 등


지금까지 설명한 여타 문제들을 최소화하기 위해서는 근본적인 문제인 전역변수의 수를

줄이며 또한 이는 성능 및 최적화에도 도움을 줍니다.

"그에 따른 성능 및 최적화 부분에 대해서는 이번 글의 주제에서 벗어나는 내용이므로

넘어 가도록 하겠습니다."


- 전역 변수에 대한 실수와 오해!!


Q: "위의 코드가 어떤 식으로 선언되며 정의될 거라고 생각하시나요?" 


  1. function a(){
  2.     var a = b = 0;
  3. }


Q: "아마도 a, b 지역변수에 0이 정의되었다고 생각하시지 않나요?"

"하지만 실상은 그렇지 않습니다."

A:

"이유는 자바스크립트의 평가는 오른쪽에서 왼쪽으로 흘러가는 형태이기 때문입니다."


평가순서를 나열하자면 먼저 b = 0;이라는 표현식이 평가되고 이때 b는 선언되지 않은
상태
(var가 없이 선언된 상태!!)이며 그다음 Literal 0은 var로 선언된 a 지역변수로
정의
됩니다.


다시말해
, a, b 변수 중 a만 var로 말미암아 지역변수로 선언되었고 b는 의도되지
않게
전역변수로 선언되었다는 것입니다.


"아마도 많이 실수하는 부분이라고 생각하지 않으시나요?"



이런 실수를 없애기 위해서는 함수작성 시 반드시 지역변수에 대한 선언 및 초기화를

미리 해야 합니다.


  1.     function a(){
  2.      
  3.         var a = 0
  4.           , b = 0;
  5.      
  6.         a = b = 0;
  7.     }

두 번째로 많은 개발자가 오해하는 부분 중 하나인 전역객체에 대한 이야기입니다.


  1.     var global = 1;
  2.      
  3.     delete global;
  4.      
  5.     alert(global) // 1

 

결과가 위와 같이 나오는 이유는 전역변수가 var로 선언되었기 때문입니다.


즉 "delete" 연산자를 이용한 객체삭제 시에는 명시적으로(var로 선언된) 선언된

전역변수는 삭제할 수 없습니다.



  1.     window['global_1'] = 1;
  2.     global_2 = 2
  3.      
  4.     delete window['global_1'];
  5.     delete global_2;
  6.      
  7.     alert(window['global_1']) // undefined
  8.     alert(global_2) // undefined



위에서 보는 바와 같이 명시적으로 선언된 전역변수가 아니라 암묵적으로 선언된

전역변수는 "delete" 연산자 때문에 삭제되어 undefined를 반환하였습니다.




 2. var 변수선언


변수를 연속적으로 선언할 때 var를 생략하고 쉼표(,)로 구분하여 선언하는 것이

효율적입니다.


아래와 같이 쉼표를 변수명 앞에 작성하면 변수를 추가/삭제/주석 처리 시 쉽습니다.

선언문과 실행문은 구분해 주는 것이 좋습니다.


선언문 끼리의 구분 및 실행문과의 구분또한, 빈 줄을 통해 구분해 주는것이 가장 좋으며,

삽입줄 수는 1 ~ 2줄이 가장 좋습니다.


선언문 끼리의 구분.


  1.     var a = null // a변수는 문자열값 입니다.
  2.       , b = null
  3.       , c = null
  4.       , d = null;
  5.      
  6.     var e = {
  7.         e1: 'e1' // el 변수는 숫자 입니다.
  8.       , e2: 'e2'
  9.       , e3: 'e3'
  10.       , e4: 'e4'
  11.     }



선언문과 실행문의 구분.


  1.     var a = null // a변수는 문자열값 입니다.
  2.       , b = null
  3.       , c = null
  4.       , d = null;
  5.  
  6.     if (a) b = true;
  7.     else if (b) c = true;
  8.     else d = true;




함수 선언부 사이에도 적절한 구분이 필요합니다.



  1.     function a(){
  2.     }
  3.      
  4.     function b(){
  5.     }



자바스크립트만의 객체리터널 표현식 내부의 함수 선언부에서도 아래와 같이 구분을

지어줍니다.


  1.     var fnSet = {
  2.        
  3.         a: function(){
  4.         },
  5.      
  6.         b: function(){
  7.         }
  8.     }





3. for 루프


  1.     for (var i = 0, length = o.length; i++){
  2.         ...
  3.     }

이런 방식으로 작성하면 length 값을 한 번만 구하고, 순회하는 동안 지역변수에
캐시
되어 있는 length를 사용하게 됩니다.

length를 캐시 하면, 사파리 3에서 2배, IE7에서 190배에 이르기까지 모든 브라우저에서
속도가
향상됩니다.


  1.     for (; i = o.length; i--){
  2.         ...
  3.     }



- 또 위의 작성법은 아래와 같은 장점이 있습니다.

1) 변수를 하나 덜 쓴다.
2) 카운트를 거꾸로 하여 0으로 내려간다. ("0과 비교하는 것이 배열의 length 또는 0이 아닌 값과 비교하는 것보다 대개 더 빠르기 때문이다")




4. for-in 루프


for-in 루프는 배열이 아닌 객체의 프로퍼티를 순회하기 위한 Statment입니다.

자바스크립트에서 배열은 객체이므로 기술적으로는 for-in 문으로 처리할 수 있지만 

추천하지는 않습니다.

이유는 보통 배열 값을 순회할 때에는 순차적인 작업이 많으나 for-in 문으로 순회할

때에는 순서를 보장하지 않기 때문입니다.


"그래서 배열객체([])는 일반 for 문으로 객체({})는 for-in 문으로 돌리는 것이

맞습니다."


  1. var object = {a: 'a', b: 'b'};
  2. for (var n in object){
  3.     var hasOwnProperty =  Object.prototype.hasOwnProperty;
  4.  
  5.     if (hasOwnProperty.call(object, n)){
  6.         alert(n);      
  7.     }
  8. }


5. switch문

여러 자바스크립트 문법 중에서 가장 코드의 가독성을 해치는 것이 바로 스위치 문입니다.

보통 if...else...문이 여러 차례 반복되는 문장을 리펙토링 하려는 경우에 많이

쓰이는데 단순히 변수를 할당하는 목적으로 쓰인다면 객체를 이용하여 가독성 및 효율을

높일 수 있습니다.

  1. var ret = '';
  2. switch (expression){
  3.  
  4.     case 'a' :
  5.         ret = '1'
  6.         break;
  7.     case 'b' :
  8.         ret = '2'
  9.         break;
  10.     case 'c' :
  11.         ret = '3'
  12.         break;
  13. }
  14.  
  15. return ret;
  16.  
  17. return {
  18.     'a': '1'
  19.   , 'b': '2'
  20.   , 'c': '3'
  21. }[expression];


6. 암묵적 타입 캐스팅 피하기


 
자바스크립트 변수를 비교할 때 암묵적으로 타입캐스팅을 실행하기 때문에 false == 0이나

"" == 0의 비교가 true를 반환한다.


암묵적 타입캐스팅 때문인 혼동을 막기 위해서는 항상 표현 의 값과 타입을 모두

확인하는 === or !== 연산자를 사용해야 합니다.


  1.     false == 0 // true
  2.     false === 0 // false
  3.      
  4.     false == null // true
  5.     false === null // false



 7. 들여쓰기


코드 가독성을 위한 가장 기본적인 규칙이다. 하지만 일관성 없는 들여쓰기는 개발자에게
더 큰 혼란을 가져다준다.



"현재 가장 널리 사용하는 방법은 한 TAB에 스페이스 4칸을 들여쓰기하는 방법이다."

 

Q: 어떤 것을 들여써야 하는가?

A: 여러 Statement (block({ }), if, else, else if, do, while, do-while,

for, for-in, switch) 및 Literal(object literal)에 들여쓴다.


  1.  
  2.     {
  3.         body;
  4.     }
  5.      
  6.     function fn(){
  7.         body;
  8.     }
  9.      
  10.     if (){
  11.         body;
  12.     }
  13.     else if(){
  14.     }
  15.     else{
  16.     }
  17.      
  18.     switch(expression){
  19.     }
  20.      
  21.     object_literal = {
  22.         literal: literal
  23.     }
  24.      
  25.     하지만 아래와 같이 과도한 들여쓰기는 코드가동성을 떨어뜨립니다.
  26.      
  27.     function a()
  28.     {
  29.        
  30.         var b = true
  31.           , c = false
  32.           , ret = true;
  33.      
  34.         if (b){
  35.             c = true;
  36.         }
  37.             // 과도한 들여쓰기 이때 return문은 들여쓰기 할 필요가 없다.
  38.             return ret;
  39.     }

 


8. 중괄호

중괄호는 코드 상 생략할 수 있는 범위가 존재합니다.

예를 들어 아래 예제의 표현 식과 같이 한 줄만 있는 if 문 이나 for 이 있다면

중괄호를 생략하여도 문법적 오류가 발생하지 않는다는 것입니다.


"하지만 코드의 일관성 위해 쓰지 않는 것이 좋다고 합니다."


"하지만 개인적인 생각으로는 코드의 청결함(우아함)을 유지하는 것 또한 가독성 향상을

위해 좋은 일이라 생각하여 위의 의견에는 100% 동의하지는 않습니다."


  1.     // 여러줄 if문 중괄호
  2.     var ret = null;
  3.     if (true){
  4.         ret = true;
  5.         return ret;
  6.     }
  7.     else{
  8.         ret = false;
  9.         return ret;
  10.     }
  11.      
  12.     // 한줄 if문 중괄호
  13.     if (true) return true;
  14.     else if (false) return false;
  15.     else return false;
  16.      
  17.     //여러줄 for문 중괄호
  18.     for (; var length = length; i--){
  19.         if (true){
  20.             continue;
  21.         }
  22.     }
  23.      
  24.     // 한줄 for문 중괄호
  25.     for (; var length = length; i--){ alert(i); }



Q: 그렇다면 중괄호 시작 위치는?

이 부분에 대해서도 개발자들 사이에 같은 줄에 둘지, 아니면 다음 줄에 둘지에 대해서

의견이 분분합니다. (개인적인 입장에서는 같은 줄에 두는 것을 선호합니다.)


하지만 개인적인 취향 및 선호도뿐만 아니라 아래와 같이 중괄호의 위치에 따라

프로그램의 동작이 달라질 수도 있는 범위가 있습니다.


  1. function fn1(){
  2.     return {
  3.         name: 'fn'
  4.     }
  5. }
  6.  
  7. alert(fn1().name); //fn
  8.  
  9.  
  10. 보기2)
  11.  
  12. function fn2(){
  13.     return
  14.     {
  15.         name: 'fn'
  16.     }
  17. }
  18.  
  19. alert(fn2().name); //undefined


위와 같이 동작하는 이유는 자바스크립트의 세미콜론 삽입장치 때문입니다.

자바스크립트는 까다롭지 않은 언어라서(유연한 언어라서????) 세미콜론을 쓰지 않고

행을 종료하면 JS 엔진이 알아서 종료된 행에 세미콜론을 추가해줍니다.



이러한 동작 방식은 위와 같이 함수의 반환 값이 객체 리터널 이면서 이 객체를 반환하는

중괄호가 다음 줄에 와 있는 경우 발생합니다.



  1.     function fn2(){
  2.         return undefined;
  3.         {
  4.             name: 'fn'
  5.         }
  6.     }


마지막 방법으로 함수 선언 부의 중괄호 시작 위치와 함수 내부의 중괄호 시작 위치를 아래
코드처럼 구분하는 방법이 있습니다.



이렇게 작성함으로써 함수블록과 그 밖에 블록으로 구분할 수 있습니다.

  1. function a()
  2. {
  3.     var b = true
  4.       , c = false;
  5.  
  6.     if (b){
  7.         c = true;
  8.     }
  9. }

9. 공백(띄어쓰기)


"공백을 규칙적으로 활용하는 것이 가독성 향상을 위해 좋습니다."

  1. 쉼표(,):: 0,: 1
  2.  
  3. ) var array = ['a', 'b', 'c', 'd'];
  4.  
  5.  
  6. 괄호('(', ')'):: 0,: 0
  7.  
  8. ) if (true){ ; }
  9.  
  10. 세미콜론(;):: 0,: 1
  11.  
  12. ) var a = 'a'; var b = 'b'; var c = 'c';
  13.  
  14.  
  15.  
  16. 산술 연산자(+, -, *, /, % ...):: 1,: 1
  17.  
  18. )
  19. var x = a + b;
  20. var y = a / b;
  21.  
  22.  
  23. 관계 연산자(==, ===, !=, !==, <, >, <=, >= ...):: 1,: 1
  24.  
  25. )
  26. var x = a > b;
  27. var y = a < b;
  28.  
  29.  
  30. 1진 연산자(++, --):: 0,: 0
  31.  
  32. )
  33. var x = a++;
  34. var y = b++;
  35.  
  36. 단항 연산자와 피연자는 띄어 쓰지 않는게 좋습니다.
  37. 하지만 jsLint에서는 아래와 같은 문법을 권장하고 있습니다.
  38.  
  39. i++; --> i = i + 1 / i += 1
  40.  
  41. 이같은 방법을 권장하는 이유는 i++"과도한 기교"를 조장한다는 이유입니다.
  42.  
  43.  
  44. 할당(a = b):: 1,: 1
  45.  
  46. )
  47. var x = a;
  48. var y = c;
  49.  
  50.  
  51. 논리 연산자(&&, ||):: 1,: 1
  52.  
  53. )
  54. var x = a && b;
  55. var y = c || d;
  56.  
  57. -밸류 연산자({'a':'b'}):: 1,: 1
  58.  
  59. )
  60. var object = {
  61.     a: 'a'
  62.   , b: 'b'
  63.   , c: 'c'
  64.   , d: 'd'
  65. };
  66.  
  67. 인라인 주석(//): 후: 1
  68.  
  69. var x = a || b; // true
  70. var y = c || d; // false
  71.  
  72. 한줄 주석:
  73.  
  74. // 내용
  75.  
  76. 여러줄 주석:
  77. /*
  78. * 여러 줄 주석1
  79. * 여러 줄 주석2
  80. * 여러 줄 주석3
  81. * 여러 줄 주석4
  82. * 여러 줄 주석5
  83. */


여러 줄로 주석을 삽입할 경우 불필요한 과도한 주석은 붙이지 않는 것이 좋습니다.

여기서 과도한 주석이란? 코드 분석 시누구나 알 수 있는 내용이나 코드 자체가 명확한

내용은 굳이 주석으로 달 필요가 없습니다.


10. 작은 따옴표(기본)



자바스크립트에서 문자열(string)을 처리할 때 큰 따옴표("")와 작은 따옴표(')

어느 것을 사용해야 하는지를 한 번쯤 은 고민해 보았을 것입니다.


"결론부터 말하자면 "작은따옴표"를 기본으로 사용하는 것이 좋다 입니다."


이유는 자바스크립트 안에서 HTML 작성 시 큰 따옴표("")가 기본으로 사용되어야

하므로 백슬래시(\)를 이용하여 일일이 이스케이프(escape) 하는 상황이 발생하면

가독성이 크게 떨어지기 때문입니다.


  1.     var anchor = '<a href="/' + foo + '.html">' + foo + '</a>'; // correct
  2.     var anchor = "<a href=\"/" + foo + ".html\">" + foo + "</a>"; // incorrect
  3.     var anchor = "<a href='/" + foo + ".html'>" + foo + "</a>"; // none sense
  4.      
  5.     var anchor = "<a href='javascript:alert(\"test\")'>foo</a>";



11. 명명 규칙(여러 Literal)



"개발자로 하여금 코드에 내용을 좀 더 예측 가능하고 이해하기 쉬운 일관된
방식으로
작성하는 것입니다."


1) 변수/함수

단어 구분

생성자, 변수, 함수 등에서 단어와 단어 사이를 구분하기 위해서 자바스크립트에서는

보통 낙타 표기법을 사용하여 각각의 단어 사이를 나눕니다.


여기서 낙타 표기법을 포함한 여러 가지 표기법을 알아보자!!!!



낙타 표기법: 첫 번째 글자는 소문자이며 각각 추가되는 단어들의 첫 글자를 대문자로

표기하는 방법입니다.


getLastName
, setLastName;


파스칼 표기법: 첫 번째 글자는 대문자이며 각각 추가되는 단어들의 첫 글자를 대문자로
표기하는
방법입니다.


GetLastName, GetLastName;


헝가리안 표기법: 첫 번째 글자는 타입을 나타내는 소문자이며 바로 파스칼 표기법에

따라 표기하는 방법입니다.

strFirstName, strLastName;



앞에서 말한 바와 같이 생성자 함수와 일반함수는 첫 글자에 대/소 문자로 구분한다고

하였습니다.

그럼 변수는 어떠한가? 변수의 단어구분은 2가지 방법이 존재합니다

첫 번째로 바로 이전에 설명하였던 낙타 표기법이 있습니다.

두 번째 방법으로는 각각의 단어 사이를 _(언더 스코어)로 분리하는 방법의 표기법이

있습니다.

firstName, first_name;


- 상수

"프로그램 생명주기 동안 값이 변경 돼서는 안 되는 변수에 이름을 붙일 때 모두

대문자로 표기합니다."


MAX_WIDTH
, MIN_WIDTH;


1) 전역변수

전역변수 또한 상수와 마찬가지로 모두 대문자로 표기하거나 상수와의 구분을 위해 첫

글자에 구분자를 두는 것도 좋은 방법입니다.

MAX_WIDTH, G_MAX_WIDTH;



2) 내부 변수(비공개 구성원(지역변수이며 내부에서만 사용하겠다는 의미가 담긴 변수))

기능을 암시할 수 있도록 이름을 정하는 또 다른 규칙으로 내부 변수(비 공개 멤버)를
수 있습니다.

자바스크립트에서도 실질적으로 비공개 메서드들을 구현하는 방법이 존재하지만,
아래와 같이 _(언더 스코어)로 쉽게 구별할 수도 있습니다.



  1.     var obj = {
  2.         name: 'name'
  3.       , getName: function(){
  4.      
  5.           return this.name;
  6.       }
  7.        , _setName: function(name){
  8.      
  9.           this.name = name;
  10.       }
  11.     }
  12.      
  13.     obj._setName('test'); // 비공개 메소드로 표기되어있으나 실질적으로는 외부에서 접근/변경이 가능하다.
  14.     alert(obj.getName()); // test



12. 생성자함수


객체 인스턴스를 구성하는 역할을 담당하는 생성자함수는 일반함수와 구별하기 위해 첫 글자를
대문자로 씁니다.


  1.     - 생성자 함수
  2.     function Constructor(){ ; }
  3.      
  4.     - 일반 함수
  5.     function constructor(){ ; }


단수/복수 과거/현재


변수를 정의할 때 이름을 단수와 복수 그리고 과거와 현재를 구분하여 정의하면 코드에

이해도가 향상됩니다.


) value, values(단수와 복수), onload, onloaded(과거와 현재)


- 그 밖에 가독성 향상을 위한 표현식들...

3항 연산과 조건부 할당

다음 3가지 방법은 같은 결과를 가집니다.


  1.     var ret = false;
  2.      
  3.     if (true) ret = true;
  4.     else ret = false;
  5.      
  6.     ret = true ? true : false;
  7.      
  8.     ret = true || false;
  9.      
  10.      
  11.     예제 소스
  12.     if (true){
  13.         callback();
  14.     }
  15.      
  16.     true && callback();


정의와 동시에 사용

아래 코드는 정의와 동시에 push 메소드를 호출하고 있습니다.


a && 뒤의 표현식 []를 정의하는 부분은 괄호를 이용하여 표현식을 따로 분리해야 합니다.


  1.     var a = true;
  2.     (a && (a = [])).push(1);
  3.  


위의 내용 밖에도 더욱 다향한 규칙 및 가독성과 성능향상을 위한 표현식이 존재합니다.

마치며....

누구라도 알것입니다.


규칙을 세우는 것보다 중요한 것은 만들어진 규칙에 따라 코드를 작성하는

것이라는 사실을 말입니다.



- 이 내용은 "@파이어준님" 이 작성하신 "섹시한 자바스크립트 컨벤션"의
내용을 
일부 발채 하였습니다. - 







자바스크립트 최적화 문법정리


1. 유효범위 관리


이전 글에서 이미 설명했듯이 자바스크립트에서 유효범위를 관리한다는 것은 코드 성능과
매우 밀접한 관계를 맺고 있습니다.


간단히 설명하자면, 자바스크립트의 모든 함수는 유효범위 체인을 가지고 있고,
그 함수 내부의 식별자는 각각의 범위에 맞게 서로 다른 식별자를 담은 객체로 유효범위
체인 내에 그룹을 형성하고 있으며, 모든 식별자 분석 시 유효범위체인 내부에 생성된
객체를 검색하게 됩니다.


또 그 과정을 통해 체인 깊숙이 들어가 있는 식별자를 검색하기 위해서는 그만큼 시간과 비용이 듭니다.

즉, 결론적으로 유효범위를 올바르게 관리하지 못한다면, 그만큼 전체적인 성능저하가 일어날 수 있다는 얘기입니다.


더 자세한 내용은 아래 링크를 참고하시기 바랍니다.



1. 자바스크립트 함수 유효범위(Scope) 관리

2. 자바스크립트 함수 유효범위(Scope) 관리 및 Closer



2. 지역변수에 저장하여 사용하기


이 내용은 앞서 얘기한 유효범위 관리의 내용과 일치되는 내용이 많습니다.


이유는 유효범위체인 내부에 형성된 그룹객체 중 해당 함수의 지역변수들을 담고 있는
활성화 객체는 몇 가지 경우(with, catch의 스코프 확장)를 제외하고는 항시 체인의 가장 처음에 생성되어 있습니다.


그 말은 그만큼 검색시간이 짧아진다는 말과 같으며, 또한 검색시간이 짧아진다는 것은 전체적인 성능향상에 효율적이라는 말과 같습니다.

또한, 이와 같은 현상은 V8 엔진을 사용하는 구글 크롬과 nitro엔진을 사용하는 사파리 4+늘 제외한 모든 브라우저에서 볼 수  있는 현상입니다.


"이 둘은(크롬, 사파리) 자바스크립트 엔진이 너무 빨라서 식별자가 어디에 저장되어 있든 접근 속도에 별다른 영향을 주지 않습니다."


하지만 국내에서 가장 많이 쓰이고 있는 IE 및 그 밖에 대부분의 브라우저에서는 극명한 차이를 느낄 수 있습니다.

"즉, 함수에서 유효범위 밖에 있는 식별자를 두 번 이상 사용할 경우, 그 값을 지역변수에 캐시(정의)하여 사용하는 것이 전체적인 성능향상에 효율적이라는 얘기입니다."


  1. function createElement(){
  2.    
  3.     var createElem = document.createElement('div');
  4.     createElem.id = 'createElem';
  5.     createElem.innerHTML = 'createElem';
  6.    
  7.     document.body.appendChild(createElem);
  8.  
  9.     return document.getElementById('createElem');
  10. }
  11.  
  12. alert(createElement().id); // createElement
  13.  
  14.  
  15. alert((function(doc){
  16.    
  17.     // closer
  18.     return function createElement(){
  19.        
  20.         // 유효범위 체인의 가장 마지막(전역변수객체)에
  21.         // 생성되어 있는 document 전역객체를
  22.         // 해당 함수의 지역변수에 저장하여 식별자 분석시
  23.         // 빠르게 접근할수 있도록 사용하였다.
  24.         var createElem = doc.createElement('div');
  25.         createElem.id = 'createElem';
  26.         createElem.innerHTML = 'createElem';
  27.        
  28.         doc.body.appendChild(createElem);
  29.  
  30.         return doc.getElementById('createElem');
  31.     }
  32. })(document)().id); // createElement
  33.  


대부분의 브라우저에서 지역변수값을 R/W 하는데 드는 비용은 크게 차이 나지 않습니다.

하지만 배열이나 객체는 다릅니다.

이들로부터 값을 R/W 할 때에는 실제로 저장된 위치를 얻어야 하는데 배열은 인덱스를
객체는 해당 객체의 속성 이름을 얻어와야 합니다.


데이터 접근에 드는 비용과 시간은 데이터 구조의 깊이가 늘어남에 커지게 됩니다.

즉, new Object·test_variable_length의 데이터 접근이 newObject·test_variable_length·test_variable_length_1.
test_variable_length_2보다 빠르다는 것이며, 이를 최적화하려는 방법은 이전과 같이 해당 데이터를 지역변수에 캐시(정의) 하여 사용하는 것입니다.



참고로 객체의 속성에 접근하기 위해 자바스크립트에는 두 가지 표현 방법이 존재하는데, 그 방법으로는 점(.) 표기법과 대괄호(['속성']) 표기법이 있습니다.

보통 두 가지 표기법상에 성능 차이는 거의 없지만, nitro 엔진을 사용하는 사파리 등에서
대괄호 표기법을 사용하는 것이 점 표기법을 사용하는 것보다 성능상 좋지 문제점이 있습니다.



  1.     var newObject = {  
  2.         'test_variable_length': new Object().hasOwnProperty.length
  3.     };
  4.      
  5.     var newObject = {  
  6.         'test_variable_length': {
  7.             'test_variable_length_1': {
  8.                 'test_variable_length_2': {}.hasOwnProperty.length
  9.             }
  10.         }
  11.     };
  12.      
  13.      
  14.     // 데이터 접근에 지역변수를 활용하지 않은 예
  15.     (function(doc){
  16.        
  17.         // closer
  18.         return function createPrivateVariable(){
  19.            
  20.             var n = 0;
  21.             for (var i = 0, length = 200000; i < length; i++){
  22.                 for (var k = 0; k < newObject.test_variable_length; k++){
  23.                     n++;
  24.                 }
  25.             }
  26.      
  27.             return n;
  28.         }
  29.     })(document)();
  30.      
  31.      
  32.     // 데이터 접근에 지역변수를 활용한 예
  33.     (function(doc){
  34.        
  35.         // closer
  36.         return function createPrivateVariable(){
  37.            
  38.             var n = 0;
  39.             for (var i = 0, len1 = 200000; i < len1; i++){
  40.                 for (var k = 0, len2 = newObject.test_variable_length.test_variable_length_1.test_variable_length_2; k < len2; k++){
  41.                     n++;
  42.                 }
  43.             }
  44.      
  45.             return n;
  46.         }
  47.     })(document)();
  48.  



3. Html Collection 객체는 반드시 캐시 한다.




자주 사용하는 Html Collection 구성원은 아래와 같습니다.

메서드:

  1. document.getElementsByName()
  2. document.getElementsByClassName()
  3. document.getElementsByTagName()


속성:

  1. document.images
  2. document.links
  3. document.forms
  4. document.forms[0].elements
  5. element·child Nodes / elements.length;


이 요소는 모두 Dom의 최신 상태를 실시간으로 질의 하여 그 결과를 반환해주는 속성과
방법입니다.


보통 DOM에 의한 속성 참조는 비 DOM 속성 참조보다 훨씬 비용이 많이 들며, 그중에서도 HTML Collection 객체를 사용할 때 가장 큽니다.


그러므로 모든 컬렉션 객체를 사용할 때에는 반드시 그 결과를 지역변수에 캐시 하여 사용하는 것이 효율적인 방법입니다.

  1.     // 순회시 데이터 접근이 총 2회씩 일어난다.
  2.     // 1. 컬렉션 전체 길이를 알아오기위해 객체에 접근하고 쿼리하여 해당 결과를
  3.     // 반환한다.
  4.      
  5.     // 2. 전체 div 컬렉션 객체에서 주어진 위치(index)의 객체를 가져온다.
  6.     (function(doc){
  7.        
  8.         // closer
  9.         return function getElementsDiv(){
  10.            
  11.             var n = null;
  12.      
  13.             for (var i = 0; i < document.getElementsByTagName('div').length; i++){
  14.                 n += document.getElementsByTagName('div')[i];
  15.             }
  16.      
  17.             return n;
  18.         }
  19.     })(document)();
  20.      
  21.      
  22.     // 순회시 데이터 접근이 총 1회씩 일어난다.
  23.     // 1. 전체 div 컬렉션 객체에서 주어진 위치(index)의 객체를 가져온다.
  24.     (function(doc){
  25.        
  26.         // closer
  27.         return function getElementsDiv(){
  28.            
  29.             var n = null;
  30.      
  31.             var divs = document.getElementsByTagName('div');
  32.             for (var i = 0, length = divs.length; i < length; i++){
  33.                 n += divs[i];
  34.             }
  35.      
  36.             return n;
  37.         }
  38.     })(document)();
  39.  
  40.  

4. if

if 문은 코드 작성 시 가능한 범위 내에서 우선적으로 가장 많이 발생할 만한 조건식부터
작성하는 것이 효율적입니다.


이유는 확률상 조건문 내부 처리시 조건식에서 빠르게 빠져 나갈 수 있기 때문입니다.

또 다른 방법으로는 조건식 내부에 또다른 조건식 분기를 만드는 방법이 있습니다.

이것으로 실행되는 조건식 수를 줄일수 있습니다.



  1. // 확률상 높은 숫자가 많이 나온다고 가정한 예입니다.
  2.  
  3.  
  4. alert((function(doc){
  5.    
  6.     // closer
  7.     return function getTerms(result){
  8.        
  9.         for (var i = 5; i > 0; i--){
  10.             if (i === result){
  11.                 return i;
  12.             }
  13.         }
  14.     }
  15. })(document)(5));

5. switch

switch문은 if 조건식이 일정량 이상 많아질 경우 사용하게 되며, 보통 조건식이
3개이상일 경우 사용하는 것이 가장 효율적입니다.


또한, 자바스크립트에서는 switch문 대체 할 수 있는 객체 리터널이라는
문법또한 존재합니다.


객체 리터널은 보통 조건식이 많을경우 효율적입니다.

즉, 객체를 통한 방법인지라 데이터 접근이 조금은 느려질수는 있겠지만, 각각의
조건식을 실행하는 비용보다는 훨씬 적게 들기 때문입니다.



  1. var n = '';
  2. var term = 1;
  3. switch (term){
  4.    
  5.     case 1:
  6.     alert(term);
  7.     break;
  8. }
  9.  
  10. // 객체 리터널을 활용한 예
  11. alert({
  12.     1: '1'
  13. }[term]);
  14.  
  1.  


조건식 형태에 따른 문법 정리

1. if문: 조건식이 3개 이하일경우

2. swith문 : 조건식이 3 ~ 10개 이하일경우

3. 객체: 조건식이 10개 이상일경우 및 검색 조건식이 메소드가 아닌 값 형태일 경우.



6. for 문

위에서 자주 언급한 바와 같이 배열의 length를 지역변수에 저장하여 효율적인
코드를 작성합니다.



  1.     alert((function(doc){
  2.        
  3.         // closer
  4.         return function getFors(result){
  5.            
  6.             var a = [1, 2, 3, 4, 5];
  7.             for (var i = 0, length = a.length; i <= length; i++){
  8.                 if (i === result){
  9.                     return i;
  10.                 }
  11.             }
  12.         }
  13.     })(document)(5)); // 5
  14.  
  15.  



두 번째 방법으로 보통 for 문은 카운트를 거꾸로 내려가서 0과 비교하는 것이
배열의 length 또는 0이 아닌 값과 비교하는 것보다 빠릅니다.

  1. alert((function(doc){
  2.    
  3.     // closer
  4.     return function getFors(result){
  5.        
  6.         var a = [1, 2, 3, 4, 5];
  7.         for (var i = a.length; i--; ){
  8.             if (a[i] === result){
  9.                 return i;
  10.                 break;
  11.             }
  12.         }
  13.     }
  14. })(document)(5)); // 4



Object 객체의 프로토타입 맴버를 확장시킨후 해당 객체를 탐색한 결과

  1. // 객체 생성
    var Forin ={
  2.     'test1':1,
  3.     'test2':2,
  4.     'test3':3
  5. };
  6.  
  7. Object.prototype.test4 = 4;// Object Property 확장
  8.  
  9. for (var n in Forin){
  10.    
  11.     alert(n); // test4, test1, test2, test3
  12. }
  13.  
  14.  
  15. // 모든 객체는 위에서 말한것처럼 내장생성자를 확장한 test4 속성이
  16. // 실시간으로 모든 객체에 반영되어 있습니다.
  17.  
  18.  
  19. var obj1 = {};
  20. var obj2 = {};
  21.  
  22. for (var n in obj1){
  23.    
  24.     alert(n); // test4
  25. }
  26.  
  27. for (var n in obj2){
  28.    
  29.     alert(n); // test4
  30. }
  31.  
  32. // 확장된 속성을 가리기 위하여 아래와 같이 object.prototype
  33. // 맴버인 hasOwnProperty() 사용하여 필터링한다.
  34.  
  35. for (var n in Forin){
  36.     if (Forin.hasOwnProperty(n)){
  37.         alert(n); // test1, test2, test3
  38.     }
  39. }
  40.  
  41. // call 메소드를 활용하여 Object생성자를 통해 직접 메소드를 호출하게
  42. // 만들어 프로토타입 검색 비용을 즐여주었다.
  43. for (var n in Forin){
  44.     if (Object.hasOwnProperty.call(Forin, n)){
  45.         alert(n); // test1, test2, test3
  46.     }
  47. }
  48.  
  49.  
  50. // call 메소드를 활용하여 Object생성자를 통해 직접 메소드를 호출하게 만들어
  51. // 프로토타입 검색 비용을 줄여주고 또 메소드를 지역변수에 캐쉬하여
  52. // 한번 더 비용을 절감하는 효과를 주었다.
  53.  
  54. var hasOwnPropety = Object.hasOwnProperty;
  55. for (var n in Forin){
  56.     if (hasOwnPropety.call(Forin, n)){
  57.         alert(n); // test1, test2, test3
  58.     }
  59. }

위와 같은 결과가 나오는 이유는 내장 생성자인 Object 객체의 프로토타입 맴버를 사용자가 임의로 확장하면, 생성된 모든 객체에 실시간으로 Object 객체의 프로토타입 맴버가 반영되게 됩니다.

즉, for-in 문을 활용한 객체 순회 시 자신의 맴버가 아니라 할지라도 해당 결과처럼 목록을 노출하는 것입니다.

이 같은 혼돈을 방지하기 위해서라도 내장 생성자는 임의로 확장하지 않는 것을 권장하며, 어쩔 수 없는 경우로 확장할 때에는 순회 시 object.prototype 맴버인 hasOwnProperty() 메서드를 사용하여 자신의 맴버를 필터링 하는 작업을 따로 수행해야 합니다. 




7. 문자열

코드 작성 시 문자열을 다루는 일은 모든 프로그램상에서 가장 많이 일어나는 반복적인
행위이며, 그만큼 성능 최적화에 대해서도 고려를 가장 많이 해야 하는 부분이기도 합니다.


이전 브라우저들은 문자열 연산에 대해 지금과 같이 최적화를 하지 않았습니다.

문자열들을 연결한 결과를 만들기 위해서는 문자열과 문자열 사이에 각각의 문자열을 더한
중간 문자열들을 만들어 메모리에 적재하였고, 또 이렇게 만들어진 중간문자열을
매번 제거함으로써 결국 성능상 좋지 않은 결과를 가져왔습니다.



  1.     var x = 'x' + 'y';
  2.     var m = x; // 중간 문자열
  3.     x = m;
  4.      
  5.     alert(x); //xy
  6.      
  7.      
  8.     // 또 다른 문자열 연결 방법으로는 아래와 같이 배열을 활용하는 방법이 있습니다.
  9.     // 배열의 push() 메소드와 join() 메소드를 활용하여 문자열을 연결하는 방법 입니다.
  10.      
  11.      
  12.     var aStr = [];
  13.     aStr.push('str');
  14.     aStr.push('str');
  15.     alert(aStr.join('')); //strstr
  16.  
  17.  


이 방법은 이전의 + 연산자를 사용한 문자열 연결보다 성능상 효율적입니다.

이유는 이전 + 연산처럼 비효율적으로 중간 문자열을 담는 메모리를 만들고 지우는
동작을 하지 않기 때문입니다.


단, 최신 브라우저에서는 브라우저의 자바스크립트 엔진 최적화로 배열 기법을 사용하는
것보다 + 연산자를 사용하는 것이 성능상 더 효율적입니다.


다시 말해, IE8 이전 버전은 배열기법이 좋으며, IE8 이후 버전에는 + 연산자를 활용한
방법이 성능상 더 좋다는 것입니다.


즉, 현재 서비스 하는 지역과 사용자의 IE 버전 분포도에 따라 문자열 연결에 방법을
선택하는 것이 중요합니다.




8. 오랜 시간 지연되는 스크립트

자바스크립트는 단일 스레드를 기반으로 하는 언어입니다.

하나의 (창(window)), 혹은 하나의 (탭(tab))에서 오로지 한 번에 한 가지 동작씩만
수행할 수 있다는 말입니다.


이 말은 곧 하나의 동작이 오랜 시간 동안 끝나지 않는다면 사이트의 모든 흐름을 멈추게
할 수도 있다는 말과 같습니다.


모든 브라우저는 이와 같은 상황에 아래와 같은 반응을 보여줍니다.


1. IE는 스크립트에 의해 "실행된 문장의 수"를 기반으로 일정량 (기본 최댓값 5백만)보다 더 많이
실행 되었으면 대화상자를 보여준다.

2. FF는 스크립트가 "실행되고 있는 시간"을 기반으로 하여 지정된 시간보다 오랫동안 실행될 때 대화상자를 보여준다.


3. 사파리는 스크립트가 "실행되고 있는 시간"을 기반으로 하여 지정된 시간보다 오랫동안 실행될 때 대화상자를 보여준다.


4. 크롬은 아무런 제약을 두지 않고 가진 메모리를 다 써버리면 프로세스가 죽는다.


5. 오페라는 이에 대하여 어떠한 작업도 하지 않는다.



보통 이와 같은 상황이 나타나는 이유로는 과도한 DOM 작업, 과도한 반복문, 과도한
재귀호출에 대한 문제가 보통입니다.



new Object()는 Factory일까?


자바스크립트에서 객체를 생성하는 방법 중 Object() 생성자 함수를 사용한
new Object() 라는 문법이 있습니다.


"하지만 이 방법은 추천 하지 않는 방법이기도 합니다."



이유를 알아보기 위해 아래 코드를 살펴 보겠습니다.

  1. alert(new Object().constructor === Object); // true
  2. alert(new Object(1).constructor === Number); // true
  3. alert(new Object('string').constructor === String); // true
  4. alert(new Object(true).constructor === Boolean); // true 
 
위의 결과를 보면 Object 생성자 함수의 인자 값에 따라 객체 생성자의 변화를
볼 수 있습니다.


즉, Object 생성자 함수는 전달되는 인자 값에 따라 다른 내장 생성자에 객체 생성을
위임 하여, 인자값에 따라 각기 다른 객체를 생성 합니다.


또한, 그것은 객체 생성시 예기치 못한 결과가 나올 수도 있다는 얘기며, 이는 생각지
못한 객체를 반환 받을 수 도 있다는 것 입니다.



그리고 이와 같은 동작 방식으로 보아 Object 생성자함수는 팩토리패턴을 사용하고 있다는
것 또한 알 수 있습니다.



"팩토리 패턴의 정의는 이와 같습니다."

보통 팩토리 패턴의 목적은 객체들을 생성 하는것에 있으며, 흔히 클래스 내부 또는 클래스의 스태틱 메소드로 구현 합니다.

또한 팩토리 패턴은 사용자가 컴파일 타임에 구체적인 타입(클래스)을 모르고도 객체를 생성할 수 있게 해주는 메소드 입니다.



아래는 팩토리패턴을 구현한 예제코드 입니다.

  1. function factory(id){
  2.    
  3.     if (typeof window[id] === 'function') return new window[id];
  4.     else return null;
  5. }
  6.  
  7. function class1(){
  8. }
  9.  
  10. function class2(){
  11. }
  12.  
  13. function class3(){
  14. }
  15.  
  16. alert(factory('class1'));
  17. alert(factory('class2'));
  18. alert(factory('class3'));
  19. alert(factory('class4'));
 

아래는 팩토리패턴의 정의와 같이 구현한 예제코드 입니다.

  1. function main(){
  2. }
  3.  
  4. main.factory = function(arg){
  5.    
  6.     var cType = null;
  7.    
  8.     if (typeof arg === 'string') cType = main.string;
  9.     else if (typeof arg === 'number') cType = main.number;
  10.     else if (typeof arg === 'boolean') cType = main.boolean;
  11.    
  12.     if (typeof cType !== 'function') return null;
  13.  
  14.     return new cType(arg);
  15. }
  16.  
  17. main.string = function string(){
  18.     return this;
  19. }
  20.  
  21. main.number = function number(){
  22.     return this;
  23. }
  24.  
  25. main.boolean = function boolean(){
  26.     return this;
  27. }
  28.  
  29.  
  30. alert(main.factory('string').constructor); // string()
  31. alert(main.factory(1).constructor); // number()
  32. alert(main.factory(true).constructor); // boolean()

간단히 설명 하자면, 위 코드는 팩토리 패턴의 정의와 같이 클래스의 스태틱 메소드
사용하여 구현 하였으며, 구체적인 타입 클래스를 모르고도 유연성 있게 객체를
생성
할 수 있게 작성된 것 입니다.

자바스크립트 호이스팅(hoisting)이란?



자바스크립트에서는 var 선언문 전에 변수를 사용해도 이미 선언된 것으로 간주한다.


이런 동작 방식을 "호이스팅"(hoisting)이라고 한다.

  1. alert(typeof fn1); // undefined
  2. alert(typeof value1); // undefined
  3.  
  4. var fn1 = function(){ ; };
  5. var value1 = 'value1';
  6.  
  7. alert(typeof fn1); // function
  8. alert(typeof value1); // string

1. 자바스크립트는 실행시 모든 변수가 선언 됩니다. 즉, 결과 에서 처럼 변수를 정의 하기전
결과는 모두 "undefined"를 반환 합니다.


2. 하지만 정의된 후의 결과는 각각 function 과 string을 반환 합니다.

  1. alert(typeof fn1); // function
  2.  
  3. function fn1(){ ; };
  4.  
  5. alert(typeof fn1); // function

1. 하지만 위와 같이 함수 리터널 방식이 아닌 함수 선언문 방식으로 작성한 전역 fn1()
함수 객체는 v
ar선언문 이전의 결과 에서도 function() 을 반환 합니다.


즉, 함수 선언문으로 선언된 변수는 호이스팅 동작에서 정의된 값이 위로 끌어 올려집니다.


아래 코드와 같이 함수 스코프 안에서도 전역과 똑같은 방식으로 호이스팅이 동작합니다.

  1. function fn(){
  2.    
  3.     alert(typeof name); // undefined
  4.     alert(typeof inner_fn); // function
  5.  
  6.     var name = 'name';
  7.  
  8.     function inner_fn(){
  9.     }
  10.  
  11.     alert(typeof name); // string
  12.     alert(typeof inner_fn); // function
  13. }
  14.  
  15. fn();

자바스크립트 call by value or call by reference


모든 OOP 언어에서다루고 있는 부분 이라고 할 수 있는 값(value)참조값(ref value)에 대해 "자바스크립" 에서는
어떻게 다루고 있는지 알아 보도록 하겠습니다.


 var value = '';
    var ref = {};
    
    function callbyvalueReference(value, ref) {

        value += 'value';
        ref.name = 'reference';
    }


    callbyvalueReference(value, ref);

    alert(value); // value
    alert(ref.name); // reference


1. value 변수와 ref 변수에 각 각 "값" 과 "객체"를 선언 및 정의 합니다.
 

2. 이렇게 정의된 2개의 변수를  fn(value, ref);  호출 합니다.

3. 값의 복사본인 함수 매개변수 value는 해당 함수의 지역 변수로 선언되고 정의 됩니다.
 

"즉, 전역변수로 선언 및 정의된 value의 문자열과는 "+=" 연산자를 통해 합쳐 지지 않는
다는 것을 말합니다."


4. 하지만 객체를 정의한 ref 전역 변수는 함수의 매개변수로 "값" 과 "복사복" 이 아닌 값에 의한 참조지역변수로
전달 되었기 때문에 내부에서 언제든 참조값 수정이 가능하게 되었습니다.



P.S 문자열 복사와 전달

위의 설명에서 자바스크립트에서 객체는 참조만 전달된다고 하였습니다.

그렇다면 문자열은 기본타입인 값(call by value)에 의한 참조이며, 매개변수 전달 시 값에 복사본이 전달된다는 말이 됩니다.

그러나 문자열의 길이는 임의적이기때문에 문자열을 바이트 단위로 복사하거나 전달, 비교하는 일은 매우 비효율적인 이기때문에 문자열은 참조 타입 형태로 구현되었다고 가정하는 것이 더 적절합니다.

위의 내용처럼 문자열이 값에 대한 참조인지 알 수 있는 방법은 없으나, 문자열 비교를 통해 값에 의해 비교되는지 참조로 비교되는지 알 수는 있습니다.

  1. // 만약 두 문자열이 값에 의해 비교되면 두 문자열은 동일할 것이고 참조에 의해 비교되면 동일하지 않을 것이다.
  2.  
  3. var s1 = 'hello';
  4. var s2 = 'hell' + 'o';
  5.  
  6. alert(s1 === s2); //true
  7.  
  8. var obj1 = {
  9.     str: 'hello'
  10. }
  11.  
  12. var obj2 = {
  13.     str: 'hell' + 'o'
  14. }
  15.  
  16. alert(obj1.str === obj2.str); //true


아래는 간단한 객체 상속 관계에서 참조값을 다루는 코드예제 입니다.

  1. function fn(){
  2. }
  3.  
  4. fn.prototype = {'arr': []};
  5.  
  6. new fn().arr.push(1);
  7.  
  8. alert(new fn().arr); // 1


1. fn() 함수 객체가 생성 됩니다.

2. fn() 함수 객체의 프로토타입 맴버에 객체({'arr': []})를 추가 합니다.

3. fn() 객체의 프로토타입 맴버에 추가된 객체의 arr 배열 속성에 1을
push() 함수로 1을 추가 합니다. 


4 . 
alert(new fn().arr); // 1  에서 "1" 이 반환되는 이유는? 

"자바스크립트" 에서 객체는 참조만 전달되기 때문이다. 즉, 자식( new fn() ) 객체 에서 속성 ({'arr': []})  값을 수정하면 부모 객체 속성 ({'arr': []}또한 수정되어 버립니다.

 


prev 1 ··· 7 8 9 10 11 12 next