Airbnb JS Style Guide - ECMAScript 6+ (ES 2015+) Styles
Bài đăng này đã không được cập nhật trong 7 năm
Trong quá trình viết JS, nhiều người chắc ai cũng gặp phải nhiều vấn đề về Style có cho JS đặc biệt là chuẩn ECMAScript 6, không biết viết sao cho đúng chuẩn. Trong bài viết này mình sẽ giới thiệu về style viết JS của công ty Airbnb. Mong sau bài viết này mọi người sẽ tìm ra được style chuẩn cho mình để code khoa học, đẹp hơn.
Arrow Functions
- Khi bạn bắt buộc phải sử dụng
function expressions
(truyền vào mộtanonymous function
), hãy sử dụngarrow function notation
.
Vì sao nên?
Arrow function
tạo ra mộtfunction
,function
này thực thi trongcontext
củathis
, bạn sẽ không còn phải truyềnthis
thông qua mộtthat
nào nữa. Cú pháp cũng ngắn gọn hơn.
Vì sao không nên? Nếu bạn có một
function
mà logic quá phức tạp, bạn nên táchfunction
đó ra ngoài sẽ tốt hơ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;
});
- Nếu function chỉ có duy nhất một câu lệnh
return
, không tồn tạiside effects
, thì hãy bỏ{ }
, và bỏ luôn từ khóareturn
. Nếu không hãy giữ{ }
và để lạireturn
Tại sao? Nó tốt hơn cho việc nối nhiều
function
lại với nhau
// 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}.`;
});
// good
[1, 2, 3].map((number, index) => ({
[index]: number,
}));
// No implicit return with side effects
function foo(callback) {
const val = callback();
if (val === true) {
// Do something if callback returns true
}
}
let bool = false;
// bad
foo(() => bool = true);
// good
foo(() => {
bool = true;
});
- Trong trường hợp biểu thức tràn xuống dòng, nên sử dụng
( )
cho dễ đọc.
Tại sao? Người đọc code sẽ biết được đâu là nơi bắt đầu, nơi kết thúc của
function
// bad
['get', 'post', 'put'].map(httpMethod => Object.prototype.hasOwnProperty.call(
httpMagicObjectWithAVeryLongName,
httpMethod,
)
);
// good
['get', 'post', 'put'].map(httpMethod => (
Object.prototype.hasOwnProperty.call(
httpMagicObjectWithAVeryLongName,
httpMethod,
)
));
- Nếu
function
chỉ có 1 tham số duy nhất, hãy bỏ dấu( )
đi, ngược lại hãy luôn sử dụng( )
cho nóan toàn
(hehe)
Vì sao? Bỏ bớt cho code sạch đẹp hơn.
// 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 don’t want it to take up space on the .map line!`
));
// 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;
});
- Tránh việc dễ hiểu nhầm giữa
=>
và toán tử so sánh<=
,>=
// 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 => (item.height > 256 ? item.largeSize : item.smallSize);
// good
const itemHeight = (item) => {
const { height, largeSize, smallSize } = item;
return height > 256 ? largeSize : smallSize;
};
Classes & Constructors
- Luôn luôn sử dụng
class
, tránh việc sử dụngprototype
Tại sao? Cú pháp của
class
ngắn gọn và dễ dàng 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;
}
}
- Sử dụng
extends
cho thừa kế trongJS
Tại sao? Tránh gặp các vấn đề 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];
}
}
- Trong trường hợp muốn sử dụng
chains functions
, hãy sử dụngreturn this
// 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);
- Bạn có thể sử dụng
method
toString()
, những hãy đảm bảo là nó chạy ngon lành, và không xuất hiệnside effects
class Jedi {
constructor(options = {}) {
this.name = options.name || 'no name';
}
getName() {
return this.name;
}
toString() {
return `Jedi - ${this.getName()}`;
}
}
- Mỗi
class
đều có mộtdefault constructor
nếu không khai báo.Constructor
rỗng hoặcdelegate
đếnparent class
là không cần thiết.
// 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';
}
}
- Tránh việc lặp
class member
Tại sao? Nó sẽ luôn luôn lấy
member
khai báo sau, nhưng đôi khi cũng trả về một đốngbug
// bad
class Foo {
bar() { return 1; }
bar() { return 2; }
}
// good
class Foo {
bar() { return 1; }
}
// good
class Foo {
bar() { return 2; }
}
Modules
- Luôn luôn sử dụng
modules
(import/ export
)
Tại sao? Cái nào mới thì mình dùng thôi
// 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;
- Không nên sử dụng
wildcard
trongimport
Tại sao? Nó chắc chắn việc bạn chỉ import 1 lần duy nhất
import * as AirbnbStyleGuide from './AirbnbStyleGuide';
// good
import AirbnbStyleGuide from './AirbnbStyleGuide';
- Không
export
trực tiếp những thứ vừaimport
// bad
// filename es6.js
export { es6 as default } from './AirbnbStyleGuide';
// good
// filename es6.js
import { es6 } from './AirbnbStyleGuide';
export default es6;
import
1 path 1 lần ở 1 nơi thôi
Tại sao? Khó maintain lắm.
// bad
import foo from 'foo';
// … some other imports … //
import { named1, named2 } from 'foo';
// good
import foo, { named1, named2 } from 'foo';
// good
import foo, {
named1,
named2,
} from 'foo';
- Không nên sử dụng
export
chomutabel bindings
Tại sao? Tuy một số trường hợp có thể dùng, nhưng nên
export
const
thì tốt hợn
// bad
let foo = 3;
export { foo };
// good
const foo = 3;
export { foo };
- Nếu
module
chỉexport
1 chỗ thì nên sử dụngexport default
hơn lànamed export
.
Vì sao? Việc này khuyến khích việc tách ra từng
module
, code dễ đọc, bảo trì hơn.
// bad
export function foo() {}
// good
export default function foo() {}
- Luôn luôn bỏ câu lệnh
import
lên trên cùng
Vì sao? Vì
import
có hiện tượnghoisted
nên bỏ lên trên cùng để tránh nhữngissue
không mong muốn.
// bad
import foo from 'foo';
foo.init();
import bar from 'bar';
// good
import foo from 'foo';
import bar from 'bar';
foo.init();
- Với import trên nhiều dòng, thì nên xuống dòng
Vì sao? Dễ đọc hơn
// bad
import {longNameA, longNameB, longNameC, longNameD, longNameE} from 'path';
// good
import {
longNameA,
longNameB,
longNameC,
longNameD,
longNameE,
} from 'path';
- Không cho phép
Webpack syntax
trong việcimport
// bad
import fooSass from 'css!sass!foo.scss';
import barCss from 'style!css!bar.css';
// good
import fooSass from 'foo.scss';
import barCss from 'bar.css';
Tobe continued...
Bài viết được đăng lại trên blog cá nhân mình. https://namtx.github.io
All rights reserved