Hướng dẫn Airbnb JavaScript Style Guide
Những nội dung của bài viết gồm có:
- Types
- References
- Objects
- Arrays
- Destructuring
- Strings
- Functions
- Arrow Functions
- Constructors
- Modules
- Iterators and Generators
- Properties
- Variables
- Hoisting
- Comparison Operators & Equality
- Blocks
- Comments
- Whitespace
- Commas
- Semicolons
- Type Casting & Coercion
- Naming Conventions
- Accessors
- Events
- jQuery
- ECMAScript 5 Compatibility
- ECMAScript 6 Styles
- Testing
- Performance
- Resources
- In the Wild
- Translation
- The JavaScript Style Guide Guide
- Chat With Us About JavaScript
- Contributors
- License
Types
- 1.1 Primitives: Khi bạn truy cập các kiểu dữ liệu nguyên thủy, bạn làm việc trực tiếp với giá trị của biến (tham trị).
string
number
boolean
null
undefined
1234567const foo = 1;let bar = foo;bar = 9;console.log(foo, bar); // => 1, 9 - 1.2 Complex: Khi bạn làm việc với các kiểu dữ liệu phức tạp, bạn làm việc gián tiếp với giá trị của biến thông qua địa chỉ vùng nhớ của nó (tham chiếu).
object
array
function
1234567const foo = [1, 2];const bar = foo;bar[0] = 9;console.log(foo[0], bar[0]); // => 9, 9
References
- 2.1 Sử dụng
const
cho khai báo hằng; tránh sử dụngvar
.
> Khi sử dụng cost
chúng ta không thể thay đổi giá trị của nó. Điều này giúp chúng ta ngăn chặn các lỗi có thể xảy ra
1 2 3 4 5 6 7 8 9 10 | ```javascript // Không tốt var a = 1; var b = 2; // Tốt const a = 1; const b = 2; |
- 2.2 Để thay đổi các thông số, sử dụng
let
và tránh sử dụngvar
.
> Vì let
nó chỉ có nghĩa trong một phạm vi nhất định (Block-Scope), không giống như var
có nghĩa trong một hàm (Funciton-Scope).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | // Không tốt var count = 1; if (true) { count += 1; } // Tốt. let count = 1; if (true) { count += 1; } |
- 2.3
let
vàconst
có giá trị trong một phạm vi nhất định (Block-Scope).1234567891011{let a = 1;const b = 1;}console.log(a); // ReferenceError Khi ngoài phạm vị của biến sẽ không thể sử dụng biến đó.console.log(b); // ReferenceError Khi ngoài phạm vị của biến sẽ không thể sử dụng biến đó.
Objects
- 3.1 Dùng
{}
để tạo một đối tượng.1234567// Không tốtconst item = new Object();// Tốtconst item = {}; - 3.2 Không sử dụng các từ khoá mặc định để làm thuộc tính.12345678910111213141516171819// Không tốtconst superman = {default: { clark: 'kent' },private: true,};// Tốtconst superman = {defaults: { clark: 'kent' },hidden: true,};
- 3.3 Dùng những từ ngữ đồng nghĩa với thuật ngử dành riêng.1234567891011121314151617181920212223// Không tốtconst superman = {class: 'alien', // class từ khoá của hệ thống};// Không tốtconst superman = {klass: 'alien', klass không có ỹ nghĩa gì trong trường hợp này};// Tốtconst superman = {type: 'alien',};
- 3.4 Sử dụng tên thuộc tính khi tạo đối tượng với tên thuộc tính động.
> Việc này giúp chúng ta định nghĩa tất cả các thuộc tính của đối tượng một lần duy nhất.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | function getKey(k) { return `a key named ${k}`; } // Không tốt const obj = { id: 5, name: 'San Francisco', }; obj[getKey('enabled')] = true; // const obj = { id: 5, name: 'San Francisco', [getKey('enabled')]: true, }; |
- 3.5 Sử dụng cách khai báo phương thức rút ngắn (Property Value Shorthand).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | // Không tốt const atom = { value: 1, addValue: function (value) { return atom.value + value; }, }; // Tốt const atom = { value: 1, addValue(value) { return atom.value + value; }, }; |
- 3.6 Sử dụng cách khai báo rút ngắn cho thuộc tính (Object Method.
> Cách viết ngắn gọn và dể hiểu hơn.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | const lukeSkywalker = 'Luke Skywalker'; // bad const obj = { lukeSkywalker: lukeSkywalker, }; // good const obj = { lukeSkywalker, }; |
- 3.7 Gom nhóm các thuộc tính được khai báo rút ngắn đặt lên đầu của mỗi đối tượng.123456789101112131415161718192021222324252627282930313233343536373839const anakinSkywalker = 'Anakin Skywalker';const lukeSkywalker = 'Luke Skywalker';// Không tốtconst obj = {episodeOne: 1,twoJediWalkIntoACantina: 2,lukeSkywalker,episodeThree: 3,mayTheFourth: 4,anakinSkywalker,};// Tốtconst obj = {lukeSkywalker, // Thuộc tính được khai báo ngắn gọnanakinSkywalker, // Thuộc tính được khai báo ngắn gọnepisodeOne: 1,twoJediWalkIntoACantina: 2,episodeThree: 3,mayTheFourth: 4,};
Arrays
- 4.1 Sử dụng
[]
để khai báo một mảng.1234567// Không tốtconst items = new Array();// Tốtconst items = []; - 4.2 Dùng
Array#push
để thêm một phần từ vào mảng thay vì thêm trực tiếp.123456789const someStack = [];// Không tốtsomeStack[someStack.length] = 'abracadabra';// TốtsomeStack.push('abracadabra');
- 4.3 Dùng
...
(Spreads) để sao chép mảng.// Không tốt123456789101112131415const len = items.length;const itemsCopy = [];let i;for (i = 0; i < len; i++) {itemsCopy[i] = items[i];}// Tốtconst itemsCopy = [...items]; - 4.4 Dùng
Array#from
để chuyển đổi từ đối tượng sang mảng.123const foo = document.querySelectorAll('.foo');const nodes = Array.from(foo);
Destructuring
- 5.1 Dùng
Destructuring
để chuyển giá trị từng thuộc tính của đối tượng vào một biến.Điều này giúp giảm bớt việc dùng các biến tạm thời để lưu các thuộc tính trong đối tượng.
1234567891011121314151617181920212223242526272829 // Không tốtfunction getFullName(user) {const firstName = user.firstName;const lastName = user.lastName;return ${firstName} ${lastName};}// Tốtfunction getFullName(obj) {const { firstName, lastName } = obj;return ${firstName} ${lastName};}// Tốt nhấtfunction getFullName({ firstName, lastName }) {return ${firstName} ${lastName};}
- 5.2 Dùng
destructuring
cho mảng.
1234567891011 const arr = [1, 2, 3, 4];// Không tốtconst first = arr[0];const second = arr[1];// Tốtconst [first, second] = arr;
- 5.3 Dùng
destructuring
cho nhiều giá trị trả về, không dùngdestructuring
array.
> Cách dùng này giúp chúng ta có thể thêm một thuộc tính mới hoặc sắp xếp thứ tự trả về mà không gây ảnh hưởng cho các hàm khác.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | // Không tốt function processInput(input) { // then a miracle occurs return [left, right, top, bottom]; } // the caller needs to think about the order of return data const [left, __, top] = processInput(input); // Tốt function processInput(input) { // then a miracle occurs return { left, right, top, bottom }; } // the caller selects only the data they need const { left, right } = processInput(input); |
Strings
- 6.1 Sử dụng dầu nháy đơn
''
đối với chuỗi.1234567// Không tốtconst name = "Capt. Janeway";// Tốtconst name = 'Capt. Janeway'; - 6.2 Khi chuỗi dài hơn 100 kí tự nên chia nhiều dòng và nối chuỗi đó lại.
- 6.3 Ghi chú: Việc áp dụng nối chuỗi nhiều sẽ gây ảnh hưởng tới hiệu năng. jsPerf & Thảo luận.123456789101112131415161718192021// Không tốtconst 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.';// Không tốtconst errorMessage = 'This is a super long error that was thrown becauseof Batman. When you stop to think about how Batman had anything to dowith this, you would get nowherefast.';// Tốtconst 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 Dùng
template
thay vì dùng cách nối chuỗi.
> Dùng template
sẽ giúp chúng ta dể đọc, cú pháp ngắn gọn.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | // Không tốt function sayHi(name) { return 'How are you, ' + name + '?'; } // Không tốt function sayHi(name) { return ['How are you, ', name, '?'].join(); } // Tốt function sayHi(name) { return `How are you, ${name}?`; } |
- 6.5 Không bao giờ sử dụng
eval()
cho chuỗi.
Functions
- 7.1 Sử dụng
Function declarations
thay vìFunction expressions
.
> Function declarations được đặt tên rõ ràng, do đó có thể xác định nó ở call stacks
. Luôn luôn dùng Arrow Functions với Function expressions
.
1 2 3 4 5 6 7 8 9 10 | // không tốt const foo = function () { }; // Tốt function foo() { } |
- 7.2 Function expressions:1234567// immediately-invoked function expression (IIFE)(() => {console.log('Welcome to the Internet. Please follow me.');})();
- 7.3 Không được khai báo một hàm khi sử dụng các câu điều kiện (if, while, …). Thay vào đó lưu hàm vào một biến cụ thể.
- 7.4 Ghi chú: ECMA-262 định nghĩa
block
như là danh sách các câu lệnh. Read ECMA-262’s note on this issue.12345678910111213141516171819202122232425// Không tốtif (currentUser) {function test() {console.log('Nope.');}}// Tốtlet test;if (currentUser) {test = () => {console.log('Yup.');};} - 7.5 Không được khai báo các tham số của hàm trùng với
arguments
.123456789101112131415// Không tốtfunction nope(name, options, arguments) {// ...stuff...}// Tốtfunction yup(name, options, args) {// ...stuff...}
7.6 Không được dùng arguments
, dùng ...
(Spreads) thay thế.
> ...
sẽ chuyển các arguments
thành một mảng các giá trị.
1 2 3 4 5 6 7 8 9 10 11 12 | ```// Không tốt function concatenateAll() { const args = Array.prototype.slice.call(arguments); return args.join(''); } // Tốt function concatenateAll(...args) { return args.join(''); } ``` |
- 7.7 Sử dụng cách truyền tham số mặc định.“
javascript
12345678910111213141516171819202122232425262728293031// Không tốt, không bao giờ áp dụng kiểu nàyfunction handleThings(opts) {opts = opts || {};// ...}// Không tốtfunction handleThings(opts) {if (opts === void 0) {opts = {};}// ...}// Tốtfunction handleThings(opts = {}) {// ...} - 7.8 Tránh đặt nó là các thông số mặc định.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | var b = 1; // Không tốt function count(a = b++) { console.log(a); } count(); // 1 count(); // 2 count(3); // Nó được định nghĩa như là tham số thứ ba, mặc định sẽ không được thực thi (= b++ ไม่ถูกเรียก) count(); // 3 |
- 7.9 Luôn đặt tham số mặc định ở cuối.“javascript123456789101112131415// Không tốtfunction handleThings(opts = {}, name) {// ...}// Tốtfunction handleThings(name, opts = {}) {// ...}
- 7.10 Không bao giờ dùng cách khơi tạo một hàm bằng cách dùng
new Function
.
1 2 3 4 5 6 7 8 9 | > Cách này đồng nghĩa với việc dùng eval(). // Không tốt var add = new Function('a', 'b', 'return a + b'); // Không tốt var subtract = Function('a', 'b', 'return a - b'); |
Arrow Functions
- 8.1 Một khi bạn sử dụng
Function expressions
(Anonymous function
), thì nên dùngArrow Functions
hoặc=>
.“javascript
12345678910111213141516171819// Không tốt[1, 2, 3].map(function (x) {const y = x + 1;return x * y;});// Tốt[1, 2, 3].map((x) => {const y = x + 1;return x * y;}); - 8.2 Nếu chỉ một câu lệnh tính toán thì có thể không dùng
{}
, nhưng khi có nhiều câu lệnh thì nên dùng{}
và dùngreturn
để trả về kết quá cuối cùng.1234567891011121314151617181920212223// Tốt[1, 2, 3].map(number => A string containing the ${number}.);// Không tốt[1, 2, 3].map(number => {const nextNumber = number + 1;A string containing the ${nextNumber}.;});// Tốt[1, 2, 3].map(number => {const nextNumber = number + 1;return A string containing the ${nextNumber}.;}); - 8.3 Khi có nhiều chuỗi có nhiều dòng nên bao chuỗi đó trong dấu
()
.“js12345678910111213141516171819// Không tốt[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.');// Tốt[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 trong hàm chỉ có một câu lệnh duy nhất có thể không cần dùng
()
.“js
1234567// Tốt[1, 2, 3].map(x => x * x);// Tốt[1, 2, 3].reduce((y, x) => x + y);
Constructors
- 9.1 Luôn luôn dùng
class
. Không nên dùngprototype
.“javascript12345678910111213141516171819202122232425262728293031323334353637// Không tốtfunction Queue(contents = []) {this._queue = [...contents];}Queue.prototype.pop = function() {const value = this._queue[0];this._queue.splice(0, 1);return value;}// Tốtclass 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
để tạo một lớp kế thừa.12345678910111213141516171819202122232425262728// Không tốtconst inherits = require('inherits');function PeekableQueue(contents) {Queue.apply(this, contents);}inherits(PeekableQueue, Queue);PeekableQueue.prototype.peek = function() {return this._queue[0];}// Tốtclass PeekableQueue extends Queue {peek() {return this._queue[0];}} - 9.3 Phương thức có thể trả về
this
.1234567891011121314151617181920212223242526272829303132333435363738394041424344454647// Không tốtJedi.prototype.jump = function() {this.jumping = true;return true;};Jedi.prototype.setHeight = function(height) {this.height = height;};const luke = new Jedi();luke.jump(); // => trueluke.setHeight(20); // => undefined// Tốtclass Jedi {jump() {this.jumping = true;return this;}setHeight(height) {this.height = height;return this;}}const luke = new Jedi();luke.jump().setHeight(20); - 9.4 Có thể mở rộng phương thức
toString()
.123456789101112131415161718192021class Jedi {constructor(options = {}) {this.name = options.name || 'no name';}getName() {return this.name;}toString() {return `Jedi - ${this.getName()}`;}}
Modules
- 10.1 Luôn luôn dùng (
import
/export
) khi làm việc vớimodules
thay thế kiểumodules
truyền thốngrequire/module.exports
.“javascript
12345678910111213141516171819202122232425262728// Không tốtconst AirbnbStyleGuide = require('./AirbnbStyleGuide');module.exports = AirbnbStyleGuide.es6;// okimport AirbnbStyleGuide from './AirbnbStyleGuide';export default AirbnbStyleGuide.es6;// Tốt nhấtimport { es6 } from './AirbnbStyleGuide';export default es6;```10.2 Không được dùng wildcard imports.```javascript// Không tốtimport * as AirbnbStyleGuide from './AirbnbStyleGuide';// Tốtimport AirbnbStyleGuide from './AirbnbStyleGuide'; - 10.3 Không được dùng
export
trực tiếp từimport
.
> Bời vì tách import
và export
giúp cho việc đọc dể, có ý nghĩa hơn.
1 2 3 4 5 6 7 8 9 10 11 | // Không tốt // filename es6.js export { es6 as default } from './airbnbStyleGuide'; // Tốt // filename es6.js import { es6 } from './AirbnbStyleGuide'; export default es6; |
Iterators and Generators
- 11.1 Không được dùng vòng lặp thay vào đó dùng
map()
hoặcreduce
thay thếfor-of
.123456789101112131415161718192021222324252627const numbers = [1, 2, 3, 4, 5];// Không tốtlet sum = 0;for (let num of numbers) {sum += num;}sum === 15;// Tốtlet sum = 0;numbers.forEach((num) => sum += num);sum === 15;// Tốt nhấtconst sum = numbers.reduce((total, num) => total + num, 0);sum === 15; - 11.2 Không được dùng
generators
ở thời điểm hiện tại.
> Có lỗi khi chuyển sang ES5
.
Properties
- 12.1 Sử dụng
.
khi truy cập vào một biến.123456789101112131415const luke = {jedi: true,age: 28,};// Không tốtconst isJedi = luke['jedi'];// Tốtconst isJedi = luke.jedi; - 12.2 Sử
[]
để truy cập thuộc tính đối với biến.123456789101112131415const luke = {jedi: true,age: 28,};function getProp(prop) {return luke[prop];}const isJedi = getProp('jedi');
Variables
- 13.1 Luôn luôn sử dụng
const
để khai báo một biến.1234567// Không tốtsuperPower = new SuperPower();// Tốtconst superPower = new SuperPower(); - 13.2 Dùng mỗi
const
cho việc khai báo một biến.123456789101112131415161718192021// Không tốtconst items = getItems(),goSportsTeam = true,dragonball = 'z';// Không tốtconst items = getItems(),goSportsTeam = true;dragonball = 'z';// Tốtconst items = getItems();const goSportsTeam = true;const dragonball = 'z'; - 13.3 Gôm nhóm biến theo
const
s vàlet
s.123456789101112131415161718192021222324252627282930// Không tốtlet i, len, dragonball,items = getItems(),goSportsTeam = true;// Không tốtlet i;const items = getItems();let dragonball;const goSportsTeam = true;let len;// Tốtconst goSportsTeam = true;const items = getItems();let dragonball;let i;let length; - 13.4 Khai báo biến khi cần thiết và đặt chúng ở đúng nơi.
> let
và const
là block scoped
và không phải function scoped.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | // Tốt function() { test(); console.log('doing stuff..'); //..other stuff.. const name = getName(); if (name === 'test') { return false; } return name; } // Không tốt - hàm không cần thiết function(hasName) { const name = getName(); if (!hasName) { return false; } this.setFirstName(name); return true; } // good function(hasName) { if (!hasName) { return false; } const name = getName(); this.setFirstName(name); return true; } |
Hoisting
- 14.1
var
được khai báo trước ở đầu trong pham vi của biến hoặc hàm.const
vàlet
được dùng với một khái niệm mới Temporal Dead Zones (TDZ). typeof is no longer safe.1234567891011121314151617181920212223242526272829303132333435363738394041function example() {console.log(notDefined); // => ReferenceError}// Khai báo một biến sau khai biến đó đã được gọi,// trong trường hợp này biến sẽ không hoistedfunction example() {console.log(declaredButNotAssigned); // => undefinedvar declaredButNotAssigned = true;}// Biến được khai báo ở đầufunction example() {let declaredButNotAssigned;console.log(declaredButNotAssigned); // => undefineddeclaredButNotAssigned = true;}// sử dụng const and letfunction example() {console.log(declaredButNotAssigned); // => throws a ReferenceErrorconsole.log(typeof declaredButNotAssigned); // => throws a ReferenceErrorconst declaredButNotAssigned = true;} - 14.2
Anonymous function
được khai báo bằng một biến.12345678910111213function example() {console.log(anonymous); // => undefinedanonymous(); // => TypeError anonymous is not a functionvar anonymous = function() {console.log('anonymous function expression');};} - 14.3
Named Function expressions
– Việc thông báo này hoạt động bằng tên hàm. Kết quả như ví dụ trước.123456789101112131415161718192021222324252627282930313233function example() {console.log(named); // => undefinednamed(); // => TypeError named is not a functionsuperPower(); // => ReferenceError superPower is not definedvar named = function superPower() {console.log('Flying');};}// Kết quả giống như khi dùng tên hàm// trùng với tên biếnfunction example() {console.log(named); // => undefinednamed(); // => TypeError named is not a functionvar named = function named() {console.log('named');}} - 14.4
Function declarations
– Đối với hàm mà không có các giá trị đầu vào cho biến.1234567891011function example() {superPower(); // => Flyingfunction superPower() {console.log('Flying');}}
Comparison Operators & Equality
- 15.1 Sử dụng
===
và!==
thay cho==
và!=
. - 15.2 Khi dùng
if
các loại đối tượng sẽ được chuyển đổi sang kiểuBoolean
:- Objects chuyển thành true
- Undefined chuyển thành false
- Null chuyển thành false
- Booleans chuyển thành the value of the boolean
- Numbers chuyển thành false Nếu +0, -0, or NaN, ngược lại true
- Strings chuyển thành false Nếu là chuỗi rổng
''
, ngược lại true
1234567if ([0]) {// true// array là object, objects chuyển thành true} - 15.3 Sử dụng ngắn gọn.12345678910111213141516171819202122232425262728293031// Không tốtif (name !== '') {// ...stuff...}// Tốtif (name) {// ...stuff...}// Không tốtif (collection.length > 0) {// ...stuff...}// Tốtif (collection.length) {// ...stuff...}
- 15.4 Truth Equality and JavaScript by Angus Croll.
Blocks
- 16.1 Sử dụng
{}
.1234567891011121314151617181920212223242526272829// Không tốtif (test)return false;// Tốtif (test) return false;// Tốtif (test) {return false;}// Không tốtfunction() { return false; }// Tốtfunction() {return false;} - 16.2 Nếu dùng nhiều câu điều kiện
if
vàelse
, đặtelse
cùng dòng với dấu}
củaif
.1234567891011121314151617181920212223242526272829// Không tốtif (test) {thing1();thing2();}else {thing3();}// Tốtif (test) {thing1();thing2();} else {thing3();}
Comments
- 17.1 Sử dụng
/** ... */
khi cần chú thích nhiều. Nên mô tả đầy đủ nhưtypes
,values
của nhiều tham số, giá trị trả về là gì.123456789101112131415161718192021222324252627282930313233343536373839// Không tốt// make() returns a new element// based on the passed in tag name//// @param {String} tag// @return {Element} elementfunction make(tag) {// ...stuff...return element;}// Tốt/**make() returns a new elementbased on the passed in tag name*@param {String} tag@return {Element} element*/function make(tag) {// ...stuff...return element;} - 17.2 Sử dụng
//
khi chú thích một dòng duy nhất. Cách một dòng đối với câu lệnh phía trước khi sử dụng//
để chú thích.12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849// Không tốtconst active = true; // is current tab// Tốt// is current tabconst active = true;// Tốtfunction getType() {console.log('fetching type...');// set the default type to 'no type'const type = this._type || 'no type';return type;}// Tốtfunction getType() {console.log('fetching type...');// set the default type to 'no type'const type = this._type || 'no type';return type;}// Tốtfunction getType() {// set the default type to 'no type'const type = this._type || 'no type';return type;} - 17.3 Thêm tiền tố phía trước
FIXME
hoặcTODO
để những người trong cùng một team có thể dể đọc hiểu code.FIXME -- need to figure this out
hoặcTODO -- need to implement
. - 17.4 Use
// FIXME:
to annotate problems.123456789101112class Calculator extends Abacus {constructor() {super();// FIXME: shouldn't use a global heretotal = 0;}} - 17.5 Dùng
// TODO:
chú thích cách giải quyết vấn đề.123456789101112class Calculator extends Abacus {constructor() {super();// TODO: total should be configurable by an options paramthis.total = 0;}}
Whitespace
- 18.1 Dùng 2
spaces
thay vì 4.1234567891011121314151617181920212223// Không tốtfunction() {∙∙∙∙const name;}// Tốtfunction() {∙const name;}// Tốtfunction() {∙∙const name;} - 18.2 Thêm dấu cách trước
{
.1234567891011121314151617181920212223242526272829303132333435// Không tốtfunction test(){console.log('test');}// Tốtfunction test() {console.log('test');}// Không tốtdog.set('attr',{age: '1 year',breed: 'Bernese Mountain Dog',});// Tốtdog.set('attr', {age: '1 year',breed: 'Bernese Mountain Dog',}); - 18.3 Thêm 1 khoảng cách sau các câu điều kiện (
if
,while
…). Không nên có khoảng cách trongFunction Arguments
.12345678910111213141516171819202122232425262728293031// Không tốtif(isJedi) {fight ();}// Tốtif (isJedi) {fight();}// Không tốtfunction fight () {console.log ('Swooosh!');}// Tốtfunction fight() {console.log('Swooosh!');} - 18.4 Đối với các phép tính (
+
,-
,*
,/
…) thêm khoảng cách trước và sau các phép tính đó.1234567// Không tốtconst x=y+5;// Tốtconst x = y + 5; - 18.5 Thêm một dòng trống khi
file
đó kết thúc.1234567// Không tốt(function(global) {// ...stuff...})(this);1234567// Không tốt(function(global) {// ...stuff...})(this);1234567// Tốt(function(global) {// ...stuff...})(this); - 18.6 Sử dụng
indentation
khi gọi nhiềumethods
cùng một lúc.“javascript11234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253// Không tốt$('#items').find('.selected').highlight().end().find('.open').updateCount();// Không tốt$('#items').find('.selected').highlight().end().find('.open').updateCount();// Tốt$('#items').find('.selected').highlight().end().find('.open').updateCount();// Không tốtconst leds = stage.selectAll('.led').data(data).enter().append('svg:svg').class('led', true).attr('width', (radius + margin) * 2).append('svg:g').attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')').call(tron.led);// Tốtconst 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); - 18.7 Thêm một dòng trống sau
{}
và bắt đầu một câu lệnh tiếp theo.123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126// Không tốtif (foo) {return bar;}return baz;// Tốtif (foo) {return bar;}return baz;// Không tốtconst obj = {foo() {},bar() {},};return obj;// Tốtconst obj = {foo() {},bar() {},};return obj;// Không tốtconst arr = [function foo() {},function bar() {},];return arr;// Tốtconst arr = [function foo() {},function bar() {},];return arr;```18.8 Trong một blocks không được thêm dòng trống.```javascript// Không tốtfunction bar() {console.log(foo);}// Không tốtif (baz) {console.log(qux);} else {console.log(foo);}// Tốtfunction bar() {console.log(foo);}// Tốtif (baz) {console.log(qux);} else {console.log(foo);}
Commas
- 19.1 Dùng
,
ở đầu: Không123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051// Không tốtconst story = [once, upon, aTime];// Tốtconst story = [once,upon,aTime,];// Không tốtconst hero = {firstName: 'Ada', lastName: 'Lovelace', birthYear: 1815, superPower: 'computers'};// Tốtconst hero = {firstName: 'Ada',lastName: 'Lovelace',birthYear: 1815,superPower: 'computers',}; - 19.2 Thêm
trailing comma
: Yup.1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556// Không tốt - git diff without trailing commaconst hero = {firstName: 'Florence',lastName: 'Nightingale'lastName: 'Nightingale',inventorOf: ['coxcomb graph', 'modern nursing']};// Tốt - git diff with trailing commaconst hero = {firstName: 'Florence',lastName: 'Nightingale',inventorOf: ['coxcomb chart', 'modern nursing'],};// Không tốtconst hero = {firstName: 'Dana',lastName: 'Scully'};const heroes = ['Batman','Superman'];// Tốtconst hero = {firstName: 'Dana',lastName: 'Scully',};const heroes = ['Batman','Superman',];
Semicolons
- 20.1 Yup.1234567891011121314151617181920212223242526272829// Không tốt(function() {const name = 'Skywalker'return name})()// Tốt(() => {const name = 'Skywalker';return name;})();// Tốt (Trong trường hợp này ; để tránh xung đột giữa 2 IIFEs liên tiếp nhau.);(() => {const name = 'Skywalker';return name;})();
Type Casting & Coercion
- 21.1 Áp dụng các kiểu chuyển đổi.
- 21.2 Strings:123456789// => this.reviewScore = 9;// Không tốtconst totalScore = this.reviewScore + '';// Tốtconst totalScore = String(this.reviewScore);
- 21.3 Numbers: Sử dụng
Number
chocasting
vàparseInt
luôn luôn dùng vớiradix
khi chuyển từ chuỗi sang số.12345678910111213141516171819202122232425const inputValue = '4';// Không tốtconst val = new Number(inputValue);// Không tốtconst val = +inputValue;// Không tốtconst val = inputValue >> 0;// Không tốtconst val = parseInt(inputValue);// Tốtconst val = Number(inputValue);// Tốtconst val = parseInt(inputValue, 10); - 21.4 Sử dụng
Bitshift
Lý do, và chú thích đầy đủ khi dùng Bitshift12345678910// Tốt/**parseInt was the reason my code was slow.Bitshifting the String to coerce it to aNumber made it a lot faster.*/const val = inputValue >> 0; - 21.5 Ghi chú: Cẩn thận khi dùng
Bitshift
.Bitshift
luôn trả về 32-bit integer (source). Khi số lớn hơn32 bits
sẽ dẫn đến một số lỗi. Thảo luận. Số lớn nhất32-bit Int
là 2,147,483,647:123452147483647 >> 0 //=> 21474836472147483648 >> 0 //=> -21474836482147483649 >> 0 //=> -2147483647 - 21.6 Booleans:12345678910111213const age = 0;// Không tốtconst hasAge = new Boolean(age);// Tốtconst hasAge = Boolean(age);// Tốtconst hasAge = !!age;
Naming Conventions
- 22.1 Nên mô tả đầy đủ một tên hàm hay biến.123456789101112131415// Không tốtfunction q() {// ...stuff...}// Tốtfunction query() {// ..stuff..}
- 22.2 Dùng cách đặt tên
camelCase
cho đối tượng, biến, hàm, kế thừa …12345678910111213// Không tốtconst OBJEcttsssss = {};const this_is_my_object = {};function c() {}// Tốtconst thisIsMyObject = {};function thisIsMyFunction() {} - 22.3 Dùng kiểu
PascalCase
để đặt tên choClass
hoặcConstructor
.12345678910111213141516171819202122232425262728293031// Không tốtfunction user(options) {this.name = options.name;}const bad = new user({name: 'nope',});// Tốtclass User {constructor(options) {this.name = options.name;}}const good = new User({name: 'yup',}); - 22.4 Dùng
_
ở đầu tên biến khi nó là loạiprivate
.123456789// Không tốtthis.__firstName__ = 'Panda';this.firstName_ = 'Panda';// Tốtthis._firstName = 'Panda'; - 22.5 Sử dụng
Arrow function
(=>
) hoặcFunction#bind
.123456789101112131415161718192021222324252627282930313233343536373839// Không tốtfunction foo() {const self = this;return function() {console.log(self);};}// Không tốtfunction foo() {const that = this;return function() {console.log(that);};}// Tốtfunction foo() {return () => {console.log(this);};} - 22.6 Khi
export
mộtclass
duy nhất, thì tênfile
nên trùng với tênclass
.1234567891011121314151617181920212223// file contentsclass CheckBox {// ...}export default CheckBox;// in some other file// Không tốtimport CheckBox from './checkBox';// Không tốtimport CheckBox from './check_box';// Tốtimport CheckBox from './CheckBox'; - 22.7 Dùng kiểu
camelCase
khiexport
mặc định hàm. Tênfile
và tên hàm nên tương tự nhau.12345function makeStyleGuide() {}export default makeStyleGuide; - 22.8 Dùng kiểu
PascalCase
khiexport
mộtsingleton
/function library
/bare object
.123456789const AirbnbStyleGuide = {es6: {}};export default AirbnbStyleGuide;
Accessors
- 23.1
Accessor functions
cho các thuộc tính không cần thiết. - 23.2 Khi tạo một
accessor functions
sử dụng cấu trúcgetVal()
vàsetVal('hello')
.123456789101112131415// Không tốtdragon.age();// Tốtdragon.getAge();// Không tốtdragon.age(25);// Tốtdragon.setAge(25); - 23.3 Nếu thuộc tính là
boolean
, sử dụngisVal()
vàhasVal()
.“`javascript123456789101112131415// Không tốtif (!dragon.age()) {return false;}// Tốtif (!dragon.hasAge()) {return false;} - 23.4 Có thể ghi đè 2 hàm mặc định
get()
vàset()
, nhưng phải có tính nhất quán.12345678910111213141516171819202122class Jedi {constructor(options = {}) {const lightsaber = options.lightsaber || 'blue';this.set('lightsaber', lightsaber);}set(key, val) {this[key] = val;}get(key) {return this[key];}}
Events
- 24.1 Dùng
hash value
thay vìraw value
khi truyền các tham số vàoevents
.1234567891011// Không tốt$(this).trigger('listingUpdated', listing.id);...$(this).on('listingUpdated', function(e, listingId) {// do something with listingId});prefer:
1234567891011// Tốt$(this).trigger('listingUpdated', { listingId: listing.id });...$(this).on('listingUpdated', function(e, data) {// do something with data.listingId});
jQuery
- 25.1 Thêm tiền tố
$
khi biến đó được tạo ra từjQuery
.1234567891011// Không tốtconst sidebar = $('.sidebar');// Tốtconst $sidebar = $('.sidebar');// Tốtconst $sidebarBtn = $('.sidebar-btn'); - 25.2
Cache jQuery
.123456789101112131415161718192021222324252627282930313233// Không tốtfunction setSidebar() {$('.sidebar').hide();// ...stuff...$('.sidebar').css({'background-color': 'pink'});}// Tốtfunction setSidebar() {const $sidebar = $('.sidebar');$sidebar.hide();// ...stuff...$sidebar.css({'background-color': 'pink'});} - 25.3 Sử dụng kiểu
Cascading
$('.sidebar ul')
hoặc là kiểuparent
>child
$('.sidebar > ul')
. jsPerf - 25.4 Sử dụng
find
.12345678910111213141516171819// Không tốt$('ul', '.sidebar').hide();// Không tốt$('.sidebar').find('ul').hide();// Tốt$('.sidebar ul').hide();// Tốt$('.sidebar > ul').hide();// Tốt$sidebar.find('ul').hide();
ECMAScript 5 Compatibility
- 26.1 Refer to Kangax‘s ES5 compatibility table.
ECMAScript 6 Styles
- 27.1
ES6
Features.
- Arrow Functions
- Classes
- Object Shorthand
- Object Concise
- Object Computed Properties
- Template Strings
- Destructuring
- Default Parameters
- Rest
- Array Spreads
- Let and Const
- Iterators and Generators
- Modules
Testing
- 28.1 Yup.12345function () {return true;}
- 28.2 No, but seriously:
- Luôn luôn viết
test
! - Chia nhỏ hàm.
- Cẩn thận khi dùng
stubs
vàmocks
. mocha
vàtape
được sử dụng ởAirbnb
.- Bảo đảm các
test
đều chạy tốt (100% coverage). - Khi sửa chửa mộ lỗi nào đó nên viết lại
test
.
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
TÀI NGUYÊN
Học ES6
Nên đọc
Tools
- Code Style Linters
Các Styles Guide
khác
- Google JavaScript Style Guide
- jQuery Core Style Guidelines
- Principles of Writing Consistent, Idiomatic JavaScript
Styles
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
In the Wild
Danh sách các trang wed sử dụng style guide
của Airbnb.
- Aan Zee: AanZee/javascript
- Adult Swim: adult-swim/javascript
- Airbnb: airbnb/javascript
- Apartmint: apartmint/javascript
- Avalara: avalara/javascript
- Billabong: billabong/javascript
- Blendle: blendle/javascript
- ComparaOnline: comparaonline/javascript
- Compass Learning: compasslearning/javascript-style-guide
- DailyMotion: dailymotion/javascript
- Digitpaint digitpaint/javascript
- Ecosia: ecosia/javascript
- Evernote: evernote/javascript-style-guide
- 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
- 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
- Peerby: Peerby/javascript
- Razorfish: razorfish/javascript-style-guide
- reddit: reddit/styleguide/javascript
- 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
- Weggo: Weggo/javascript
- Zillow: zillow/javascript
- ZocDoc: ZocDoc/javascript
ITZone via Github
Người thực hiện giangpii