Airbnb JS Style Guide - ECMAScript 6+ (ES 2015+) Styles
Bài đăng này đã không được cập nhật trong 8 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 functiontạo ra mộtfunction,functionnày thực thi trongcontextcủathis, bạn sẽ không còn phải truyềnthisthông qua mộtthatnà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
functionmà 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
functionlạ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
functionchỉ 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
classngắ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
extendscho 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
methodtoString(), 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 constructornếu không khai báo.Constructorrỗng hoặcdelegateđếnparent classlà 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
memberkhai 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
wildcardtrongimport
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
exporttrự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;
import1 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
exportchomutabel bindings
Tại sao? Tuy một số trường hợp có thể dùng, nhưng nên
exportconstthì tốt hợn
// bad
let foo = 3;
export { foo };
// good
const foo = 3;
export { foo };
- Nếu
modulechỉexport1 chỗ thì nên sử dụngexport defaulthơ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
importlên trên cùng
Vì sao? Vì
importcó hiện tượnghoistednên bỏ lên trên cùng để tránh nhữngissuekhô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 syntaxtrong 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