Javascript là một ngôn ngữ kịch bản mà ở đó ta có thể viết các đoạn mã một cách khá tự do. Cùng phục vụ cho một mục đích nhưng chúng ta có rất nhiều cách khác nhau để viết trên Javascript. Bài viết này sẽ tổng hợp một số trường hợp cần lưu ý khi viết các đoạn mã Javascript và cho thấy sự ảnh hưởng của chúng như thế nào về tốc độ thực hiện các đoạn mã trên bốn trình duyệt phổ biến là Firefox, Safari, Internet Explorer (IE) và Chrome.
#1: Tránh việc gọi hàm quá nhiều
Ta có hai hàm là methodCall()
và inlinedMethod()
cùng thực hiện việc tính tổng các số tự nhiên từ 1 đến 10000 như sau:
function methodCall(){
function square(n){return n*n};
var i = 10000, sum = 0;
while(i--) sum += square(i);
}
function inlinedMethod(){
var i = 10000, sum = 0;
while(i--) sum += i*i;
}
Việc gọi hàm square()
nhiều lần khiến cho thời gian thực hiện hàm methodCall()
mất nhiều thời gian hơn khi thực hiện hàm inlinedMethod()
. Điều này thể hiện qua bảng thống kê dưới đây
methodCall() |
inlinedMethod() |
|
---|---|---|
![]() |
0.410s | 0.150s |
![]() |
0.056s | 0.045s |
![]() |
N/A | 0.128s |
![]() |
0.027s | 0.016s |
Đối với IE8, việc gọi hàm quá nhiều lần như trong methodCall()
sẽ là nguyên nhân của việc trình duyệt bật lên cảnh báo về sự trì trệ trong tốc độ thực thi và bị đơ.
#2: Khi khai báo một mảng, đối tượng
Khi khai báo một mảng, hoặc đối tượng trong javascript, ta có thể dùng một trong hai cách sau
function classic(){
var a = new Array, o = new Object;
}
function literals(){
var a = [], o = {};
}
Việc rườm rà trong sử dụng ngôn ngữ trong hàm classic()
sẽ làm tăng thời gian thực thi so với hàm literals()
, cụ thể như sau
classic() |
literals() |
|
---|---|---|
![]() |
0.291s | 0.265s |
![]() |
0.020s | 0.016s |
![]() |
0.220s | 0.185s |
![]() |
0.024s | 0.010s |
#3: Khi sử dụng vòng lặp
Hai đoạn code dưới đây đều thực hiện 60 lần lặp trong việc tăng giá trị của một biến lên một đơn vị
function normalLoop(){
var i = 60, j = 0;
while(i--) j++;
}
function unrolledLoop(){
var j = 0;
j++; j++; j++; j++; j++; j++;
j++; j++; j++; j++; j++; j++;
j++; j++; j++; j++; j++; j++;
j++; j++; j++; j++; j++; j++;
j++; j++; j++; j++; j++; j++;
j++; j++; j++; j++; j++; j++;
j++; j++; j++; j++; j++; j++;
j++; j++; j++; j++; j++; j++;
j++; j++; j++; j++; j++; j++;
j++; j++; j++; j++; j++; j++;
}
Thực ra thì cách viết như trong hàm unrolledLoop()
ít khi sử dụng trong thực tế, tuy nhiên cái mà ví dụ muốn chỉ ra ở đây là việc sử dụng câu lệnh lặp sẽ làm tăng thêm một khoảng thời gian. Điều này được thể hiện cụ thể như sau:
normalLoop() |
unrolledLoop() |
|
---|---|---|
![]() |
0.023s | 0.010s |
![]() |
0.003s | 0.001s |
![]() |
0.032s | 0.015s |
![]() |
0.005s | 0.001s |
#4: Sử dụng cơ chế cache
Cho hai đoạn mã như dưới đây đều sử dụng đối tượng window
trong một vòng lặp 10000 lần. Tuy nhiên, một đoạn mã thì ở đó đối tượng window
sẽ được cache vào một biến, và trong vòng lặp sẽ gọi ra đối tượng window
thông qua biến đó. Đoạn mã còn lại sẽ trực tiếp gọi đến đối tượng window
trong vòng lặp.
function cached(){
var w = window; i = 10000;
while(i--) w.test = 'test';
}
function uncached(){
var i = 10000;
while(i--) window.test = 'test';
}
Việc gọi trực tiếp quá nhiều lần một đối tượng (window
, document
…) sẽ làm tăng thời gian thực thi, thể hiện ở bảng dưới đây:
uncached() |
cached() |
|
---|---|---|
![]() |
1.440s | 0.825s |
![]() |
0.07s | 0.07s |
![]() |
2.22s | 2.19s |
![]() |
0.48s | 0.16s |
Trong trường hợp trên ta có thể thấy Safari thực thi nhanh vượt trội so với các trình duyệt khác. Ngược lại, IE lại quá là ì ạch.
#5: Sử dụng biểu thức quan hệ
Hai đoạn mã dưới đây là tương tự nhau chỉ khác nhau về thứ tự các toán tử được sử dụng trong biểu thức quan hệ
var b = false; n = 99;
function notTuned(){
return n*n && b;
}
var b = false; n = 99;
function tuned(){
return b && n*n;
}
Tuy chỉ là đổi chỗ vị trí các toán tử cho nhau, nhưng điều này lại có ảnh hưởng đến tốc độ thực thi. Trong ví dụ trên, n*n
tham gia vào quan hệ &&
luôn trả về giá trị là số nguyên khác 0 và javascript sẽ hiểu đó là true
, nếu n*n
được đặt trước thì biểu thức quan hệ sẽ phải xét tiếp giá trị của b
trước khi đưa ra giá trị cuối cùng của biểu thức. Nhưng ngược lại, nếu b
được đặt trước, b
luôn trả về là false
như vậy biểu thức chắc chắn trả về giá trị false, do đó n*n
không cần phải được xét đến nữa và chúng ta sẽ tiết kiệm được khoảng thời gian dùng để thực hiện tính toán giá trị của n*n
.
Cụ thể sẽ giống như ở bảng dưới đây
notTuned() |
tuned() |
|
---|---|---|
![]() |
0.005s | 0.004s |
![]() |
0.011s | 0.010s |
![]() |
0.906s | 0.391s |
![]() |
0.037s | 0.021s |
#6: Thao tác với đối tượng
Khi thao tác với một đối tượng trong javascript, ta có thể sử dụng từ khóa with
hoặc toán tử “.
” như sau
function useWith(){
var obj = { prop: 'test', str: ''};
with(obj){
var i = 10000;
while(i--) str += prop;
return str;
}
}
function useDot(){
var obj = { prop: 'test', str: ''}, i = 10000;
while(i--) obj.str += obj.prop;
return obj.str;
}
Theo thống kê, việc sử dụng từ khóa with
tốn nhiều thời gian hơn việc sử dụng toán tử “.
” như bảng dưới đây
useWith() |
useDot() |
|
---|---|---|
![]() |
0.071s | 0.012s |
![]() |
0.039s | 0.028s |
![]() |
0.078s | 0.078s |
![]() |
0.077s | 0.006s |
#7: Sử dụng try/catch
var a = 0;
function useTryCatch(){
try{
a += 1;
}catch(e){}
}
var a = 0;
function notUseTryCatch(){
a += 1;
}
Việc try/catch
sẽ làm tăng thực thi các đoạn mã script
useTryCatch() |
notUseTryCatch() |
|
---|---|---|
![]() |
0.006s | 0.005s |
![]() |
0.287s | 0.011s |
![]() |
0.460s | 0.460s |
![]() |
0.123s | 0.012s |
Tuy nhiên thực tế thì có những trường hợp chúng ta bắt buộc phải dùng đến try/catch
nếu không chương trình sẽ bị dừng ngay ở đoạn mã lỗi. Vì vậy tùy vào từng trường hợp cụ thể mà chúng ta hãy cân nhắc có thực sự cần thiết phải dùng đến try/catch
hay không.
Tốc độ thực thi một đoạn mã Javascript thực sự phụ thuộc vào nhiều yếu tố, nó có thể là do logic khi ta code, do việc ta sử dụng ngôn ngữ (API), hay do chính bản thân các trình duyệt… vì vậy để cải thiện tốc độ một đoạn mã không chỉ đơn giản là làm theo những gì nói trong bài viết này. Bài viết chỉ hy vọng cung cấp cho bạn đọc một số điểm hữu ích mà dựa vào đó ta có khả năng cải thiện được phần nào tốc độ thực thi các đoạn mã Javascript mà thôi.
Good luck!