Vinova tuyển lập trình viên Mobile & Web ở Hà Nội, lương $300-1000

Article: Mẹo vặt JavaScript 2787

javascript
ngocdaothanh.myopenid.com 174
Updated over 4 years ago

Chủ đề này tập hợp một số mẹo vặt đúc kết từ kinh nghiệm thực tế sử dụng JavaScript (JS). Để hiểu và sử dụng được những mẹo này, người đọc cần có kiến thức trung bình khá trở lên về JS, nghĩa là ít nhất cũng đã đọc qua Nhập môn JS, JS nâng caoTài liệu hướng dẫn về prototype.js.

Qui ước

Để làm việc hiệu quả, các thành viên trong cùng project cần tuân theo qui ước chung. JS và Ruby có rất nhiều điểm tương đồng. Qui ước sau lấy chút tinh thần của Ruby.


var CONST = 10; // hằng số
var $global; // biến toàn cục
var i; // biến cục bộ
var Klass; // class

Trong JS không có hằng số, nên viết hoa tên biến để tự nhắc nhở đừng thay đổi giá trị của chúng.

JS không bắt phải thêm var khi khai báo biến. Nhưng khi khai báo biến, nên thêm var để nhìn là biết ngay (1) biến được khai báo ở chỗ nào và (2) giới hạn hoạt động của nó. Ví dụ:


function f() {
total = 0;
for (i = 1; i <= 6; i++)
total += i;
return total;
}

alert(f());

alert(total);
alert(i);

Trong ví dụ trên, vì totali không được khai báo với var nên chúng được coi như biến toàn cục, trái với ý của lập trình viên là muốn vùng hoạt động của chúng bị giới hạn trong f.

Nên khai báo tất cả các biến sử dụng trong hàm ngay ở đầu hàm, giống như ngôn ngữ C. Lí do là trong phạm vi hàm, biến khai báo trong block có scope vượt ra ngoài phạm vi của block, có thể gây hậu quả không lường trước. Ví dụ:


function f() {
for(var i = 0; i < 10; i++) {
document.write(i + ':');
for(var i = 0;i < 5; i++) {
document.write(' ' + i);
}
document.write("<br />");
}
}

Khi chạy, f sẽ chạy vô hạn, vì biến i trong block thứ hai có tác dụng cả ở ngoài block, tác động đến biến i trong block thứ nhất.

JS không cần dấu chấm phẩy (;) để ngăn cách. Nhưng nên dùng, chương trình sẽ dễ đọc hơn và tương thích với các chương trình làm gọn/mã hóa JS (cruncher, obfuscator).

Hàm cũng chỉ là biến

Đoạn mã 1 Đoạn mã 2

// cách khai báo 1
function f() {
alert('f');
}

f();
alert(f);

f = 5;
alert(f);

// cách khai báo 2
var f = function() {
alert('f');
}

f();
alert(f);

f = 5;
alert(f);

Hai đoạn mã trên cho thấy 2 điều:

  • Hàm cũng chỉ là biến, giá trị có thể thay đổi.
  • Có thề khai báo hàm theo kiểu của khai báo biến. Kiểu khai báo này hay hơn, vì nó cho thấy bản chất của hàm trong JS.

Tóm lại, trong JS mọi thứ đều là biến, đều có thể thay đổi.

onload của window và onready của DOM

window.onload được gọi sau khi trang web được load xong. Do đó, nên dùng window.onload làm điểm bắt đầu của chương trình, tương tự như dùng hàm main trong ngôn ngữ C hoặc Java.

Như khái niệm resource của chương trình desktop, trang HTML có thể coi là resource của chương trình web. Trình duyệt nạp trang web theo từng dòng từ trên xuống dưới, mà đoạn JS thường được đặt trước đoạn HTML, nên lỗi lập trình viên hay gặp phải là khởi tạo biến phụ thuộc đoạn HTML trong khi đoạn HTML chưa được nạp.

Nếu dùng prototype.js, thì nên dùng Event.observe, nó cho phép nhiều hàm cùng bắt window.onload.

Ví dụ:


Event.observe(window, 'load', f1, false);
Event.observe(window, 'load', f2, false);

var f1 = function() {
alert('f1');
}

var f2 = function() {
alert('f2');
}

Cập nhật: nên dùng onready của DOM thay cho onload của window

Tham khảo:

  • http://www.geekdaily.net/2007/07/27/javascript-windowonload-is-bad-mkay/
  • http://clientside.cnet.com/code-snippets/event-scripting/a-dom-ready-extension-for-prototype/

Dùng class

JS hỗ trợ lập trình hướng đối tượng khá tốt. Không dùng tính năng này mà để biến và hàm tóe loe, thì lúc chương trình phức tạp sẽ rất khó kiểm soát. Để hiểu rõ lập trình hướng đối tượng trong JS, nên đọc quyển Professional JavaScript for Web Developers

Việc khai báo và sử dụng class rất đơn giản nếu dùng prototype.js, xin xem thêm hướng dẫn của bạn lebinh.

Tách HTML và JS

Nên viết (1) các đoạn <script> và (2) xử lí sự kiện (như onclick, onchange, ...) thành tập tin riêng, tách khỏi tập tin HTML, sao cho trong tập tin HTML, không còn bất kì đoạn mã JS nào.

Việc tách này mang lại một số lợi điểm:

  • Tập tin HTML không còn vướng đống spaghetti JS, nên dễ thiết kế và chỉnh sửa giao diện.
  • Các đoạn JS liên quan đến nhau được tập hợp lại thành một hoặc nhiều tập tin, nên dễ viết và sửa lỗi.

Ví dụ:

Submit

Khi dùng JavaScript để submit form thông qua iframe (ví dụ khi muốn upload file), chú ý là form và các thành phần cần submit nằm trong form không được đặt là disabled.

Cross-domain Ajax

Cross-domain Ajax không phải là một vấn đề mới. Ngay khi có Ajax người ta đã muốn giải quyết vấn đề này vì cả Firefox và IE đều không cho phép bạn gửi 1 request đến một domain khác với domain hiện hành.

Sẽ rất tuyệt với nếu có thể lấy dữ liệu từ trang từ điển Fast Dictionary để dùng ở mọi trang web khác.

Có 4 giải pháp:

  • Dùng 1 proxy. Gửi request tới một trang trên máy chủ của mình và trang này forward tới trang ở domain khác, nhận dữ liệu trả lời và chuyển lại cho trang ban đầu. Nhược điểm của phương pháp này là phải thực hiện từ phía server-side và do đó tốn tài nguyên/băng thông của server.
  • Dùng JS. Tương tự như cách Google Analytics dùng khi nhúng một đoạn Javascript để theo dõi những ai truy cập vào trang web. Nhược điểm là dữ liệu chuyển qua lại phải là dạng JSON.
  • Dùng Flash. Đây là một kỹ thuật tấn công DOS phổ biến bằng cách cài flash vào trang web và request sang trang khác. Nhược điểm là phụ thuộc vào flash và bị một số nguy cơ bảo mật khác
  • Dùng IFrame. Đây là một kỹ thuật khá phức tạp. Tuy nhiên, Dojo đã cho phép bạn thực hiện điều này bằng cách khá đơn giản Đây là giải pháp tương đối toàn vẹn. Nhược điểm chính là IFrame sẽ tiêu tốn bộ nhớ của browser nhưng có lẽ không đáng kể.

Frame busting

Để tránh trang của mình chạy trong frame của người khác, có thể dùng:


if (top != self) {
top.location.href = location.href;
}

Thu nhỏ ảnh

Thỉnh thoảng ảnh nhúng vào diễn đàn, blog... to quá khổ, cần thu nhỏ cho vừa khít thành phần HTML bao quanh ảnh. Nếu dùng prototype.js thì chỉ đoạn mã ngắn sau, nhìn mã chắc các bạn đoán được thuật toán.

function resizeBigImages() {
  var images = $$('#all img');
  for (var i = 0; i < images.length; i++) {
    var p = $(images[i].parentNode);
    if (p != null) {
      var w = p.getWidth();
      if (images[i].width > w) {
        var ratio = w/images[i].width;
        var h = ratio*images[i].height;
        images[i].width = w;
        images[i].height = h;
      }
    }
  }
}
new PeriodicalExecuter(resizeBigImages, 3);

Nén

Đối với file JS to, nhất là khi dùng các thư viện như Dojo hay “YUI”: http://developer.yahoo.com/yui/, có thể dùng JavaScript Compressor để nén lại. Tỉ lệ nén thường đạt trở lên 50%.

Nguyên tắc nén là loại bỏ khoảng trắng và các dấu xuống dòng. Do đó khi viết chương trình, nên cẩn thận các dấu ; { }. Có thể dùng chương trình JSLint để kiểm tra xem source code không “chuẩn” chỗ nào, sau đó sửa lại cho tương thích với chương trình nén.

1 2 3 4 

Editors
ngocdaothanh.myopenid.com 174
jishin.myopenid.com 18
phananhvu.myopenid.com 125

Comments

id.cntt.tv/[Anonymous] 9
over 5 years ago
Prototype đã nén: http://ajaxian.com/archives/compressed-versions-of-prototype

Prototype + Scriptaculous đã nén: http://www.stevekallestad.com/blog/prototype_and_scriptaculous_compressed.html

id.cntt.tv/cuongx
over 4 years ago
“Hàm cũng chỉ là biến” -> Sai. Hàm là một object, biến trỏ đến hàm là 1 tham chiếu (reference)

“Hàm cũng chỉ là biến, giá trị có thể thay đổi” -> giá trị thay đổi là do biến tham chiếu đến chỗ khác hoặc mang giá trị khác.

Trong ví dụ ở trên:

// cách khai báo 1

function f() { alert(‘f’); }

f();

alert(f);

f = 5;

alert(f);

-> Kết quả thay đổi do JS dịch rất thông minh, tuỳ cơ ứng biến mà dịch f là biến tham chiếu hay biến giá trị

Đoạn mã sau sẽ chỉ rõ hơn:

function f() { alert(‘f’); }

f();

alert(typeof f); // kết quả là: “function”. Vì f lúc này là biến tham chiếu đến object có tên f khai báo ở trên

f = 5; // JS tự động đổi kiểu cho f, f trở thành biến giá trị

alert(typeof f); /* kết quả là: “number”, tức là 1 số. vì f lúc này là 1 biến giá trị, ko phải biến tham chiếu */

Với phần VD còn lại ở trên cũng tương tự như vậy.

“Có thề khai báo hàm theo kiểu của khai báo biến. Kiểu khai báo này hay hơn, vì nó cho thấy bản chất của hàm trong JS” -> Ko đúng. Kiểu 1 hay hơn vì nói rõ bản chất của hàm.

“Tóm lại, trong JS mọi thứ đều là biến, đều có thể thay đổi” -> Sai

Trong JS có hai loại: giá trị và object

  • Giá trị thì chỉ có duy nhất kiểu số, nếu thực hiện phép lấy kiểu của biến sẽ trả về chuỗi “number”, VD như câu lệnh alert(typeof f) ở trên.
  • Object: tất cả phần còn lại mà ko phải là số thì là object.

Chìa khoá của vấn đề ở đây là: JS là ngôn ngữ thông dịch và Nó dịch mã rất thông minh.

id.cntt.tv/cuongx
over 4 years ago
[Submit

Khi dùng JavaScript để submit form thông qua iframe (ví dụ khi muốn upload file), chú ý là form và các thành phần cần submit nằm trong form không được đặt là disabled.]

-> Khi dùng iframe thì ko nên để ẩn, chỉ cần đặt kích thước của nó là 1×1 là ổn. Với 1×1 thì ko nhìn thấy -> nó vẫn là visible, lúc đó xử lý onload hay bắt lỗi ko tìm đc nguồn của iframe đều ko có hạn chế gì.

<iframe src="..." height="1px" width="1px"></iframe>
id.cntt.tv/cuongx
over 4 years ago
Khi dùng tool JSLint có 1 hạn chế là nó ko cho phép tách 1 câu lệnh thành nhiều dòng mà ko có cặp {}:

if (true)

alert(“it’s true!”); // nằm trên 2 dòng

Câu lệnh trên ko sai nhưng JSLint vẫn báo lỗi. Sửa lại thành:

if (true) alert(“it’s true!”); // nằm cùng 1 dòng

hoặc

if (true) {

alert(“it’s true!”); // có cặp dấu {}

}

thì ok.

ngocdaothanh.myopenid.com 174
over 4 years ago
Với 1×1 thì ko nhìn thấy

Thường thì do có thêm cả border, kích thước sẽ lớn gấp vài lần 1x1, nên vẫn nhìn thấy và nhìn thấy rất xấu xí. Cách tốt hơn là kẹp nó vào div có position là absolute, giới hạn kích thước rồi tống nó đến tọa độ âm.

ngocdaothanh.myopenid.com 174
over 4 years ago

Trong JS có hai loại: giá trị và object...

Chính xác phải là primitive và object. Primitive là những gì có thể tham khảo ở đây. Danh sách những built-in object có thể tham khảo ở đây.

Số cũng là object. x là object, nếu có thể viết x.cái_gì_đó, ví dụ:

var x = 1;
x.toString();
 
phananhvu.myopenid.com 125
over 4 years ago
“Hàm cũng chỉ là biến” -> Sai. Hàm là một object, biến trỏ đến hàm là 1 tham chiếu (reference)

“Hàm cũng chỉ là biến, giá trị có thể thay đổi” -> giá trị thay đổi là do biến tham chiếu đến chỗ khác hoặc mang giá trị khác.

blah blah ...

“Tóm lại, trong JS mọi thứ đều là biến, đều có thể thay đổi” -> Sai

blah blah ...

Chìa khoá của vấn đề ở đây là: JS là ngôn ngữ thông dịch và Nó dịch mã rất thông minh.

Các ý này theo em là chính xác. Nếu không ai thắc mắc gì thì có lẽ xin bạn id.cntt.tv/cuongx update luôn bài viêt cho đúng tính thần bliki chứ nhỉ :D

ngocdaothanh.myopenid.com 174
over 4 years ago

Hàm Object.inspect của Firebug hay được dùng để inspet xem object chứa gì. Nhưng đôi khi nó không đủ mạnh để inspect đến nơi đến chốn (ví dụ chỉ hiện xxx [Object] thì huề vốn), lúc này có thể dùng đoạn mã này.

cuongx.myopenid.com 3
over 4 years ago

@phananhvu.myopenid.com

Xin lỗi bác, lâu rồi ko vào nên ko để ý comment của bác. Nhân tiện tôi mới viết 1 bài về JS có nói đến hàm Bài viết: JavaScript in advanced 2. Bác vào tham khảo nhé.

You must login to be able to comment

Uploaded files

No file uploaded yet

You must login to be able to upload

Nhà tài trợ:

Mọi người đều tự do viết bài, sửa bài của người khác, và bình luận ở trang web này. Bạn muốn chủ động tạo bài mới để chia sẻ kinh nghiệm với mọi người? Xin click link ở dưới.

Create new content