Airbnb JS Style Guide - ECMAScript 6+ (ES 2015+) Styles
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 ...
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ột anonymous function), hãy sử dụng arrow function notation.
Vì sao nên? Arrow function tạo ra một function, function này thực thi trong context của this, bạn sẽ không còn phải truyền this thông qua một that 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ách function đó 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ại side effects, thì hãy bỏ { }, và bỏ luôn từ khóa return. Nếu không hãy giữ { } và để lại return
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ụng prototype
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ế trong JS
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ụng return 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ện side 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ột default constructor nếu không khai báo. Constructor rỗng hoặc delegate đến parent 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 đống bug
// 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 trong import
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ừa import
// 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 cho mutabel 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ụng export 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ượng hoisted nên bỏ lên trên cùng để tránh những issue 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ệc import
// 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