우선, 다른 언어와 달리 JS의 효율성은 JS 엔진의 효율성에 크게 좌우됩니다. 엔진 구현의 장단점 외에도 엔진 자체는 일부 특수 코드 패턴에 대한 몇 가지 최적화 전략을 채택합니다. 예를 들어 FF, Opera 및 Safari의 JS 엔진은 문자열 연결 작업(+)을 특별히 최적화했습니다. 당연히, 최대 효율을 달성하기 위해서는 엔진의 기질을 이해하고 엔진의 취향에 부응하도록 노력해야 합니다. 따라서 서로 다른 엔진의 경우 최적화가 서로 상충될 가능성이 가장 높습니다.
그리고 크로스 브라우저 웹 프로그래밍을 한다면 가장 큰 문제는 IE6(JScript 5.6)입니다! hotfix가 없으면 JScript 엔진의 가비지 수집 버그로 인해 실제 응용 프로그램의 성능이 다른 브라우저와 동일하지 않게 됩니다. 따라서 이 상황에서 최적화하는 것은 실제로 JScript에 최적화하는 것입니다!
그래서 첫 번째 원칙은 IE6(패치되지 않은 JScript 5.6 이하)에만 최적화하는 것입니다!
프로그램이 IE6에서 허용 가능한 성능으로 최적화된 경우 기본적으로 다른 브라우저에서의 성능에는 문제가 없습니다.
따라서 아래에서 설명하는 많은 문제는 다른 엔진에서 완전히 다를 수 있다는 점에 유의하세요. 예를 들어 루프의 문자열 연결은 일반적으로 Array.join을 사용해야 하는 것으로 간주되지만 문자열로 인해. SpiderMonkey와 같은 엔진의 "" +" 작업이 최적화되었으며 결과적으로 Array.join을 사용하는 것은 "+"를 직접 사용하는 것만큼 효율적이지 않습니다! 그러나 IE6을 고려한다면 다른 브라우저에서의 이러한 효율성 차이는 전혀 언급할 가치가 없습니다.
JS 최적화는 여전히 다른 언어의 최적화와 동일합니다. 예를 들어, 최적화를 시작하자마자 서두르지 마세요. 그건 의미가 없습니다. 최적화의 핵심은 여전히 가장 중요한 부분인 병목 현상에 집중하는 것입니다. 일반적으로 병목 현상은 대규모 루프에서 항상 나타납니다. 이는 루프 자체에 성능 문제가 있다는 의미는 아니지만 루프가 가능한 성능 문제를 빠르게 증폭시킬 수 있다는 의미입니다.
그래서 두 번째 원칙은 대규모 루프를 주요 최적화 개체로 삼는 것입니다.
다음 최적화 원칙은 대규모 루프에서만 의미가 있습니다. 이러한 최적화는 루프 본문 외부에서 수행하는 것이 기본적으로 의미가 없습니다.
현재 대부분의 JS 엔진은 해석되어 실행되며, 해석된 실행의 경우 모든 작업에서 함수 호출의 효율성이 낮습니다. 또한 지나치게 깊은 프로토타입 상속 체인이나 다중 레벨 참조도 효율성을 감소시킵니다. JScript에서 수준 10 참조의 오버헤드는 빈 함수 호출의 오버헤드의 약 1/2입니다. 두 가지 모두의 오버헤드는 간단한 연산(예: 4개의 산술 연산)보다 훨씬 큽니다.
세 번째 원칙은 너무 많은 참조 수준과 불필요한 다중 메서드 호출을 피하는 것입니다.
어떤 경우에는 속성 액세스로 보이는 것이 실제로는 메서드 호출이라는 점에 유의하는 것이 중요합니다. 예를 들어 모든 DOM 속성은 실제로 메서드입니다. NodeList를 순회할 때 node.length에 대한 루프 조건의 액세스는 속성 읽기처럼 보이지만 실제로는 함수 호출과 동일합니다. 또한 IE DOM 구현에서는 매번 내부 순회를 통해 childNodes.length를 다시 계산해야 합니다. (맙소사, 이게 사실이에요! childNodes.length의 액세스 시간이 childNodes.length의 값에 비례한다는 것을 측정했기 때문입니다!) 이것은 매우 비쌉니다. 따라서 node.length를 미리 js 변수에 저장하면 순회 성능이 확실히 향상될 수 있습니다.
함수 호출이기도 하며 사용자 정의 함수의 효율성은 언어 내장 함수보다 훨씬 낮습니다. 왜냐하면 후자는 엔진의 기본 메서드에 대한 래퍼이고 엔진은 일반적으로 다음 언어로 작성되기 때문입니다. c, C++, 자바. 또한 동일한 함수의 경우 내장 언어 구성의 비용은 일반적으로 내장 함수 호출보다 더 효율적입니다. 왜냐하면 전자는 JS 코드의 구문 분석 단계에서 결정되고 최적화될 수 있기 때문입니다.
그래서 네 번째 원칙은 언어 자체의 구조와 내장 기능을 사용하려고 노력하는 것입니다.
다음은 고성능 String.format 방법의 예입니다. String.format의 전통적인 구현은 String.replace(regex, func)를 사용하는 것입니다. 패턴에 n개의 자리 표시자(반복되는 항목 포함)가 포함되어 있으면 사용자 정의 함수 func가 n번 호출됩니다. 이 고성능 구현에서 각 형식 호출은 Array.join만 수행한 다음 String.replace(regex, string) 작업을 수행합니다. 둘 다 사용자 정의 함수 호출 없이 엔진의 내장 메서드입니다. 2개의 내장 메소드 호출과 n개의 커스텀 메소드 호출, 이것이 바로 성능의 차이입니다.
기본 제공되는 기능이기도 하지만 여전히 성능에는 차이가 있습니다. 예를 들어 JScript에서 인수의 액세스 성능은 매우 좋지 않아 함수 호출을 거의 따라잡을 수 있습니다. 따라서 가변 매개변수를 사용하는 단순한 함수가 성능 병목 현상이 발생하는 경우 내부적으로 일부 변경을 하고 인수에 액세스하지 않고 매개변수에 대한 명시적인 판단을 통해 처리할 수 있습니다.
예:
Java 코드
function sum() { var r = 0; for (var i = 0; i < arguments.length; i++) { r += arguments[i]; } return r; }
이 합계는 일반적으로 더 적은 숫자로 호출되며 매개 변수가 적을 때 성능이 향상되기를 바랍니다. 다음으로 변경된 경우:
Java 코드
function sum() { switch (arguments.length) { case 1: return arguments[0]; case 2: return arguments[0] + arguments[1]; case 3: return arguments[0] + arguments[1] + arguments[2]; case 4: return arguments[0] + arguments[1] + arguments[2] + arguments[3]; default: var r = 0; for (var i = 0; i < arguments.length; i++) { r += arguments[i]; } return r; } }
사실 크게 개선되지는 않겠지만, 다음으로 변경된 경우:
Java代码
function sum(a, b, c, d, e, f, g) { var r = a ? b ? c ? d ? e ? f ? a + b + c + d + e + f : a + b + c + d + e : a + b + c + d : a + b + c : a + b : a : 0; if (g === undefined) return r; for (var i = 6; i < arguments.length; i++) { r += arguments[i]; } return r; }
就会提高很多(至少快1倍)。
最后是第五原则,也往往是真实应用中最重要的性能障碍,那就是尽量减少不必要的对象创建。
本身创建对象是有一定的代价的,但是这个代价其实并不大。最根本的问题是由于JScript愚蠢之极的垃圾回收调度算法,导致随着对象个数的增加,性能严重下降(据微软的人自己说复杂度是O(n^2))。
比如我们常见的字符串拼接问题,经过我的测试验证,单纯的多次创建字符串对象其实根本不是性能差的原因。要命的是在对象创建期间的无谓的垃圾回收的开销。而Array.join的方式,不会创建中间字符串对象,因此就减少了那该死的垃圾回收的开销。
因此,如果我们能把大规模对象创建转化为单一语句,则其性能会得到极大的提高!例如通过构造代码然后eval——实际上PIES项目中正在根据这个想法来做一个专门的大规模对象产生器……
好了上面就是总结的JS优化五大原则。
위 내용은 다섯 가지 주요 JavaScript 최적화 원칙을 요약합니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!