Photo by Pankaj Patel on Unsplash
Hướng dẫn phong cách Airbnb JavaScript()
Một cách tiếp cận hợp lý nhất đối với JavaScript.
Các hướng dẫn phong cách khác
Dịch từ Airbnb JavaScript Style Guide.
Các kiểu (Types)
- 1.1 Nguyên thủy (Primitives): Bạn truy cập trực tiếp vào các kiểu nguyên thủy.
stringnumberbooleannullundefined
const foo = 1;
let bar = foo;
bar = 9;
console.log(foo, bar); // => 1, 9
- 1.2 Phức hợp (Complex): Bạn truy cập các kiểu phức hợp bằng tham chiếu.
objectarrayfunction
const foo = [1, 2];
const bar = foo;
bar[0] = 9;
console.log(foo[0], bar[0]); // => 9, 9
Tham chiếu (References)
- 2.1 Sử dụng
constcho tất cả các tham chiếu của bạn; tránhvar. eslint:prefer-const,no-const-assign
Tại sao? Điều này đảm bảo rằng bạn không thể gán lại các tham chiếu của mình, điều này có thể dẫn đến lỗi và mã khó hiểu.
// bad
var a = 1;
var b = 2;
// good
const a = 1;
const b = 2;
- 2.2 Nếu bạn phải gán lại tham chiếu, hãy sử dụng
letthay vìvar. eslint:no-varjscs:disallowVar
Tại sao?
letthuộc phạm vi khối (block-scoped), thay vì phạm vi hàm (function-scoped) giống nhưvar.
// bad
var count = 1;
if (true) {
count += 1;
}
// good, use the let.
let count = 1;
if (true) {
count += 1;
}
- 2.3 Lưu ý rằng cả
letvàconstđều thuộc phạm vi khối.
// const và let chỉ tồn tại trong các khối mà chúng được định nghĩa.
{
let a = 1;
const b = 1;
}
console.log(a); // ReferenceError
console.log(b); // ReferenceError
Đối tượng (Objects)
- 3.1 Sử dụng cú pháp literal để tạo đối tượng. eslint rules:
no-new-object.
// bad
const item = new Object();
// good
const item = {};
- 3.2 Đừng sử dụng từ dành riêng (reserved words) làm khóa. Nó sẽ không hoạt động trong IE8. Xem thêm. Sử dụng chúng trong các mô-đun ES6 và mã phía máy chủ thì không sao. jscs:
disallowIdentifierNames
// bad
const superman = {
default: { clark: 'kent' },
private: true,
};
// good
const superman = {
defaults: { clark: 'kent' },
hidden: true,
};
- 3.3 Sử dụng các từ đồng nghĩa dễ đọc thay cho các từ dành riêng. jscs:
disallowIdentifierNames
// bad
const superman = {
class: 'alien',
};
// bad
const superman = {
klass: 'alien',
};
// good
const superman = {
type: 'alien',
};
- 3.4 Sử dụng tên thuộc tính được tính toán (computed property names) khi tạo các đối tượng với tên thuộc tính động.
Tại sao? Chúng cho phép bạn định nghĩa tất cả các thuộc tính của một đối tượng ở một nơi.
function getKey(k) {
return `a key named ${k}`;
}
// bad
const obj = {
id: 5,
name: 'San Francisco',
};
obj[getKey('enabled')] = true;
// good
const obj = {
id: 5,
name: 'San Francisco',
[getKey('enabled')]: true,
};
- 3.5 Sử dụng cách viết tắt phương thức đối tượng. eslint:
object-shorthandjscs:requireEnhancedObjectLiterals
// bad
const atom = {
value: 1,
addValue: function (value) {
return atom.value + value;
},
};
// good
const atom = {
value: 1,
addValue(value) {
return atom.value + value;
},
};
- 3.6 Sử dụng cách viết tắt giá trị thuộc tính. eslint:
object-shorthandjscs:requireEnhancedObjectLiterals
Tại sao? Nó ngắn hơn và rõ ràng hơn.
const lukeSkywalker = 'Luke Skywalker';
// bad
const obj = {
lukeSkywalker: lukeSkywalker,
};
// good
const obj = {
lukeSkywalker,
};
- 3.7 Nhóm các thuộc tính viết tắt của bạn ở đầu khai báo đối tượng.
Tại sao? Để dễ dàng biết thuộc tính nào đang sử dụng cách viết tắt.
const anakinSkywalker = 'Anakin Skywalker';
const lukeSkywalker = 'Luke Skywalker';
// bad
const obj = {
episodeOne: 1,
twoJediWalkIntoACantina: 2,
lukeSkywalker,
episodeThree: 3,
mayTheFourth: 4,
anakinSkywalker,
};
// good
const obj = {
lukeSkywalker,
anakinSkywalker,
episodeOne: 1,
twoJediWalkIntoACantina: 2,
episodeThree: 3,
mayTheFourth: 4,
};
- 3.8 Chỉ trích dẫn các thuộc tính là định danh không hợp lệ. eslint:
quote-propsjscs:disallowQuotedKeysInObjects
Tại sao? Nói chung chúng tôi coi nó là chủ quan dễ đọc hơn. Nó cải thiện việc tô sáng cú pháp và cũng dễ dàng được tối ưu hóa bởi nhiều công cụ JS.
// bad
const bad = {
'foo': 3,
'bar': 4,
'data-blah': 5,
};
// good
const good = {
foo: 3,
bar: 4,
'data-blah': 5,
};
Mảng (Arrays)
- 4.1 Sử dụng cú pháp literal để tạo mảng. eslint:
no-array-constructor
// bad
const items = new Array();
// good
const items = [];
- 4.2 Sử dụng Array#push thay vì gán trực tiếp để thêm một mục vào mảng.
const someStack = [];
// bad
someStack[someStack.length] = 'abracadabra';
// good
someStack.push('abracadabra');
- 4.3 Sử dụng array spreads
...để sao chép mảng.
// bad
const len = items.length;
const itemsCopy = [];
let i;
for (i = 0; i < len; i++) {
itemsCopy[i] = items[i];
}
// good
const itemsCopy = [...items];
- 4.4 Để chuyển đổi một đối tượng giống mảng (array-like object) thành một mảng, hãy sử dụng Array#from.
const foo = document.querySelectorAll('.foo');
const nodes = Array.from(foo);
- 4.5 Sử dụng các câu lệnh return trong các callback của phương thức mảng. Có thể bỏ qua return nếu thân hàm bao gồm một câu lệnh duy nhất theo sau 8.2. eslint:
array-callback-return
// good
[1, 2, 3].map((x) => {
const y = x + 1;
return x * y;
});
// good
[1, 2, 3].map(x => x + 1);
// bad
const flat = {};
[[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => {
const flatten = memo.concat(item);
flat[index] = memo.concat(item);
});
// good
const flat = {};
[[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => {
const flatten = memo.concat(item);
flat[index] = flatten;
return flatten;
});
// bad
inbox.filter((msg) => {
const { subject, author } = msg;
if (subject === 'Mockingbird') {
return author === 'Harper Lee';
} else {
return false;
}
});
// good
inbox.filter((msg) => {
const { subject, author } = msg;
if (subject === 'Mockingbird') {
return author === 'Harper Lee';
}
return false;
});
Phá vỡ cấu trúc (Destructuring)
- 5.1 Sử dụng phá vỡ cấu trúc đối tượng khi truy cập và sử dụng nhiều thuộc tính của một đối tượng. jscs:
requireObjectDestructuring
Tại sao? Phá vỡ cấu trúc giúp bạn không phải tạo các tham chiếu tạm thời cho các thuộc tính đó.
// bad
function getFullName(user) {
const firstName = user.firstName;
const lastName = user.lastName;
return `${firstName} ${lastName}`;
}
// good
function getFullName(user) {
const { firstName, lastName } = user;
return `${firstName} ${lastName}`;
}
// best
function getFullName({ firstName, lastName }) {
return `${firstName} ${lastName}`;
}
- 5.2 Sử dụng phá vỡ cấu trúc mảng. jscs:
requireArrayDestructuring
const arr = [1, 2, 3, 4];
// bad
const first = arr[0];
const second = arr[1];
// good
const [first, second] = arr;
- 5.3 Sử dụng phá vỡ cấu trúc đối tượng cho nhiều giá trị trả về, không phải phá vỡ cấu trúc mảng.
Tại sao? Bạn có thể thêm các thuộc tính mới hoặc thay đổi thứ tự của mọi thứ mà không làm hỏng các nơi gọi.
// bad
function processInput(input) {
// sau đó một phép màu xảy ra
return [left, right, top, bottom];
}
// người gọi cần phải suy nghĩ về thứ tự của dữ liệu trả về
const [left, __, top] = processInput(input);
// good
function processInput(input) {
// sau đó một phép màu xảy ra
return { left, right, top, bottom };
}
// người gọi chỉ chọn dữ liệu họ cần
const { left, right } = processInput(input);
Chuỗi (Strings)
- 6.1 Sử dụng dấu nháy đơn
''cho các chuỗi. eslint:quotesjscs:validateQuoteMarks
// bad
const name = "Capt. Janeway";
// good
const name = 'Capt. Janeway';
- 6.2 Các chuỗi làm cho dòng vượt quá 100 ký tự không nên được viết trên nhiều dòng bằng cách sử dụng nối chuỗi.
- 6.3 Lưu ý: Nếu lạm dụng, nối chuỗi dài có thể ảnh hưởng đến hiệu suất. jsPerf & Thảo luận.
// bad
const errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.';
// bad
const errorMessage = 'This is a super long error that was thrown because \
of Batman. When you stop to think about how Batman had anything to do \
with this, you would get nowhere \
fast.';
// good
const errorMessage = 'This is a super long error that was thrown because ' +
'of Batman. When you stop to think about how Batman had anything to do ' +
'with this, you would get nowhere fast.';
- 6.4 Khi xây dựng chuỗi theo chương trình, hãy sử dụng template strings thay vì nối chuỗi. eslint:
prefer-templatetemplate-curly-spacingjscs:requireTemplateStrings
Tại sao? Template strings cung cấp cho bạn cú pháp ngắn gọn, dễ đọc với các dòng mới phù hợp và các tính năng nội suy chuỗi (string interpolation).
// bad
function sayHi(name) {
return 'How are you, ' + name + '?';
}
// bad
function sayHi(name) {
return ['How are you, ', name, '?'].join();
}
// bad
function sayHi(name) {
return `How are you, ${ name }?`;
}
// good
function sayHi(name) {
return `How are you, ${name}?`;
}
- 6.5 Không bao giờ sử dụng
eval()trên một chuỗi, nó mở ra quá nhiều lỗ hổng.
Hàm (Functions)
- 7.1 Sử dụng khai báo hàm (function declarations) thay vì biểu thức hàm (function expressions). jscs:
requireFunctionDeclarations
Tại sao? Khai báo hàm có tên, vì vậy chúng dễ nhận biết hơn trong stack traces. Ngoài ra, khai báo hàm được hoisted (đưa lên đầu), trong khi biểu thức hàm thì không. Quy tắc này giúp cho Arrow Functions có thể thay thế hoàn toàn biểu thức hàm.
// bad
const foo = function () {
};
// good
function foo() {
}
- 7.2 IIFE: eslint:
wrap-iifejscs:requireParenthesesAroundIIFE
Tại sao? IIFE là một đơn vị duy nhất - bao gồm cả hàm và lệnh gọi của nó trong dấu ngoặc đơn làm cho điều này rõ ràng. Lưu ý rằng trong thế giới mô-đun, bạn thường không cần IIFEs nữa.
// immediately-invoked function expression (IIFE)
(function () {
console.log('Welcome to the Internet. Please follow me.');
}());
-
7.3 Không bao giờ khai báo một hàm trong một khối không phải hàm (if, while, v.v.). Thay vào đó, hãy gán hàm cho một biến. Các trình duyệt sẽ cho phép bạn làm điều đó, nhưng tất cả chúng đều diễn giải nó theo cách khác nhau. eslint:
no-loop-func -
7.4 Lưu ý: ECMA-262 định nghĩa
blocklà một danh sách các câu lệnh (statements). Một khai báo hàm không phải là một câu lệnh. Đọc ghi chú của ECMA-262 về vấn đề này.
// bad
if (currentUser) {
function test() {
console.log('Nope.');
}
}
// good
let test;
if (currentUser) {
test = () => {
console.log('Yup.');
};
}
- 7.5 Không bao giờ đặt tên tham số là
arguments. Điều này sẽ được ưu tiên hơn đối tượngargumentsđược cung cấp cho mọi phạm vi hàm.
// bad
function nope(name, options, arguments) {
// ...stuff...
}
// good
function yup(name, options, args) {
// ...stuff...
}
- 7.6 Không bao giờ sử dụng
arguments, chọn sử dụng cú pháp rest...thay thế.prefer-rest-params
Tại sao?
...rõ ràng về những đối số bạn muốn lấy ra. Thêm vào đó, các đối số rest là một Array thực sự, và không chỉ đơn thuần là Array-like nhưarguments.
// bad
function concatenateAll() {
const args = Array.prototype.slice.call(arguments);
return args.join('');
}
// good
function concatenateAll(...args) {
return args.join('');
}
- 7.7 Sử dụng cú pháp tham số mặc định thay vì thay đổi các đối số của hàm.
// really bad
function handleThings(opts) {
// No! We shouldn't mutate function arguments.
// Double bad: if opts is false it will be set to an object which may
// be what you want but it can cause subtle bugs.
opts = opts || {};
// ...
}
// still bad
function handleThings(opts) {
if (opts === void 0) {
opts = {};
}
// ...
}
// good
function handleThings(opts = {}) {
// ...
}
- 7.8 Tránh các tác dụng phụ (side effects) với các tham số mặc định.
Tại sao? Chung gây nhầm lẫn khi suy luận.
var b = 1;
// bad
function count(a = b++) {
console.log(a);
}
count(); // 1
count(); // 2
count(3); // 3
count(); // 3
- 7.9 Luôn đặt các tham số mặc định ở cuối cùng.
// bad
function handleThings(opts = {}, name) {
// ...
}
// good
function handleThings(name, opts = {}) {
// ...
}
- 7.10 Không bao giờ sử dụng Function constructor để tạo một hàm mới.
Tại sao? Tạo một hàm theo cách này đánh giá một chuỗi tương tự như eval(), điều này mở ra các lỗ hổng.
// bad
var add = new Function('a', 'b', 'return a + b');
// still bad
var subtract = Function('a', 'b', 'return a - b');
- 7.11 Khoảng cách trong chữ ký hàm (function signature).
Tại sao? Sự nhất quán là tốt, và bạn không nên phải thêm hoặc xóa khoảng trắng khi thêm hoặc xóa tên.
// bad
const f = function(){};
const g = function (){};
const h = function() {};
// good
const x = function () {};
const y = function a() {};
- 7.12 Không bao giờ thay đổi (mutate) tham số. eslint:
no-param-reassign
Tại sao? Thao tác các đối tượng được truyền vào dưới dạng tham số có thể gây ra tác dụng phụ biến không mong muốn trong trình gọi ban đầu.
// bad
function f1(obj) {
obj.key = 1;
};
// good
function f2(obj) {
const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1;
};
- 7.13 Không bao giờ gán lại (reassign) các tham số. eslint:
no-param-reassign
Tại sao? Việc gán lại các tham số có thể dẫn đến hành vi không mong muốn, đặc biệt khi truy cập đối tượng
arguments. Nó cũng có thể gây ra các vấn đề tối ưu hóa, đặc biệt là trong V8.
```javascript
// bad
function f1(a) {
a = 1;
}
function f2(a) {
if (!a) { a = 1; }
}
// good
function f3(a) {
const b = a || 1;
}
function f4(a = 1) {
}
```
Arrow Functions
- 8.1 Khi bạn phải sử dụng biểu thức hàm (như khi truyền một hàm ẩn danh), hãy sử dụng ký hiệu arrow function. eslint:
prefer-arrow-callback,arrow-spacingjscs:requireArrowFunctions
Tại sao? Nó tạo ra một phiên bản của hàm thực thi trong ngữ cảnh của
this, thường là những gì bạn muốn, và là một cú pháp ngắn gọn hơn.
Tại sao không? Nếu bạn có một hàm khá phức tạp, di chuyển logic đó ra bên ngoài vào khai báo hàm của riêng nó.
// bad
[1, 2, 3].map(function (x) {
const y = x + 1;
return x * y;
});
// good
[1, 2, 3].map((x) => {
const y = x + 1;
return x * y;
});
- 8.2 Nếu thân hàm bao gồm một câu lệnh duy nhất trả về một biểu thức mà không có tác dụng phụ, hãy bỏ qua dấu ngoặc nhọn và sử dụng trả về ngầm định (implicit return). Nếu không, hãy giữ dấu ngoặc nhọn và sử dụng câu lệnh
return. eslint:arrow-parens,arrow-body-stylejscs:disallowParenthesesAroundArrowParam,requireShorthandArrowFunctions
Tại sao? Syntactic sugar (Cú pháp ngọt). Nó dễ đọc khi nhiều hàm được nối với nhau.
Tại sao không? Nếu bạn định trả về một đối tượng.
// bad
[1, 2, 3].map(number => {
const nextNumber = number + 1;
`A string containing the ${nextNumber}.`;
});
// good
[1, 2, 3].map(number => `A string containing the ${number}.`);
// good
[1, 2, 3].map((number) => {
const nextNumber = number + 1;
return `A string containing the ${nextNumber}.`;
});
- 8.3 Trong trường hợp biểu thức kéo dài trên nhiều dòng, hãy bao bọc nó trong dấu ngoặc đơn để dễ đọc hơn.
Tại sao? Nó cho thấy rõ nơi hàm bắt đầu và kết thúc.
// bad
[1, 2, 3].map(number => 'As time went by, the string containing the ' +
`${number} became much longer. So we needed to break it over multiple ` +
'lines.'
);
// good
[1, 2, 3].map(number => (
`As time went by, the string containing the ${number} became much ` +
'longer. So we needed to break it over multiple lines.'
));
- 8.4 Nếu hàm của bạn lấy một đối số duy nhất và không sử dụng dấu ngoặc nhọn, hãy bỏ qua dấu ngoặc đơn. Nếu không, hãy luôn bao gồm dấu ngoặc đơn xung quanh các đối số. eslint:
arrow-parensjscs:disallowParenthesesAroundArrowParam
Tại sao? Ít lộn xộn về mặt thị giác.
// bad
[1, 2, 3].map((x) => x * x);
// good
[1, 2, 3].map(x => x * x);
// good
[1, 2, 3].map(number => (
`A long string with the ${number}. It’s so long that we’ve broken it ` +
'over multiple lines!'
));
// bad
[1, 2, 3].map(x => {
const y = x + 1;
return x * y;
});
// good
[1, 2, 3].map((x) => {
const y = x + 1;
return x * y;
});
- 8.5 Tránh nhầm lẫn cú pháp arrow function (
=>) với các toán tử so sánh (<=,>=). eslint:no-confusing-arrow
// bad
const itemHeight = item => item.height > 256 ? item.largeSize : item.smallSize;
// bad
const itemHeight = (item) => item.height > 256 ? item.largeSize : item.smallSize;
// good
const itemHeight = item => { return item.height > 256 ? item.largeSize : item.smallSize; }
Constructors
- 9.1 Luôn sử dụng
class. Tránh thao túngprototypetrực tiếp.
Tại sao? Cú pháp
classngắn gọn hơn và dễ suy luận hơn.
// bad
function Queue(contents = []) {
this._queue = [...contents];
}
Queue.prototype.pop = function () {
const value = this._queue[0];
this._queue.splice(0, 1);
return value;
}
// good
class Queue {
constructor(contents = []) {
this._queue = [...contents];
}
pop() {
const value = this._queue[0];
this._queue.splice(0, 1);
return value;
}
}
- 9.2 Sử dụng
extendsđể kế thừa.
Tại sao? Đây là một cách tích hợp để kế thừa chức năng từ một nguyên mẫu (prototype) mà không phá vỡ
instanceof.
// bad
const inherits = require('inherits');
function PeekableQueue(contents) {
Queue.apply(this, contents);
}
inherits(PeekableQueue, Queue);
PeekableQueue.prototype.peek = function () {
return this._queue[0];
}
// good
class PeekableQueue extends Queue {
peek() {
return this._queue[0];
}
}
- 9.3 Các phương thức có thể trả về
thisđể giúp việc nối chuỗi phương thức (method chaining).
// bad
Jedi.prototype.jump = function () {
this.jumping = true;
return true;
};
Jedi.prototype.setHeight = function (height) {
this.height = height;
};
const luke = new Jedi();
luke.jump(); // => true
luke.setHeight(20); // => undefined
// good
class Jedi {
jump() {
this.jumping = true;
return this;
}
setHeight(height) {
this.height = height;
return this;
}
}
const luke = new Jedi();
luke.jump()
.setHeight(20);
- 9.4 Không sao khi viết một phương thức toString() tùy chỉnh, chỉ cần đảm bảo rằng nó hoạt động thành công và không gây ra tác dụng phụ.
class Jedi {
constructor(options = {}) {
this.name = options.name || 'no name';
}
getName() {
return this.name;
}
toString() {
return `Jedi - ${this.getName()}`;
}
}
- 9.5 Các lớp có một constructor mặc định nếu không có constructor nào được chỉ định. Một constructor trống hoặc chỉ ủy quyền cho một lớp cha là không cần thiết.
no-useless-constructor
// bad
class Jedi {
constructor() {}
getName() {
return this.name;
}
}
// bad
class Rey extends Jedi {
constructor(...args) {
super(...args);
}
}
// good
class Rey extends Jedi {
constructor(...args) {
super(...args);
this.name = 'Rey';
}
}
Mô-đun (Modules)
- 10.1 Luôn sử dụng các mô-đun (
import/export) thay vì hệ thống mô-đun không chuẩn. Bạn luôn có thể chuyển đổi (transpile) sang hệ thống mô-đun ưa thích của bạn.
Tại sao? Các mô-đun là tương lai, hãy bắt đầu sử dụng tương lai ngay bây giờ.
// bad
const AirbnbStyleGuide = require('./AirbnbStyleGuide');
module.exports = AirbnbStyleGuide.es6;
// ok
import AirbnbStyleGuide from './AirbnbStyleGuide';
export default AirbnbStyleGuide.es6;
// best
import { es6 } from './AirbnbStyleGuide';
export default es6;
- 10.2 Không sử dụng wildcard imports (import ký tự đại diện).
Tại sao? Điều này đảm bảo bạn có một default export duy nhất.
// bad
import * as AirbnbStyleGuide from './AirbnbStyleGuide';
// good
import AirbnbStyleGuide from './AirbnbStyleGuide';
- 10.3 Và không export trực tiếp từ một import.
Tại sao? Mặc dù viết một dòng ngắn gọn, nhưng có một cách rõ ràng để import và export mọi thứ làm cho mọi thứ nhất quán.
// bad
// filename es6.js
export { es6 as default } from './airbnbStyleGuide';
// good
// filename es6.js
import { es6 } from './AirbnbStyleGuide';
export default es6;
Iterators và Generators
- 11.1 Đừng sử dụng iterators. Ưu tiên các hàm bậc cao của JavaScript như
map()vàreduce()thay vì các vòng lặp nhưfor-of. eslint:no-iterator
Tại sao? Điều này thực thi quy tắc bất biến (immutable) của chúng tôi. Làm việc với các hàm thuần túy trả về giá trị dễ suy luận hơn các tác dụng phụ.
eslint rules: no-iterator.
const numbers = [1, 2, 3, 4, 5];
// bad
let sum = 0;
for (let num of numbers) {
sum += num;
}
sum === 15;
// good
let sum = 0;
numbers.forEach(num => sum += num);
sum === 15;
// best (use the functional force)
const sum = numbers.reduce((total, num) => total + num, 0);
sum === 15;
- 11.2 Đừng sử dụng generators vào lúc này.
Tại sao? Chúng chưa chuyển đổi tốt sang ES5.
Thuộc tính (Properties)
- 12.1 Sử dụng dot notation (ký hiệu dấu chấm) khi truy cập các thuộc tính. eslint:
dot-notationjscs:requireDotNotation
const luke = {
jedi: true,
age: 28,
};
// bad
const isJedi = luke['jedi'];
// good
const isJedi = luke.jedi;
- 12.2 Sử dụng bracket notation (ký hiệu ngoặc vuông)
[]khi truy cập các thuộc tính bằng một biến.
const luke = {
jedi: true,
age: 28,
};
function getProp(prop) {
return luke[prop];
}
const isJedi = getProp('jedi');
Biến (Variables)
- 13.1 Luôn sử dụng
consthoặcletđể khai báo biến. Không làm như vậy sẽ dẫn đến các biến toàn cục (global variables). Chúng tôi muốn tránh làm ô nhiễm không gian tên toàn cục (global namespace). Captain Planet đã cảnh báo chúng tôi về điều đó.
// bad
superPower = new SuperPower();
// good
const superPower = new SuperPower();
- 13.2 Sử dụng một khai báo
consthoặcletcho mỗi biến. eslint:one-varjscs:disallowMultipleVarDecl
Tại sao? Dễ dàng hơn để thêm các khai báo biến mới theo cách này, và bạn không bao giờ phải lo lắng về việc hoán đổi một
;cho một,hoặc giới thiệu các sự khác biệt chỉ do dấu câu.
// bad
const items = getItems(),
goSportsTeam = true,
dragonball = 'z';
// bad
// (compare to above, notice the error)
const items = getItems(),
goSportsTeam = true;
dragonball = 'z';
// good
const items = getItems();
const goSportsTeam = true;
const dragonball = 'z';
- 13.3 Nhóm tất cả các
constcủa bạn và sau đó nhóm tất cả cácletcủa bạn.
Tại sao? Điều này hữu ích khi sau này bạn có thể cần gán một biến phụ thuộc vào một trong các biến được gán trước đó.
// bad
let i, len, dragonball,
items = getItems(),
goSportsTeam = true;
// bad
let i;
const items = getItems();
let dragonball;
const goSportsTeam = true;
let len;
// good
const goSportsTeam = true;
const items = getItems();
let dragonball;
let i;
let length;
- 13.4 Gán các biến ở nơi bạn cần chúng, nhưng đặt chúng ở một nơi hợp lý.
Tại sao?
letvàconstlà block scoped (phạm vi khối) và không phải function scoped (phạm vi hàm).
// bad - unnecessary function call
function checkName(hasName) {
const name = getName();
if (hasName === 'test') {
return false;
}
if (name === 'test') {
this.setName('');
return false;
}
return name;
}
// good
function checkName(hasName) {
if (hasName === 'test') {
return false;
}
const name = getName();
if (name === 'test') {
this.setName('');
return false;
}
return name;
}
Hoisting
- 14.1 Các khai báo
varđược hoisted lên đầu phạm vi của chúng, nhưng việc gán của chúng thì không. Các khai báoconstvàletđược ban phước với một khái niệm mới gọi là Temporal Dead Zones (TDZ). Điều quan trọng là phải biết tại sao typeof không còn an toàn nữa.
// we know this wouldn't work (assuming there
// is no notDefined global variable)
function example() {
console.log(notDefined); // => throws a ReferenceError
}
// creating a variable declaration after you
// reference the variable will work due to
// variable hoisting. Note: the assignment
// value of `true` is not hoisted.
function example() {
console.log(declaredButNotAssigned); // => undefined
var declaredButNotAssigned = true;
}
// The interpreter is hoisting the variable
// declaration to the top of the scope,
// which means our example could be rewritten as:
function example() {
let declaredButNotAssigned;
console.log(declaredButNotAssigned); // => undefined
declaredButNotAssigned = true;
}
// using const and let
function example() {
console.log(declaredButNotAssigned); // => throws a ReferenceError
console.log(typeof declaredButNotAssigned); // => throws a ReferenceError
const declaredButNotAssigned = true;
}
- 14.2 Anonymous function expressions hoist tên biến của chúng, nhưng không hoist phép gán hàm.
function example() {
console.log(anonymous); // => undefined
anonymous(); // => TypeError anonymous is not a function
var anonymous = function () {
console.log('anonymous function expression');
};
}
- 14.3 Named function expressions hoist tên biến, không phải tên hàm hoặc thân hàm.
function example() {
console.log(named); // => undefined
named(); // => TypeError named is not a function
superPower(); // => ReferenceError superPower is not defined
var named = function superPower() {
console.log('Flying');
};
}
// the same is true when the function name
// is the same as the variable name.
function example() {
console.log(named); // => undefined
named(); // => TypeError named is not a function
var named = function named() {
console.log('named');
}
}
- 14.4 Function declarations hoist tên và thân hàm của chúng.
function example() {
superPower(); // => Flying
function superPower() {
console.log('Flying');
}
}
- Để biết thêm thông tin, tham khảo JavaScript Scoping & Hoisting bởi Ben Cherry.
Toán tử so sánh & Đẳng thức (Comparison Operators & Equality)
-
15.2 Các câu lệnh điều kiện như câu lệnh
ifđánh giá biểu thức của chúng bằng cách ép buộc (coercion) với phương thức trừu tượngToBooleanvà luôn tuân theo các quy tắc đơn giản sau:
- Objects đánh giá là true
- Undefined đánh giá là false
- Null đánh giá là false
- Booleans đánh giá là giá trị của boolean
- Numbers đánh giá là false nếu là +0, -0, hoặc NaN, ngược lại là true
- Strings đánh giá là false nếu là một chuỗi rỗng
'', ngược lại là true
if ([0] && []) {
// true
// an array (even an empty one) is an object, objects will evaluate to true
}
- 15.3 Sử dụng phím tắt.
// bad
if (name !== '') {
// ...stuff...
}
// good
if (name) {
// ...stuff...
}
// bad
if (collection.length > 0) {
// ...stuff...
}
// good
if (collection.length) {
// ...stuff...
}
- 15.4 Để biết thêm thông tin, xem Truth Equality and JavaScript bởi Angus Croll.
- 15.5 Sử dụng dấu ngoặc nhọn để tạo các khối trong mệnh đề
casevàdefaultcó chứa các khai báo lexical (ví dụ:let,const,function, vàclass).
Tại sao? Các khai báo lexical hiển thị trong toàn bộ khối
switchnhưng chỉ được khởi tạo khi được gán, điều này chỉ xảy ra khi đạt đếncasecủa nó. Điều này gây ra vấn đề khi nhiều mệnh đềcasecố gắng định nghĩa cùng một thứ.
eslint rules: no-case-declarations.
// bad
switch (foo) {
case 1:
let x = 1;
break;
case 2:
const y = 2;
break;
case 3:
function f() {}
break;
default:
class C {}
}
// good
switch (foo) {
case 1: {
let x = 1;
break;
}
case 2: {
const y = 2;
break;
}
case 3: {
function f() {}
break;
}
case 4:
bar();
break;
default: {
class C {}
}
}
- 15.7 Ternaries không nên lồng nhau và thường là các biểu thức một dòng.
eslint rules: no-nested-ternary.
// bad
const foo = maybe1 > maybe2
? "bar"
: value1 > value2 ? "baz" : null;
// better
const maybeNull = value1 > value2 ? 'baz' : null;
const foo = maybe1 > maybe2
? 'bar'
: maybeNull;
// best
const maybeNull = value1 > value2 ? 'baz' : null;
const foo = maybe1 > maybe2 ? 'bar' : maybeNull;
- 15.8 Tránh các câu lệnh ternary không cần thiết.
eslint rules: no-unneeded-ternary.
// bad
const foo = a ? a : b;
const bar = c ? true : false;
const baz = c ? false : true;
// good
const foo = a || b;
const bar = !!c;
const baz = !c;
Khối (Blocks)
- 16.1 Sử dụng dấu ngoặc nhọn với tất cả các khối nhiều dòng.
// bad
if (test)
return false;
// good
if (test) return false;
// good
if (test) {
return false;
}
// bad
function foo() { return false; }
// good
function bar() {
return false;
}
- 16.2 Nếu bạn đang sử dụng các khối nhiều dòng với
ifvàelse, hãy đặtelsetrên cùng một dòng với dấu ngoặc nhọn đóng của khốiif. eslint:brace-stylejscs:disallowNewlineBeforeBlockStatements
// bad
if (test) {
thing1();
thing2();
}
else {
thing3();
}
// good
if (test) {
thing1();
thing2();
} else {
thing3();
}
Các câu lệnh điều khiển (Control Statements)
- 17.1 Trong trường hợp câu lệnh điều khiển của bạn (
if,while, v.v.) quá dài hoặc vượt quá độ dài dòng tối đa, mỗi điều kiện (được nhóm) có thể được đặt vào một dòng mới. Toán tử logic nên bắt đầu dòng.
Tại sao? Yêu cầu các toán tử ở đầu dòng giữ cho các toán tử được căn chỉnh và tuân theo một mẫu tương tự như nối chuỗi phương thức. Điều này cũng cải thiện khả năng đọc bằng cách làm cho logic phức tạp dễ theo dõi hơn bằng mắt thường.
// bad
if ((foo === 123 || bar === 'abc') && doesItLookGoodWhenItBecomesThatLong() && isThisReallyHappening()) {
thing1();
}
// bad
if (foo === 123 &&
bar === 'abc') {
thing1();
}
// bad
if (foo === 123
&& bar === 'abc') {
thing1();
}
// bad
if (
foo === 123 &&
bar === 'abc'
) {
thing1();
}
// good
if (
foo === 123
&& bar === 'abc'
) {
thing1();
}
// good
if (
(foo === 123 || bar === 'abc')
&& doesItLookGoodWhenItBecomesThatLong()
&& isThisReallyHappening()
) {
thing1();
}
// good
if (foo === 123 && bar === 'abc') {
thing1();
}
- 17.2 Đừng sử dụng toán tử lựa chọn thay cho câu lệnh điều khiển.
// bad
!isRunning && startRunning();
// good
if (!isRunning) {
startRunning();
}
Bình luận (Comments)
- 18.1 Sử dụng
/** ... */cho các bình luận nhiều dòng. Bao gồm mô tả, chỉ định kiểu và giá trị cho tất cả các tham số và giá trị trả về.
// bad
// make() returns a new element
// based on the passed in tag name
//
// @param {String} tag
// @return {Element} element
function make(tag) {
// ...stuff...
return element;
}
// good
/**
* make() returns a new element
* based on the passed in tag name
*
* @param {String} tag
* @return {Element} element
*/
function make(tag) {
// ...stuff...
return element;
}
- 18.2 Sử dụng
//cho các bình luận một dòng. Đặt các bình luận một dòng trên một dòng mới phía trên chủ đề của bình luận. Đặt một dòng trống trước bình luận trừ khi nó ở dòng đầu tiên của khối.
// bad
const active = true; // is current tab
// good
// is current tab
const active = true;
// bad
function getType() {
console.log('fetching type...');
// set the default type to 'no type'
const type = this._type || 'no type';
return type;
}
// good
function getType() {
console.log('fetching type...');
// set the default type to 'no type'
const type = this._type || 'no type';
return type;
}
// also good
function getType() {
// set the default type to 'no type'
const type = this._type || 'no type';
return type;
}
-
18.3 Tiền tố các bình luận của bạn với
FIXMEhoặcTODOgiúp các nhà phát triển khác nhanh chóng hiểu nếu bạn đang chỉ ra một vấn đề cần được xem xét lại hoặc nếu bạn đang đề xuất một giải pháp cần được thực hiện. Những điều này khác với các bình luận thông thường vì chúng có thể hành động được. Các hành động làFIXME: -- need to figure this outhoặcTODO: -- need to implement. -
18.4 Sử dụng
// FIXME:để chú thích các vấn đề.
class Calculator extends Abacus {
constructor() {
super();
// FIXME: shouldn't use a global here
total = 0;
}
}
- 18.5 Sử dụng
// TODO:để chú thích các giải pháp cho các vấn đề.
class Calculator extends Abacus {
constructor() {
super();
// TODO: total should be configurable by an options param
this.total = 0;
}
}
Khoảng trắng (Whitespace)
- 19.1 Sử dụng dấu tab mềm (ký tự khoảng trắng) được đặt thành 2 khoảng trắng. eslint:
indentjscs:validateIndentation
// bad
function foo() {
∙∙∙∙const name;
}
// bad
function bar() {
∙const name;
}
// good
function baz() {
∙∙const name;
}
- 19.2 Đặt 1 khoảng trắng trước dấu ngoặc nhọn mở. eslint:
space-before-blocksjscs:requireSpaceBeforeBlockStatements
// bad
function test(){
console.log('test');
}
// good
function test() {
console.log('test');
}
// bad
dog.set('attr',{
age: '1 year',
breed: 'Bernese Mountain Dog',
});
// good
dog.set('attr', {
age: '1 year',
breed: 'Bernese Mountain Dog',
});
- 19.3 Đặt 1 khoảng trắng trước dấu ngoặc đơn mở trong các câu lệnh điều khiển (
if,while, v.v.). Không có khoảng trắng giữa danh sách đối số và tên hàm trong các lệnh gọi và khai báo hàm. eslint:space-after-keywords,space-before-keywordsjscs:requireSpaceAfterKeywords
// bad
if(isJedi) {
fight ();
}
// good
if (isJedi) {
fight();
}
// bad
function fight () {
console.log ('Swooosh!');
}
// good
function fight() {
console.log('Swooosh!');
}
- 19.4 Đặt khoảng trắng xung quanh các toán tử. eslint:
space-infix-opsjscs:requireSpaceBeforeBinaryOperators,requireSpaceAfterBinaryOperators
// bad
const x=y+5;
// good
const x = y + 5;
- 19.5 Kết thúc tệp bằng một ký tự dòng mới (newline character).
// bad
(function (global) {
// ...stuff...
})(this);
// bad
(function (global) {
// ...stuff...
})(this);↵
↵
// good
(function (global) {
// ...stuff...
})(this);↵
- 19.6 Sử dụng thụt đầu dòng khi thực hiện chuỗi phương thức dài (hơn 2 chuỗi phương thức). Sử dụng dấu chấm ở đầu, nhấn mạnh rằng dòng đó là một cuộc gọi phương thức, không phải là một câu lệnh mới. eslint:
newline-per-chained-callno-whitespace-before-property
// bad
$('#items').find('.selected').highlight().end().find('.open').updateCount();
// bad
$('#items').
find('.selected').
highlight().
end().
find('.open').
updateCount();
// good
$('#items')
.find('.selected')
.highlight()
.end()
.find('.open')
.updateCount();
// bad
const leds = stage.selectAll('.led').data(data).enter().append('svg:svg').classed('led', true)
.attr('width', (radius + margin) * 2).append('svg:g')
.attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')')
.call(tron.led);
// good
const leds = stage.selectAll('.led')
.data(data)
.enter().append('svg:svg')
.classed('led', true)
.attr('width', (radius + margin) * 2)
.append('svg:g')
.attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')')
.call(tron.led);
// good
const leds = stage.selectAll('.led').data(data);
- 19.7 Để một dòng trống sau các khối và trước câu lệnh tiếp theo. jscs:
requirePaddingNewLinesAfterBlocks
// bad
if (foo) {
return bar;
}
return baz;
// good
if (foo) {
return bar;
}
return baz;
// bad
const obj = {
foo() {
},
bar() {
},
};
return obj;
// good
const obj = {
foo() {
},
bar() {
},
};
return obj;
// bad
const arr = [
function foo() {
},
function bar() {
},
];
return arr;
// good
const arr = [
function foo() {
},
function bar() {
},
];
return arr;
- 19.8 Đừng đệm các khối của bạn bằng các dòng trống. eslint:
padded-blocksjscs:disallowPaddingNewlinesInBlocks
// bad
function bar() {
console.log(foo);
}
// also bad
if (baz) {
console.log(qux);
} else {
console.log(foo);
}
// good
function bar() {
console.log(foo);
}
// good
if (baz) {
console.log(qux);
} else {
console.log(foo);
}
- 19.9 Đừng thêm khoảng trắng bên trong dấu ngoặc đơn. eslint:
space-in-parensjscs:disallowSpacesInsideParentheses
// bad
function bar( foo ) {
return foo;
}
// good
function bar(foo) {
return foo;
}
// bad
if ( foo ) {
console.log(foo);
}
// good
if (foo) {
console.log(foo);
}
- 19.10 Đừng thêm khoảng trắng bên trong dấu ngoặc vuông. eslint:
array-bracket-spacingjscs:disallowSpacesInsideArrayBrackets
// bad
const foo = [ 1, 2, 3 ];
console.log(foo[ 0 ]);
// good
const foo = [1, 2, 3];
console.log(foo[0]);
- 19.11 Thêm khoảng trắng bên trong dấu ngoặc nhọn. eslint:
object-curly-spacingjscs: [disallowSpacesInsideObjectBrackets](http://jscs.info/rule/
// bad
const foo = {clark: 'kent'};
// good
const foo = { clark: 'kent' };
- 19.12 Tránh các dòng dài quá 100 ký tự (bao gồm cả khoảng trắng). eslint:
max-lenjscs:maximumLineLength
Tại sao? Điều này đảm bảo khả năng đọc và khả năng bảo trì.
// bad
const foo = 'Whatever national crop flips the window. The cartoon reverts within the screw. Whatever wizard constrains a helpful ally. The counterpart ascends!';
// bad
$.ajax({ method: 'POST', url: 'https://airbnb.com/', data: { name: 'John' } }).done(() => console.log('Congratulations!')).fail(() => console.log('You have failed this city.'));
// good
const foo = 'Whatever national crop flips the window. The cartoon reverts within the screw. ' +
'Whatever wizard constrains a helpful ally. The counterpart ascends!';
// good
$.ajax({
method: 'POST',
url: 'https://airbnb.com/',
data: { name: 'John' },
})
.done(() => console.log('Congratulations!'))
.fail(() => console.log('You have failed this city.'));
Dấu phẩy (Commas)
- 20.1 Dấu phẩy đầu dòng: Không. eslint:
comma-stylejscs:requireCommaBeforeLineBreak
// bad
const story = [
once
, upon
, aTime
];
// good
const story = [
once,
upon,
aTime,
];
// bad
const hero = {
firstName: 'Ada'
, lastName: 'Lovelace'
, birthYear: 1815
, superPower: 'computers'
};
// good
const hero = {
firstName: 'Ada',
lastName: 'Lovelace',
birthYear: 1815,
superPower: 'computers',
};
- 20.2 Dấu phẩy cuối cùng bổ sung (trailing comma): Có. eslint:
comma-danglejscs:requireTrailingComma
Tại sao? Điều này dẫn đến chênh lệch git rõ ràng hơn. Ngoài ra, các bộ chuyển đổi mã nguồn (transpilers) như Babel sẽ loại bỏ dấu phẩy cuối cùng bổ sung trong mã đã chuyển đổi, có nghĩa là bạn không phải lo lắng về vấn đề dấu phẩy cuối cùng trong các trình duyệt cũ.
// bad - git diff without trailing comma
const hero = {
firstName: 'Florence',
lastName: 'Nightingale'
+ lastName: 'Nightingale',
+ inventorOf: ['coxcomb graph', 'modern nursing']
};
// good - git diff with trailing comma
const hero = {
firstName: 'Florence',
lastName: 'Nightingale',
+ inventorOf: ['coxcomb chart', 'modern nursing'],
};
// bad
const hero = {
firstName: 'Dana',
lastName: 'Scully'
};
const heroes = [
'Batman',
'Superman'
];
// good
const hero = {
firstName: 'Dana',
lastName: 'Scully',
};
const heroes = [
'Batman',
'Superman',
];
Dấu chấm phẩy (Semicolons)
- 21.1 Có. eslint:
semijscs:requireSemicolons
// bad
(function () {
const name = 'Skywalker'
return name
})()
// good
(() => {
const name = 'Skywalker';
return name;
}());
// good (guards against the function becoming an argument when two files with IIFEs are concatenated)
;(() => {
const name = 'Skywalker';
return name;
}());
Ép kiểu & Coercion (Type Casting & Coercion)
// => this.reviewScore = 9;
// bad
const totalScore = this.reviewScore + '';
// good
const totalScore = String(this.reviewScore);
- 22.3 Số (Numbers): Sử dụng
Numberđể ép kiểu vàparseIntluôn đi kèm với cơ số (radix) để phân tích chuỗi. eslint:radix
const inputValue = '4';
// bad
const val = new Number(inputValue);
// bad
const val = +inputValue;
// bad
const val = inputValue >> 0;
// bad
const val = parseInt(inputValue);
// good
const val = Number(inputValue);
// good
const val = parseInt(inputValue, 10);
- 22.4 Nếu vì lý do nào đó bạn đang làm điều gì đó hoang dã và
parseIntlà nút cổ chai của bạn và cần sử dụng Bitshift vì lý do hiệu suất, hãy để lại bình luận giải thích lý do tại sao và bạn đang làm gì.
// good
/**
* parseInt was the reason my code was slow.
* Bitshifting the String to coerce it to a
* Number made it a lot faster.
*/
const val = inputValue >> 0;
- 22.5 Lưu ý: Hãy cẩn thận khi sử dụng các phép toán bitshift. Các số được biểu diễn dưới dạng giá trị 64-bit, nhưng các phép toán bitshift luôn trả về số nguyên 32-bit (nguồn). Bitshift có thể dẫn đến hành vi không mong muốn đối với các giá trị số nguyên lớn hơn 32 bit. Thảo luận. Số nguyên có dấu 32-bit lớn nhất là 2,147,483,647:
2147483647 >> 0 //=> 2147483647
2147483648 >> 0 //=> -2147483648
2147483649 >> 0 //=> -2147483647
- 22.6 Booleans:
const age = 0;
// bad
const hasAge = new Boolean(age);
// good
const hasAge = Boolean(age);
// good
const hasAge = !!age;
Quy ước đặt tên (Naming Conventions)
- 23.1 Tránh tên biến một chữ cái. Hãy mô tả với việc đặt tên của bạn.
// bad
function q() {
// ...stuff...
}
// good
function query() {
// ..stuff..
}
- 23.2 Sử dụng camelCase khi đặt tên đối tượng, hàm và các thể hiện (instances). eslint:
camelcasejscs:requireCamelCaseOrUpperCaseIdentifiers
// bad
const OBJEcttsssss = {};
const this_is_my_object = {};
function c() {}
// good
const thisIsMyObject = {};
function thisIsMyFunction() {}
- 23.3 Chỉ sử dụng PascalCase khi đặt tên constructor hoặc lớp (class). eslint:
new-capjscs:requireCapitalizedConstructors
// bad
function user(options) {
this.name = options.name;
}
const bad = new user({
name: 'nope',
});
// good
class User {
constructor(options) {
this.name = options.name;
}
}
const good = new User({
name: 'yup',
});
- 23.4 Sử dụng dấu gạch dưới
_ở đầu khi đặt tên các thuộc tính riêng tư (private properties). eslint:no-underscore-danglejscs:disallowDanglingUnderscores
// bad
this.__firstName__ = 'Panda';
this.firstName_ = 'Panda';
// good
this._firstName = 'Panda';
- 23.5 Đừng lưu tham chiếu đến
this. Sử dụng arrow functions hoặc Function#bind. jscs:disallowNodeTypes
// bad
function foo() {
const self = this;
return function () {
console.log(self);
};
}
// bad
function foo() {
const that = this;
return function () {
console.log(that);
};
}
// good
function foo() {
return () => {
console.log(this);
};
}
- 23.6 Nếu tệp của bạn export một lớp duy nhất, tên tệp của bạn phải khớp chính xác với tên lớp đó.
// file contents
class CheckBox {
// ...
}
export default CheckBox;
// in some other file
// bad
import CheckBox from './checkBox';
// bad
import CheckBox from './check_box';
// good
import CheckBox from './CheckBox';
- 23.7 Sử dụng camelCase khi bạn export-default một hàm. Tên tệp của bạn phải giống hệt với tên hàm của bạn.
function makeStyleGuide() {
}
export default makeStyleGuide;
- 23.8 Sử dụng PascalCase khi bạn export một singleton / thư viện hàm / đối tượng trần (bare object).
const AirbnbStyleGuide = {
es6: {
}
};
export default AirbnbStyleGuide;
Accessors
- 24.1 Các hàm truy cập (Accessor functions) cho các thuộc tính là không bắt buộc.
- 24.2 Đừng sử dụng JavaScript getters/setters vì chúng gây ra các tác dụng phụ không mong muốn và khó kiểm thử, bảo trì và lý luận. Thay vào đó, nếu bạn tạo các hàm truy cập, hãy sử dụng getVal() và setVal(‘hello’).
// bad
dragon.age();
// good
dragon.getAge();
// bad
dragon.age(25);
// good
dragon.setAge(25);
- 24.3 Nếu thuộc tính là boolean, hãy sử dụng
isVal()hoặchasVal().
// bad
if (!dragon.age()) {
return false;
}
// good
if (!dragon.hasAge()) {
return false;
}
- 24.4 Không sao khi tạo các hàm get() và set(), nhưng hãy nhất quán.
class Jedi {
constructor(options = {}) {
const lightsaber = options.lightsaber || 'blue';
this.set('lightsaber', lightsaber);
}
set(key, val) {
this[key] = val;
}
get(key) {
return this[key];
}
}
Sự kiện (Events)
- 25.1 Khi đính kèm dữ liệu (payloads) vào các sự kiện (cho dù là sự kiện DOM hoặc thứ gì đó độc quyền hơn như sự kiện Backbone), hãy truyền một mã băm (hash) thay vì một giá trị thô. Điều này cho phép một người đóng góp tiếp theo thêm nhiều dữ liệu hơn vào payload sự kiện mà không cần tìm và cập nhật mọi trình xử lý cho sự kiện. Ví dụ, thay vì:
// bad
$(this).trigger('listingUpdated', listing.id);
...
$(this).on('listingUpdated', (e, listingId) => {
// do something with listingId
});
thích:
// good
$(this).trigger('listingUpdated', { listingId: listing.id });
...
$(this).on('listingUpdated', (e, data) => {
// do something with data.listingId
});
jQuery
- 26.1 Tiền tố các đối tượng jQuery với
$. jscs:requireDollarBeforejQueryAssignment
// bad
const sidebar = $('.sidebar');
// good
const $sidebar = $('.sidebar');
// good
const $sidebarBtn = $('.sidebar-btn');
- 26.2 Cache các truy vấn jQuery (jQuery lookups).
// bad
function setSidebar() {
$('.sidebar').hide();
// ...stuff...
$('.sidebar').css({
'background-color': 'pink'
});
}
// good
function setSidebar() {
const $sidebar = $('.sidebar');
$sidebar.hide();
// ...stuff...
$sidebar.css({
'background-color': 'pink'
});
}
- 26.3 Đối với các truy vấn DOM, sử dụng Cascading
$('.sidebar ul')hoặc parent > child$('.sidebar > ul'). jsPerf - 26.4 Sử dụng
findvới phạm vi truy vấn đối tượng jQuery.
// bad
$('ul', '.sidebar').hide();
// bad
$('.sidebar').find('ul').hide();
// good
$('.sidebar ul').hide();
// good
$('.sidebar > ul').hide();
// good
$sidebar.find('ul').hide();
Tương thích ECMAScript 5 (ECMAScript 5 Compatibility)
- 27.1 Tham khảo Bảng tươg thích ES5 của Kangax.
Phong cách ECMAScript 6 (ECMAScript 6 Styles)
- 28.1 Đây là một tập hợp các liên kết đến các tính năng ES6 khác nhau.
- Arrow Functions
- Classes
- Object Shorthand
- Object Concise
- Computed Object Properties
- Template Strings
- Destructuring
- Default Parameters
- Rest
- Array Spreads
- Let and Const
- Iterators and Generators
- Modules
Thư viện chuẩn (Standard Library)
Thư viện chuẩn chứa một số tiện ích bị hỏng nhưng để lại vì lý do kế thừa.
- 29.1 Sử dụng
Number.isNaNthay vìisNaN.
Tại sao?
isNaNép buộc các giá trị không phải số thành số, trả về true cho bất cứ thứ gì ép buộc thành NaN. Nếu hành vi này là mong muốn, hãy làm cho nó rõ ràng.
// bad
isNaN('1.2'); // false
isNaN('1.2.3'); // true
// good
Number.isNaN('1.2.3'); // false
Number.isNaN(Number('1.2.3')); // true
- 29.2 Sử dụng
Number.isFinitethay vìisFinite.
Tại sao?
isFiniteép buộc các giá trị không phải số thành đố, trả về true cho bất cứ thứ gì ép buộc thành một số hữu hạn. Nếu hành vi này là mong muốn, hãy làm cho nó rõ ràng.
// bad
isFinite('2e3'); // true
// good
Number.isFinite('2e3'); // false
Number.isFinite(parseInt('2e3', 10)); // true
Kiểm thử (Testing)
- 30.1 Có.
function foo() {
return true;
}
- 30.2 Không, nghiêm túc đấy:
- Cho dù bạn sử dụng khung kiểm thử nào, bạn cũng nên viết các bài kiểm tra!
- Cố gắng viết nhiều hàm thuần túy nhỏ (pure functions), và tối thiểu hóa sự thay đổi (mutation).
- Hãy cẩn thận với stubs và mocks - chúng có thể làm cho các bài kiểm tra của bạn dễ vỡ hơn.
- Chúng tôi chủ yếu sử dụng
mochatại Airbnb.tapecũng được sử dụng thỉnh thoảng cho các mô-đun nhỏ, riêng biệt. - 100% độ bao phủ kiểm thử là một mục tiêu tốt để phấn đấu, ngay cả khi nó không phải lúc nào cũng thực tế để đạt được.
- Bất cứ khi nào bạn sửa lỗi, hãy viết bài kiểm tra hồi quy (regression test). Một lỗi được sửa mà không có bài kiểm tra hồi quy gần như chắc chắn sẽ bị hỏng lại trong tương lai.
Hiệu suất (Performance)
- On Layout & Web Performance
- String vs Array Concat
- Try/Catch Cost In a Loop
- Bang Function
- jQuery Find vs Context, Selector
- innerHTML vs textContent for script text
- Long String Concatenation
- Loading…
Tài nguyên (Resources)
Học ES6
- Draft ECMA 2015 (ES6) Spec
- ExploringJS
- ES6 Compatibility Table
- Comprehensive Overview of ES6 Features
Nên đọc
Công cụ
- Code Style Linters
Các hướng dẫn phong cách khác
- Google JavaScript Style Guide
- jQuery Core Style Guidelines
- Principles of Writing Consistent, Idiomatic JavaScript
Phong cách khác
- Naming this in nested functions - Christian Johansen
- Conditional Callbacks - Ross Allen
- Popular JavaScript Coding Conventions on Github - JeongHoon Byun
- Multiple var statements in JavaScript, not superfluous - Ben Alman
Đọc thêm
- Understanding JavaScript Closures - Angus Croll
- Basic JavaScript for the impatient programmer - Dr. Axel Rauschmayer
- You Might Not Need jQuery - Zack Bloom & Adam Schwartz
- ES6 Features - Luke Hoban
- Frontend Guidelines - Benjamin De Cock
Sách
- JavaScript: The Good Parts - Douglas Crockford
- JavaScript Patterns - Stoyan Stefanov
- Pro JavaScript Design Patterns - Ross Harmes and Dustin Diaz
- High Performance Web Sites: Essential Knowledge for Front-End Engineers - Steve Souders
- Maintainable JavaScript - Nicholas C. Zakas
- JavaScript Web Applications - Alex MacCaw
- Pro JavaScript Techniques - John Resig
- Smashing Node.js: JavaScript Everywhere - Guillermo Rauch
- Secrets of the JavaScript Ninja - John Resig and Bear Bibeault
- Human JavaScript - Henrik Joreteg
- Superhero.js - Kim Joar Bekkelund, Mads Mobæk, & Olav Bjorkoy
- JSBooks - Julien Bouquillon
- Third Party JavaScript - Ben Vinegar and Anton Kovalyov
- Effective JavaScript: 68 Specific Ways to Harness the Power of JavaScript - David Herman
- Eloquent JavaScript - Marijn Haverbeke
- You Don’t Know JS: ES6 & Beyond - Kyle Simpson
Blogs
- DailyJS
- JavaScript Weekly
- JavaScript, JavaScript…
- Bocoup Weblog
- Adequately Good
- NCZOnline
- Perfection Kills
- Ben Alman
- Dmitry Baranovskiy
- Dustin Diaz
- nettuts
Podcasts
Trong thế giới thực (In the Wild)
Đây là danh sách các tổ chức sử dụng hướng dẫn phong cách này. Gửi cho chúng tôi một pull request hoặc một issue và chúng tôi sẽ thêm bạn vào danh sách.
- Aan Zee: AanZee/javascript
- Adult Swim: adult-swim/javascript
- Airbnb: airbnb/javascript
- Apartmint: apartmint/javascript
- Avalara: avalara/javascript
- Avant: avantcredit/javascript
- Billabong: billabong/javascript
- Bisk: bisk/javascript
- Blendle: blendle/javascript
- Brainshark: brainshark/javascript
- ComparaOnline: comparaonline/javascript
- Compass Learning: compasslearning/javascript-style-guide
- DailyMotion: dailymotion/javascript
- Digitpaint digitpaint/javascript
- Ecosia: ecosia/javascript
- Evernote: evernote/javascript-style-guide
- Evolution Gaming: evolution-gaming/javascript
- ExactTarget: ExactTarget/javascript
- Expensify Expensify/Style-Guide
- Flexberry: Flexberry/javascript-style-guide
- Gawker Media: gawkermedia/javascript
- General Electric: GeneralElectric/javascript
- GoodData: gooddata/gdc-js-style
- Grooveshark: grooveshark/javascript
- How About We: howaboutwe/javascript
- Huballin: huballin/javascript
- HubSpot: HubSpot/javascript
- Hyper: hyperoslo/javascript-playbook
- InfoJobs: InfoJobs/JavaScript-Style-Guide
- Intent Media: intentmedia/javascript
- Jam3: Jam3/Javascript-Code-Conventions
- JeopardyBot: kesne/jeopardy-bot
- JSSolutions: JSSolutions/javascript
- Kinetica Solutions: kinetica/javascript
- Mighty Spring: mightyspring/javascript
- MinnPost: MinnPost/javascript
- MitocGroup: MitocGroup/javascript
- ModCloth: modcloth/javascript
- Money Advice Service: moneyadviceservice/javascript
- Muber: muber/javascript
- National Geographic: natgeo/javascript
- National Park Service: nationalparkservice/javascript
- Nimbl3: nimbl3/javascript
- Orion Health: orionhealth/javascript
- OutBoxSoft: OutBoxSoft/javascript
- Peerby: Peerby/javascript
- Razorfish: razorfish/javascript-style-guide
- reddit: reddit/styleguide/javascript
- React: /facebook/react/blob/master/CONTRIBUTING.md#style-guide
- REI: reidev/js-style-guide
- Ripple: ripple/javascript-style-guide
- SeekingAlpha: seekingalpha/javascript-style-guide
- Shutterfly: shutterfly/javascript
- Springload: springload/javascript
- StudentSphere: studentsphere/javascript
- Target: target/javascript
- TheLadders: TheLadders/javascript
- T4R Technology: T4R-Technology/javascript
- VoxFeed: VoxFeed/javascript-style-guide
- WeBox Studio: weboxstudio/javascript
- Weggo: Weggo/javascript
- Zillow: zillow/javascript
- ZocDoc: ZocDoc/javascript
Bản dịch (Translation)
Hướng dẫn phong cách này cũng có sẵn trong các ngôn ngữ khác:
Brazilian Portuguese: armoucar/javascript-style-guide
Bulgarian: borislavvv/javascript
Catalan: fpmweb/javascript-style-guide
Chinese (Simplified): sivan/javascript-style-guide
Chinese (Traditional): jigsawye/javascript
French: nmussy/javascript-style-guide
German: timofurrer/javascript-style-guide
Italian: sinkswim/javascript-style-guide
Japanese: mitsuruog/javacript-style-guide
Korean: tipjs/javascript-style-guide
Polish: mjurczyk/javascript
Russian: uprock/javascript
Spanish: paolocarrasco/javascript-style-guide
Vietnamese: f1remoon/javascript-style-guide
Hướng dẫn về Hướng dẫn kiểu JavaScript (The JavaScript Style Guide Guide)
Trò chuyện với chúng tôi về JavaScript
- Tìm chúng tôi trên gitter.
Những người đóng góp (Contributors)
Giấy phép (License)
(The MIT License)
Copyright (c) 2014-2016 Airbnb
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ‘Software’), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Amendments
Chúng tôi khuyến khích bạn fork hướng dẫn này và thay đổi các quy tắc để phù hợp với hướng dẫn phong cách của nhóm bạn. Dưới đây, bạn có thể liệt kê một số sửa đổi cho hướng dẫn phong cách. Điều này cho phép bạn định kỳ cập nhật hướng dẫn phong cách của mình mà không cần phải giải quyết xung đột hợp nhất (merge conflicts).