본문으로 건너뛰기

JS 학습노트 12_ 최적화

무료2015-04-14#JS#js性能优化

본고에서는 유지보수성 최적화, 성능 최적화, 릴리스 최적화를 포함한다

一.유지보수성 최적화

1.주석 추가

주석은 코드의 가독성과 유지보수성을 향상시킬 수 있습니다. 물론, 이상적인 상황은 주석으로 가득 차는 것이지만, 이는 현실적이지 않습니다. 따라서 중요한 곳에만 주석을 추가하면 됩니다:

  • 함수와 메서드: 특히 반환값. 직접 보면 알 수 없기 때문입니다

  • 큰 코드 블록 (기능 모듈): 모듈 기능 설명

  • 복잡한 알고리즘: 핵심 포인트를 기술하여 이해를 용이하게 함

  • Hack: 어떤 문제를 수정하기 위한 것인지, 현재 방안은 완성되었는지, 개선 가능한지

2.변수 타입 "암시"

초기값을 통해 암시합니다. 예를 들어:

var found = false;
var count = 1;
var name = '';
var student = null;

3.디커플링 (계층화)

  • 구조층:HTML

  • 행동층:JS

  • 표현층:CSS

가능한 한 "월권"하지 마십시오. 정말로 월권하고 싶더라도, 문서 또는 주석으로 설명해야 합니다. 가능한 한 긴밀한 결합을 피하십시오. 예를 들어:

  • JS-HTML:

      elem.innerHTML = '<div class="block"><h2>title</h2><p>content</p></div>';
      //最好用下面的方式代替(显示页面中的隐藏元素)
      //elem.style.display = 'block';
    
  • JS-CSS:

      elem.style.color = '#e0e0e0';
      //最好用下面的方式代替(设置类,不要直接设置样式)
      //elem.className = 'my_class';
    
  • JS-JS:

      //逻辑耦合
      elem.onclick = function(e){
        if(txt.value !== null && txt.value !== '' &&...){
          //DOM 操作
        }
      }
      //最好用下面的方式代替(分离逻辑功能块)
      function isValid(){
        return txt.value !== null && txt.value !== '' &&...;
      }
      function display(){
        //DOM 操作
      }
      elem.onclick = function(e){
        if(isValid()){
          display();
        }
      }
    

    논리 결합을 피하는 몇 가지 원칙:

    • event 객체를 전달하지 않고, 필요한 데이터만 전달

    • 이벤트 트리거는 액션을 실행하는 유일한 방법이어서는 안 됨

    • 이벤트 핸들러는 이벤트 관련 데이터 (이벤트 소스, 좌표값 등) 만 처리한 후, 애플리케이션 로직에 인계

4.코딩 원칙

  • 객체 소유권을 존중하고, 남의 객체를 함부로 수정하지 마십시오. 구체적인 요구:

    • 인스턴스/프로토타입에 속성이나 메서드를 추가하지 않음

    • 기존 메서드를 재작성하지 않음

    대체 방안:

    • 합성: 필요한 기능을 구현한 새 객체를 생성하고, 필요한 객체를 참조

    • 상속: 커스텀 타입을 생성하고, 수정이 필요한 타입을 상속한 후 추가 기능 추가

  • 네임스페이스를 사용하여 전역 변수를 피함. 예를 들어:

      var mySystem = {};
      mySystem.mod1 = {...};
    
  • 상수를 사용하여 유지보수성 향상. 예를 들어:

      var Consts = {};
      Consts.IMG_PATH = '../imgs/';
      
    

    주의:상수에는 일반적인 CSS 클래스명과 기타 유지보수에 영향을 줄 수 있는 값 포함

二.성능 최적화

1.긴 스코프 체인 검색 피하기

여러 번 참조해야 하는 전역 변수를 로컬 변수로 저장

2.with 문은 가능한 한 사용하지 않기

with 문은 스코프를 연장하며, 긴 스코프 검색의 오버헤드가 존재합니다. 로컬 변수로 저장하여 대체할 수 있습니다 (with 만큼 편리하지는 않지만, 조금은 낫습니다)

3.불필요한 속성 검색 피하기

  1. 반복적으로 사용하는 값을 로컬 변수로 저장. 예를 들어:

     //不要用下面的代码(6 次属性查找)
     var qs = window.location.href.substring(window.location.href.indexOf('?'));
     //应该用下面的代码(4 次,而且更具可读性)
     var url = window.location.href;
     var qs = url.substring(url.indexOf('?'));
    
  2. 루프 최적화

    • 감산 반복이 더 빠름 (i--)

    • 종료 조건 단순화. 매번 루프 시 종료 조건을 체크하므로, 조건을 단순화하면 효율 향상

    • 루프 본체 단순화. 루프 내부의 계산량을 가능한 한 줄임

    • 후 테스트 루프 (do...while) 사용. 첫 번째 루프 전의 판단을 회피할 수 있음

  3. 루프 전개

루프 횟수가 확정된 경우, 루프를 사용하지 않는 것이 좋음. 루프에는 루프 생성과 종료 조건 처리의 추가 오버헤드가 존재하기 때문. 예를 들어:

    for(i = 0;i < 2;i++){
      process(arr[i]);
    }
    //下面的比上面的快
    process(arr[0]);
    process(arr[1]);
    process(arr[2]);
    

루프 횟수가 확정되지 않은 경우, Duff 테크닉 (Tom Duff 가 발명) 을 사용하여 일부 루프를 전개하고 효율을 향상시킬 수 있음. 예를 들어:

    //credit: Jeff Greenberg for JS implementation of Duff's Device
    //假设 values.length > 0
    var iterations = Math.ceil(values.length / 8);
    var startAt = values.length % 8;
    var i = 0;
    do {
        switch(startAt){
            case 0: process(values[i++]);
            case 7: process(values[i++]);
            case 6: process(values[i++]);
            case 5: process(values[i++]);
            case 4: process(values[i++]);
            case 3: process(values[i++]);
            case 2: process(values[i++]);
            case 1: process(values[i++]);
        }
        startAt = 0;
    } while (--iterations > 0);
    //以上代码来自:http://www.cnblogs.com/kylindai/archive/2013/12/04/3458476.html

또는 또 다른 더 빠른 Duff 방법:

    //credit: Speed Up Your Site (New Riders, 2003)
    var iterations = Math.floor(values.length / 8);
    var leftover = values.length % 8;
    var i = 0;
    if (leftover > 0){
        do {
            process(values[i++]);
        } while (--leftover > 0);
    }
    do {
        process(values[i++]);
        process(values[i++]);
        process(values[i++]);
        process(values[i++]);
        process(values[i++]);
        process(values[i++]);
        process(values[i++]);
        process(values[i++]);
    } while (--iterations > 0);
    //以上代码来自:http://www.cnblogs.com/kylindai/archive/2013/12/04/3458476.html

4. 이중 해석 피하기

이중 해석이란:js 로 js 를 해석하는 것. 구체적으로는 eval() 함수 또는 new Function(strCode) 또는 setTimeout(strCode) 사용

이중 해석의 단점:해석을 위해 또 다른 파서를 시작해야 하며, 큰 오버헤드가 존재하고, 직접 해석보다 훨씬 느림

  1. 네이티브 메서드가 더 빠름

네이티브 메서드는 C/C++ 로 컴파일된 모듈이므로, 훨씬 빠름

  1. switch 가 if-else 보다 빠름

이유는 말하기 어렵지만, CSDN 참조 가능

  1. 비트 연산은더 빠르지 않음

다른 언어에서는 수학 연산을 비트 연산으로 단순화하면 계산 속도를 향상시킬 수 있지만, js 에서는 그렇지 않음. js 내부에는 1 종의 수치 타입 (double) 만 있으므로, 비트 연산에는 변환이 필요:double - int - 비트 연산 - double. 성능은 상상할 수 있음

  1. 문장 수 줄이기

    • 1 개의 var 로 여러 변수 선언

    • 반복 값 삽입.arr[i++] 로 한 문장으로 처리.i++ 를 독립된 문장으로 하지 않음

    • 배열/객체 리터럴 사용.코드 행 수가 명확히 줄어듦

  2. DOM 최적화

    • 현장 업데이트 감소.DocumentFragment 를 사용하여 최적화 가능

    • innerHTML 로 DOM 노드 생성이 한 무더기 노드를 생성하여 조립하는 것보다 빠르지만, JS-HTML 결합 문제가 존재함. 신중하게 고려

    • 이벤트 위임 사용

    • 실시간 업데이트 컬렉션 주의 (NodeList, NamedNodeMap, HTMLCollection)

      참조를 로컬 변수로 저장하는 외에, 해당 속성에 액세스하는 것도 재쿼리가 필요하다는 점에 주의.예를 들어:var imgs = document.images; imgs.length 에 액세스하는 것도 다시 쿼리 필요

三.릴리스 최적화

  1. 검증 도구 (예:JSLint) 를 사용하여 코드 검사, 구문 오류 외의 잠재적 문제 발견

  2. 주석 제거 (보안상 고려)

  3. js 파일 병합, 외부 파일 수 최소화

  4. 코드 압축 (예:YUI 압축기).코드 자체 크기 감소, 동시에 코드 난독화로 보안성 향상 가능. 하지만 코드 난독화 자체에 위험이 존재하며, 오류를 유발할 수 있음

  5. 서버 압축 기능 활성화.예를 들어 gzip 모듈

댓글

아직 댓글이 없습니다

댓글 작성