Giới thiệu cuốn Maintainable Javascript 2

1. Comments

1.1. Comment 1 dòng

// Ví dụ tốt
if (condition) {

  // if you made it here, then all security checks passed
  allowed();
}

// Không tốt: Không có dòng trống trước comment
if (condition) {
  // if you made it here, then all security checks passed
  allowed();
}

// Không tốt: Sai lề
if (condition) {

// if you made it here, then all security checks passed
  allowed();
}

// Ví dụ tốt
var result = something + somethingElse; // somethingElse will never be null

// Không tốt: Không đủ khoảng trống giữa code và comment
var result = something + somethingElse;// somethingElse will never be null

// Tốt
// if (condition) {
// doSomething();
// thenDoSomethingElse();
// }

// Không tốt: Đoạn này nên dùng comment nhiều dòng
// This next piece of code is quite difficult, so let me explain.
// What you want to do is determine whether the condition is true
// and only then allow the user in. The condition is calculated
// from several different functions and may change during the
// lifetime of the session.
if (condition) {
  // if you made it here, then all security checks passed
  allowed();
}

1.2. Comment nhiều dòng

// Ví dụ tốt
if (condition) {

  /*
   * if you made it here,
   * then all security checks passed
   */
  allowed();
}

// Không tốt: Không có dòng trống trước comment
if (condition) {
  /*
   * if you made it here,
   * then all security checks passed
   */
  allowed();
}

// Không tốt: Thiếu khoảng trống sau dấu *
if (condition) {

  /*
   *if you made it here,
   *then all security checks passed
   */
  allowed();
}

// Không tốt: Sai lề
if (condition) {

/*
 * if you made it here,
 * then all security checks passed
 */
  allowed();
}

// Không tốt: Không dùng comment nhiều dòng cho comment đuôi
var result = something + somethingElse; /*somethingElse will never be null*/

1.3 Sử dụng comment

1.3.1 Sử dụng comment để giải thích rõ nghĩa code

// Không tốt
// Khởi tạo count
var count = 10;

//Tốt
// Thay đổi giá trị này sẽ dẫn đến những thay đổi nghiêm trọng
var count = 10;

1.3.1.1 Giải thích các đoạn code khó hiểu

// Ví dụ tốt

if (mode) {

  /*
   * In mode 2 (prototype to prototype and object to object), we recurse
   * once to do the proto to proto mix. The object to object mix will be
   * handled later on.
   */
   if (mode === 2) {
       Y.mix(receiver.prototype, supplier.prototype, overwrite,
           whitelist, 0, merge);
   }

  /*
   * Depending on which mode is specified, we may be copying from or to
   * the prototypes of the supplier and receiver.
   */
   from = mode === 1 || mode === 3 ? supplier.prototype : supplier;
   to = mode === 1 || mode === 4 ? receiver.prototype : receiver;

  /*
   * If either the supplier or receiver doesn't actually have a
   * prototype property, then we could end up with an undefined from
   * or to. If that happens, we abort and return the receiver.
   */
   if (!from || !to) {
       return receiver;
   }
} else {
   from = supplier;
   to = receiver;
}

1.3.1.2 Các đoạn code theo chủ ý của tác giả giống như là có lỗi

while (element &&(element = element[axis])) { // NOTE: assignment
  if ( (all || element[TAG_NAME]) && (!fn || fn(element)) ) {
    return element;
  }
}

1.3.1.3 Hack Browser-Specific

  var ret = false;

  if ( !needle || !element || !needle[NODE_TYPE] || !element[NODE_TYPE]) {
    ret = false;
  } else if (element[CONTAINS]) {

    // IE & SAF contains fail if needle not an ELEMENT_NODE
    if (Y.UA.opera || needle[NODE_TYPE] === 1) {
      ret = element[CONTAINS](needle);
    } else {
      ret = Y_DOM._bruteContains(element, needle);
    }
  } else if (element[COMPARE_DOCUMENT_POSITION]) { // gecko
    if (element === needle || !!(element[COMPARE_DOCUMENT_POSITION](needle) & 16)) {
      ret = true;
    }
  }
  return ret;

1.3.1.3 Documentation comment

/**
Returns a new object containing all of the properties of all the supplied
objects. The properties from later objects will overwrite those in earlier
objects.

Passing in a single object will create a shallow copy of it. For a deep copy,
use `clone()`.

@method merge
@param {Object} objects* One or more objects to merge.
@return {Object} A new merged object.
**/
Y.merge = function () {
  var args = arguments,
    i = 0,
    len = args.length,
    result = {};

  for (; i < len; ++i) {
    Y.mix(result, args[i], true);
  }

  return result;
};

2. Các câu lệnh và diễn đạt

2.1. Các khối lệnh với cặp dấu ngoặc {}

2.1.1. Cặp dấu ngoặc {} có thể được dùng với tất cả các khối lệnh if, for, while, do…while, try…catch…finally

// Không tốt dù đúng ngữ pháp JavaScript
if(condition)
  doSomething();

// Không tốt dù đúng ngữ pháp JavaScript
if(condition) doSomething();

// Ví dụ tốt
if (condition) {
  doSomething();
}

// Không tốt dù đúng ngữ pháp JavaScript
if (condition) { doSomething(); }

2.1.2. Dùng cặp dấu ngoặc cũng giúp phát hiện lỗi dễ hơn và làm rõ ý định viết của người code.

Ví dụ

if (condition)
  doSomething();
  doSomethingElse();

Nêú dùng cặp dấu ngoặc {} sẽ phát hiện lỗi dễ hơn

if (condition) {
  doSomething();
}
  doSomethingElse();
}

Hoặc

if (condition) {
  doSomething();
doSomethingElse();
}

2.1.3. Căn lề cặp dấu ngoặc

Có 2 cách căn lề cặp dấu ngoặc phổ biến a, Kế thừa từ Java

if (condition) {
  doSomething();
} else {
  doSomethingElse();
}

b, Theo kiểu của C#

if (condition)
{
  doSomething();
}
else
{
  doSomethingElse();
}

2.1.4. Khoảng trắng với cặp dấu ngoặc

2.1.4.1 Không có khoảng trắng giữa tên lệnh, dấu ngoặc đơn mở và dấu ngoặc nhọn mở

if(condition){
  doSomething();
}

2.1.4.2 Có khoảng trắng giữa tên lệnh, dấu ngoặc đơn mở và dấu ngoặc nhọn mở

if (condition) {
  doSomething();
}

2.1.4.3 Loại 2 thêm vào các khoảng trắng sau dấu ngoặc đơn mở và trước dấu ngoặc đơn đóng

if ( condition ) {
  doSomething();
}

Tác giả thích loại 2 vì là sự cân bằng giữa loại 1 và 3.

2.2 Câu lệnh switch

2.2.1 Lề

2.2.1.1 Cách dùng phổ biến

switch(condition) {
  case "first":
    // code
    break;

  case "second":
    // code
    break;

  case "third":
    // code
    break;

  default:
    // code
}

2.2.1.2 Crockford’s Code Conventions và Dojo Style Guide

switch(condition) {
case "first":
  // code
  break;
case "second":
  // code
  break;
case "third":
  // code
  break;
default:
  // code
}

2.2.2 Nhảy qua Falling Through

switch(condition) {

  // obvious fall through
  case "first":
  case "second":
    // code
    break;

  case "third":
    // code

    /*falls through*/
  default:
    // code
}

2.2.3 default

2.2.3.1 Có default dù nó không làm gì

switch(condition) {
  case "first":
    // code
    break;

  case "second":
    // code
    break;

  default:
    // do nothing
}

2.2.3.2 Không viết default khi nó không làm gì

Khi default không làm gì, không viết nó mà chỉ viết comment //no default. Tác giả khuyến khích dùng cách này vì nó chỉ rõ chủ ý của tác giả là không có default và tiết kiệm bộ nhớ vì không cần dùng thêm cấu trúc không cần thiết.

switch(condition) {
  case "first":
    // code
    break;

  case "second":
    // code
    break;

  // no default
}

2.3. Câu lệnh with

var book = {
  title: "Maintainable JavaScript",
  author: "Nicholas C. Zakas"
};

var message = "The book is ";

with (book) {
  message += title;
  message += " by " + author;
}

2.4 Vòng lặp for

2.4.1 break

var values = [ 1, 2, 3, 4, 5, 6, 7 ],
    i, len;

for (i=0, len=values.length; i < len; i++) {
  if (i == 2) {
      break; // no more iterations
  }
  process(values[i]);
}

2.4.2 continue

var values = [ 1, 2, 3, 4, 5, 6, 7 ],
    i, len;

for (i=0, len=values.length; i < len; i++) {
  if (i == 2) {
    continue; // skip just this iteration
  }
  process(values[i]);
}

Crockford’s Code Conventions không cho phép dùng continue vì nó có thể được thay thế sử dụng điều kiện và trở nên dễ hiểu, ít khả năng lỗi hơn. Ví dụ trên có thể viết bằng cách khác là:

var values = [ 1, 2, 3, 4, 5, 6, 7 ],
    i, len;
for (i=0, len=values.length; i < len; i++) {
  if (i != 2) {
    process(values[i]);
  }
}

. Tác giả khuyến khích tránh continue khi có thể tránh nhưng không loại bỏ nó hoàn toàn.

2.5 Vòng lặp for-in

2.5.1 hasOwnProperty()

Crockford’s Code Conventions yêu cầu và tác giả khuyến khích dùng hasOwnProperty() check để lọc vòng lặp for-in chỉ cho các thuộc tính instance vì for-in trả về cả các thuộc tính instance của object và các thuộc tính kế thừa từ prototype.

var prop;

for (prop in object) {
  if (object.hasOwnProperty(prop)) {
    console.log("Property name is " + prop);
    console.log("Property value is " + object[prop]);
  }
}

Khi muốn dùng với prototype chain, bạn nên dùng comment.

var prop;

for (prop in object) { // include prototype properties
  console.log("Property name is " + prop);
  console.log("Property value is " + object[prop]);
}

2.5.2 Dùng for-in với các đối tượng, Không dùng với mảng

// Không tốt
var values = [ 1, 2, 3, 4, 5, 6, 7],
    i;

for (i in values) {
  process(items[i]);
}

To be continued...