Javascript Patterns - 기본

2020. 3. 2. 15:12웹/Javascript Pattern

Javascript  Patterns - 기본

 

1. 유지보수 가능한 코드 작성

  • 읽기 쉽다

  • 일관적이다

  • 예측 가능하다

  • 한 사람이 작성한 것처럼 보인다.

  • 문서화되어 있다.

 

 

2. 전역 변수 최소화

  • 전역변수의 문제점

    • 자바스크립트 애플리케이션이나 웹페이지 내 모든 코드 사이에 공유된다는 점.

    • 모든 전역 변수는 동일한 전역 네임스페이스 안에 존재하기 때문에, 애플리케이션 내의 다른 영역에서 목적이 다른 전역 변수를 동일한 이름으로 정의할 경우 서로 덮어쓰게 된다.

/*
 * result는 선언되지 않은 상태로 사용.
 * 이 코드는 잘 동작하지만, 이 함수를 호출하고 나면 전역 네임스페이스에 result라는 변수가 남아 문제를 유발
 */

function sum(x, y) {
    result = x + y;
    return result;
}

 

  • 변수를 선언할 때 항상 var 명시

    • 자바스크립트에서 변수를 선언하지 않고도 사용

    • 암묵적 전역이라는 개념이 존재

/*
 * 안티패턴, 사용하지 말것
 * a 는 지역 변수, b는 전역변수가 된다.
 */
function foo() {
    var a = b = 0;
}

 

※ 암묵적 전역 변수를 피해야하는 이유

이식성(portability) : 코드를 다른 실행 환경(호스트)에서 실행할 경우, 원래의 실행 환경에서는 존재하지 않았던 암묵적 전역 변수가 새로운 실행 환경의 호스트 객체를 의도치 않게 덮어쓸 수 있기 때문.

 

  • var 선언을 빼먹었을 때의 부작용

    • 암묵적 전역 변수와 명시적으로 선언된 변수 사이에 존재하는 차이점은 delete 연산자를 사용하여 변수의 정의를 취소할 수 있는지 여부

    • var를 명시적으로 선언된 전역 변수는 삭제 X

    • var를 사용하지 않고 생성한 암묵적 전역 변수는 삭제 O

    • 암묵적 전역 변수가 엄밀히 말하면 변수가 아니라 전역 객체의 프로퍼티라는 사실

// 세개의 전역 변수 정의
var global_var = 1;
global_novar = 2;

(function () {
    global_fromfunc = 3;
});


// 삭제
delete global_var;        // false
delete global_novar;      // true
delete global_fromfunc;   // true


// 삭제 되었는지 확인
typeof global_var;        // number
typeof global_novar;      // undefined
typeof global_fromfunc;   // undefined

 

  • 전역 객체에 대한 접근

    • 브라우저에서는 window 속성을 통해 전역 객체에 접근, 그러나 다른 환경에서는 존재하지 않을 수 있다.

// window 라는 식별자를 직접 사용하지 않고, 전역객체에 접근
var global = (function() {
    return this;
});

 

  • 단일 var 패턴

    • 함수에서 필요로 하는 모든 지역 변수를 한군데서 찾을 수 있다.

    • 변수를 선언하기 전에 사용할 때 발생하는 로직상의 오류를 막아준다.(hosting으로 인한 오류 유발)

    • 변수를 먼저 선언한 후에 사용해야 한다는 사실을 상기시키기 때문에 전역 변수를 최소화하는데 도움이 된다.

    • 코드량이 줄어든다.

function func() {
    var a = 1,
        b = 2,
        sum = a + b,
        myobject = {},
        ...
}

 

  • 호이스팅(hosting) : 분산된 var 선언의 문제점

    • 함수 내에 여기저기서 여러 개의 var 선언을 사용할 수 있지만, 실제로는 모두 함수 상단에서 변수가 선언된 것과 동일하게 동작 => 호이스팅

    • 호이스팅으로 인해 함수 안에서 변수를 사용한 다음에 선언하면 로직상의 오류를 일으킬 수 있다.

// 안티 패턴
myname = "global";          // 전역변수 

function func() {
    alert(myname);          // undefined
    var myname = "local";   // 지역변수
    alert(myname);          // local
}
func();


// 위의 코드는 아래와 같이 동작
myname = "global";          // 전역변수
function func() {
    var myname;             
    alert(myname);          // undefined
    myname = "local";       
    alert(myname);          // local
}
func();

 

 

3. for 루프

  • HTMLCollection의 length를 캐시해야한다.

// 최적화되지 않은 루프
for(var i=0; i<array.length; i++ {
    ..
}

 

이 패턴의 문제점은 루프 순회시 마다 array.length(배열의 length)에 접근한다는 점

array가 배열이 아니라 HTMLCollection 이라면 이것으로 인해 코드가 느려질수 있다.

HTMLCollection은 length에 접근할 때마다 실제 DOM에 질의를 요청하는 것과 같으며, DOM 접근은 일반적으로 비용이 크다.

 

※ DOM 메서드에서 반환되는 HTMLCollection 객체

      - document.getElementByName()

      - document.getElementByClassName()

      - document.getElementByTagName()

      - 기타 등등..

for(var i=0, max=array.length; i<max; i++){
    ...
}

따라서 for 루프를 좀더 최적화하기 위해서는 배열의 length를 캐시해야한다.

 

  • 미세 최적화

    • 변수를 하나 덜 쓴다.(max 변수 사용 X)

    • 카운트를 거꾸로 하여 0으로 내려간다. 0과 비교하는 것이 배열의 length 또는 0이 아닌 값과 비교하는 것보다 대개 더 빠르기 때문.

    • ++, -- 은 JSLint에서 권장 하지 않는다.( i = i+1, i += 1 로 대체)     

방법 1)
var i, array = [];
for(i=array.length; i--;){
    ..
}


방법 2)
var array = [], i=array.length;
while(i--){
    ...
}

 

 

4. For-in 루프

  • 배열이 아닌 객체를 순환할때만 사용.

          ※ 배열도 객체이기 때문에 기술적으로 배열을 순회할수 있지만, 권장사항은 아니다.

  • hasOwnProperty

    • 프로토타입 체인의 변경사항은 실시간으로 반영되기 때문에, 자동적으로 모든 객체가 이 새로운 메서드를 사용할 수 있다.

    • 프로토타입의 프로퍼티를 스킵하기 위해서는 hasOwnProperty 함수 사용

 

var man = {
    hand: 2,
    legs: 2,
    heads: 1
};

for(var i in man){
    if(man.hasOwnProperty(i)){
        console.log(i, " : ", man[i]);
    }
}


for(var i in man){
    console.log(i, " : ", man[i]);
}


console.log("============== clone 추가 ================");
if(typeof Object.prototype.clone === "undefined"){
    Object.prototype.clone = function() {};
}

for(var i in man){
    if(man.hasOwnProperty(i)){
        console.log(i, " : ", man[i]);
    }
}

for(var i in man){
    console.log(i, " : ", man[i]);
}


-- 출력 결과 --
Hand : 2
Leg : 2
Heads : 1

Hand : 2
Leg : 2
Heads : 1



============== clone 추가 ================
Hand : 2
Leg : 2
Heads : 1

Hand : 2
Leg : 2
Heads : 1

Clone : f()

 

 

5. 내장 생성자 프로토타입 확장

Object, Array, Function과 같은 내장 생성자의 프로토타입을 확장하는 것은 코드의 지속성을 심각하게 저해시킬수 있다.

따라서, 내장 생성자 프로토타입을 확장하지 않는 것이 최선이다.

 

예외가 허용되는 조건(3가지 조건 모두 만족 시)

  • 해당 기능이 ECMAScript의 향후 버전이나 자바스크립트 구현에서 일관되게 내장 메서드로 구현될 예정이다.

    예를 들어 ECMAScript 5에 기술되었으나 아직 브라우저에 내장되지 않은 메서드라면 추가할 수 있다.

    이 경우에는 유용한 메서드를 미리 정의하는 것이라고 할 수 있다. 

  • 이 프로퍼티 또는 메서드가 이미 존재하는지, 이미 코드 어딘가에 구현되어 있거나, 지원 브라우저 중 일부 자바스크립트 엔진에 내장되어 있는지 확인

  • 이 변경사항을 문서화하고 팀 내에 공유

`

 

 

출처 : JavaScript Patterns