Viblo CTF
+2

Javascript - the bad part

Mở đầu

Javascript là ngôn ngữ mà bạn phải dùng thường xuyên khi thực hiện các thao tác xử lí phía client khi làm 1 trang web. Tuy nhiên nó cũng là 1 ngôn ngữ chứa đựng nhiều thứ phiền toái và dễ gây hiểu lầm. Tất nhiên mọi ngôn ngữ đều có những điểm mạnh và những điểm yếu nhưng bài viết này sẽ tập trung vào việc đưa ra 1 số vấn đề trong Javascript mà gây ra nhiều khó khăn cho lập trình viên nhất là khi chưa hiểu biết sâu về ngôn ngữ rắc rối này.

Biến toàn cục

Biến toàn cục (global variable) là 1 trong những nguyên nhân gây ra nhiều rắc rối nhất trên Javascript. 1 biến toàn cục có thể được gọi đến trong mọi scope. Không thể phủ nhận rằng sử dụng biến toàn cục rất tiện lợi đặc biệt là trong những chương trình nhỏ. Tuy nhiên vấn đề sẽ phát sinh khi chương trình trở nên lớn hơn. Việc sử dụng nhiều biến toàn cục làm suy giảm độ tin cậy của chương trình .

1 số lí do cho vấn đề trên có thể kể đến là:

  • Việc sử dụng nhiều biến toàn cục gây khó khăn cho việc chạy từng module nhỏ (khi test method chẳng hạn)
  • Khi chương trình lớn, nhiều khả năng sẽ có tên biến trong các module trùng với tên của biến toàn cục, dễ gây ra nhầm lẫn
  • Do phạm vi sử dụng của biến toàn cục là rất lớn nên khi chương trình lớn rất khó để nắm bắt hết logic của chương trình (nhất là khhi bạn không phải là người xây dựng chương trình từ những bước ban đầu)

Có nhiều ngôn ngữ khác cũng sử dụng biến toàn cục nhưng vấn đề của Javascript không phải là cho phép sử dụng biến toàn cục mà là phụ thuộc nhiều vào biến toàn cục. Quá trình biên dịch file javascript thường gắn liền với 1 global object ( trên trình duyệt là biến window) và sử dụng biến toàn cục là giải pháp để gắn kết các thành phần của chương trình với nhau.

Trên javascript chúng ta có thể định nghĩa biến toàn cục bằng 1 số cách sau

  • Khai báo var
var foo = val
  • Khai báo với global object
window.foo = val
  • Khai báo ngầm
foo = val

Việc cho phép bỏ qua khai báo var có vẻ khá tiện lợi nhưng nó cũng dẫn đến 1 lỗi rất hay gặp là sử dụng biến mà quên khai báo biến đó.

Scope

Cú pháp của Javacript được thừa hưởng từ C. Trong C 1 block (đoạn code được đặt trong cặp dấu ngoặc) sẽ tạo ra 1 block và biến được khai báo trong block thì không sử dụng được ngoài block. Có thể nói javascript sử dụng cú pháp block nhưng lại không cung cấp block scope. Điều này có thể gây bất ngờ cho những lập trình viên có kinh nghiệm trong những ngôn ngữ khác.

Chúng ta có thể kiểm chứng điều này với ví dụ dưới đây

function scopeTest() {

  // vòng for dưới đây là 1 ví dụ cho block trong javascript
  for (var i = 0; i <= 5; i++)
  {
    var count = i;
  }

  alert(count);

}

// gọi hàm scopeTest
scopeTest( );

Và kết quả là

Javascript scope

Như vậy thông báo alert với nội dung là giá trị của biến count vẫn xuất hiện chứng tỏ chúng ta có thể sử dụng biến được khai báo bên trong block ngay tại bên ngoài block.

Vậy là javascript không sử dụng block scope, câu hỏi đặt ra là nó sử dụng kiểu scope gì. Câu trả lời là function scope. Hãy xem xét ví dụ dưới đây

function scope1() {
  var a = 1;
}
function scope2() {
  var x = 2;

  if(x == 2)
  {
    var y = 3;
    for (var i = 0; i <= 4; i++)
    {
      var count = i;
    }
  }
  var str = "y=" + y + " i=" + i + " count=" + count;
  if (typeof(a) === "undefined") {
    str = str + " a=undefined";
    } else {
      str = str + " a=undefined";
    }
    alert(str);
  }
  scope2();

Thông báo alert sẽ hiển thị kết quả của các biến a, x, y, count trong đó biến a chỉ được khai báo trong scope1 còn các biến x, y, count được khai báo trong scope2, có thể nằm trong block hoặc không. Kết quả cho thấy các biến được khai báo trong 1 function có thể được gọi đến và sử dụng tại mọi nơi trong function đó không phụ thuộc vào việc nó có nằm trong block nào hay không, nhưng không thể gọi 1 biến của function này trong 1 function khác.

Javascript scope

Dấu chấm phẩy

Javascript có cơ chế hỗ trợ chương trình lỗi bằng cách tự động thêm dấu chấm phẩy 1 cách tự động. Tuy nhiên việc bỏ qua dấu chấm phẩy có thể gây ra những lỗi trầm trọng.

Việc tự động thêm dấu chấm phẩy đôi lúc gây ra bug mà khó phát hiện ra để sửa. Hãy xem xét ví dụ sau. Do cơ chế tự động thêm dấu chấm phẩy mà 2 đoạn code có vẻ như giống nhau này lại trả về 2 kết quả khác nhau.

return
{
  status: true
}
return {
  status: true
}

Nhìn qua thì 2 cách viết trên không có gì khác biệt nhiều ngoài vị trí dấu ngoặc tuy nhiên kết quả trả về thì lại khác nhau. Chúng ta có thể sẽ nghĩ rằng cả 2 đoạn code return trên trả về 1 object nhưng thực tế thì chỉ có cách viết thứ 2 mới cho kết quả như vậy. Cách viết 1 trả về undefined

Semicolon

Nếu không nắm rõ về cách mà Javascript tự động thêm dấu ; tốt nhất bạn hãy viết các câu lệnh một cách rõ ràng và đầy đủ dấu ; thay vì bỏ qua chúng. Có thể việc bỏ qua dấu ; giúp bạn tiết kiệm được chút ít dung lượng file js nhưng thay vào đó có thể gây ra lỗi không đáng có.

Bạn có thể tham khảo về cơ chế tự động thêm dấu ; của javascript tại đây

Floating Point

Cách lưu trữ biến kiểu Float trong Javascript có thể sẽ khiến bạn kinh ngạc và xem xét lại khả năng toán học của mình.

Hãy nhìn vào phép toán đơn giản dưới đây:

var a = 0.5 + 0.25;
var a = 0.1 + 0.2;

Kết quả có vẻ rõ như ban ngày 0.75 và 0.3 nhưng rất tiếc đó không phải là kết quả đúng trong Javascript. Và đây là đáp án

float

Vậy là 0.1 + 0.2 = 0.30000000000000004

Lí do là Javascript lưu trữ giá trị kiểu float bằng 64bit và giá trị khi đem ra tính toán của 0.1 và 0.2 thực ra chỉ là giá trị gần đúng lấy từ số nhị phân 64 bit nên kết quả nhận được không phải là kết quả mong muốn như phép tính số học. Còn 0.5 (1/2) và 0.25 (1/4) có thể biểu diễn chính xác dưới dạng số nhị phân nên kết quả tính ra vẫn chính xác.

Lời kết

Javascript quả thực là 1 ngôn ngữ khá rắc rối với những thứ dễ gây hiểu lầm như trên. Những điểm được liệt kê trong bài viết chỉ là 1 trong những vấn đề hay gặp phải trong Javascript. Ngoài ra còn có nhiều vấn đề khác như so sánh trong Javascript, tính truthy hay falsy trong Javascript ...


Variables Comparison in Javascript

Việc hiểu rõ những vấn đề này sẽ giúp bạn tránh được những phiền toái tốn thời gian khi lập trình với Javascript.


All Rights Reserved