แนวทางที่สมเหตุสมผลเป็นส่วนใหญ่สำหรับ JavaScript
หมายเหตุ : คู่มือนี้จะถือว่าคุณใช้ Babel และกำหนดให้คุณใช้ babel-preset-airbnb หรือเทียบเท่า นอกจากนี้ยังถือว่าคุณกำลังติดตั้ง shims/polyfills ในแอปของคุณด้วย airbnb-browser-shims หรือเทียบเท่า
คู่มือนี้มีให้ในภาษาอื่นด้วย ดูการแปล
คำแนะนำสไตล์อื่น ๆ
1.1 Primitives : เมื่อคุณเข้าถึงประเภทดั้งเดิม คุณจะทำงานกับค่าของมันโดยตรง
string
number
boolean
null
undefined
symbol
bigint
const foo = 1 ;
let bar = foo ;
bar = 9 ;
console . log ( foo , bar ) ; // => 1, 9
1.2 ซับซ้อน : เมื่อคุณเข้าถึงประเภทที่ซับซ้อน คุณจะอ้างอิงถึงค่าของมัน
object
array
function
const foo = [ 1 , 2 ] ;
const bar = foo ;
bar [ 0 ] = 9 ;
console . log ( foo [ 0 ] , bar [ 0 ] ) ; // => 9, 9
⬆ กลับไปด้านบน
2.1 ใช้ const
สำหรับการอ้างอิงทั้งหมดของคุณ หลีกเลี่ยงการใช้ var
eslint: prefer-const
, no-const-assign
ทำไม เพื่อให้แน่ใจว่าคุณไม่สามารถกำหนดข้อมูลอ้างอิงใหม่ได้ ซึ่งอาจนำไปสู่จุดบกพร่องและโค้ดที่เข้าใจยาก
// bad
var a = 1 ;
var b = 2 ;
// good
const a = 1 ;
const b = 2 ;
2.2 หากคุณต้องกำหนดการอ้างอิงใหม่ ให้ใช้ let
แทน var
eslint: no-var
ทำไม
let
ให้มีการกำหนดขอบเขตแบบบล็อกมากกว่าการกำหนดขอบเขตฟังก์ชันเช่นvar
// bad
var count = 1 ;
if ( true ) {
count += 1 ;
}
// good, use the let.
let count = 1 ;
if ( true ) {
count += 1 ;
}
2.3 โปรดทราบว่าทั้ง let
และ const
มีการกำหนดขอบเขตแบบบล็อก ในขณะที่ var
มีการกำหนดขอบเขตฟังก์ชัน
// const and let only exist in the blocks they are defined in.
{
let a = 1 ;
const b = 1 ;
var c = 1 ;
}
console . log ( a ) ; // ReferenceError
console . log ( b ) ; // ReferenceError
console . log ( c ) ; // Prints 1
ในโค้ดด้านบน คุณจะเห็นว่าการอ้างอิง a
และ b
จะสร้าง ReferenceError ในขณะที่ c
มีตัวเลข นี่เป็นเพราะว่า a
และ b
มีการกำหนดขอบเขตบล็อก ในขณะที่ c
ถูกกำหนดขอบเขตไว้ที่ฟังก์ชันที่มีอยู่
⬆ กลับไปด้านบน
3.1 ใช้ไวยากรณ์ตามตัวอักษรสำหรับการสร้างวัตถุ eslint: no-new-object
// bad
const item = new Object ( ) ;
// good
const item = { } ;
3.2 ใช้ชื่อคุณสมบัติที่คำนวณเมื่อสร้างออบเจ็กต์ด้วยชื่อคุณสมบัติไดนามิก
ทำไม ช่วยให้คุณสามารถกำหนดคุณสมบัติทั้งหมดของวัตถุได้ในที่เดียว
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.3 ใช้ชวเลขวิธีวัตถุ eslint: object-shorthand
// bad
const atom = {
value : 1 ,
addValue : function ( value ) {
return atom . value + value ;
} ,
} ;
// good
const atom = {
value : 1 ,
addValue ( value ) {
return atom . value + value ;
} ,
} ;
3.4 ใช้ชวเลขค่าทรัพย์สิน eslint: object-shorthand
ทำไม มันสั้นกว่าและสื่อความหมาย
const lukeSkywalker = 'Luke Skywalker' ;
// bad
const obj = {
lukeSkywalker : lukeSkywalker ,
} ;
// good
const obj = {
lukeSkywalker ,
} ;
3.5 จัดกลุ่มคุณสมบัติชวเลขของคุณที่จุดเริ่มต้นของการประกาศวัตถุของคุณ
ทำไม ง่ายกว่าที่จะบอกว่าคุณสมบัติใดใช้ชวเลข
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.6 เฉพาะคุณสมบัติคำพูดที่เป็นตัวระบุที่ไม่ถูกต้อง eslint: quote-props
ทำไม โดยทั่วไปแล้ว เราถือว่าการอ่านง่ายขึ้นตามอัตวิสัย ปรับปรุงการเน้นไวยากรณ์และยังปรับให้เหมาะสมได้ง่ายขึ้นด้วยกลไก JS จำนวนมาก
// bad
const bad = {
'foo' : 3 ,
'bar' : 4 ,
'data-blah' : 5 ,
} ;
// good
const good = {
foo : 3 ,
bar : 4 ,
'data-blah' : 5 ,
} ;
3.7 อย่าเรียกใช้เมธอด Object.prototype
โดยตรง เช่น hasOwnProperty
, propertyIsEnumerable
และ isPrototypeOf
eslint: no-prototype-builtins
ทำไม วิธีการเหล่านี้อาจถูกบดบังโดยคุณสมบัติบนวัตถุที่เป็นปัญหา - พิจารณา
{ hasOwnProperty: false }
- หรือวัตถุอาจเป็นวัตถุว่าง (Object.create(null)
) ในเบราว์เซอร์สมัยใหม่ที่รองรับ ES2022 หรือมี polyfill เช่น https://npmjs.com/object.hasown ก็สามารถใช้Object.hasOwn
เป็นทางเลือกแทนObject.prototype.hasOwnProperty.call
ได้
// bad
console . log ( object . hasOwnProperty ( key ) ) ;
// good
console . log ( Object . prototype . hasOwnProperty . call ( object , key ) ) ;
// better
const has = Object . prototype . hasOwnProperty ; // cache the lookup once, in module scope.
console . log ( has . call ( object , key ) ) ;
// best
console . log ( Object . hasOwn ( object , key ) ) ; // only supported in browsers that support ES2022
/* or */
import has from 'has' ; // https://www.npmjs.com/package/has
console . log ( has ( object , key ) ) ;
/* or */
console . log ( Object . hasOwn ( object , key ) ) ; // https://www.npmjs.com/package/object.hasown
3.8 ต้องการไวยากรณ์การแพร่กระจายของวัตถุมากกว่า Object.assign
กำหนดให้กับวัตถุที่คัดลอกตื้น ใช้ไวยากรณ์พารามิเตอร์ส่วนที่เหลือของอ็อบเจ็กต์เพื่อรับอ็อบเจ็กต์ใหม่โดยละเว้นคุณสมบัติบางอย่าง eslint: prefer-object-spread
// very bad
const original = { a : 1 , b : 2 } ;
const copy = Object . assign ( original , { c : 3 } ) ; // this mutates `original` ಠ_ಠ
delete copy . a ; // so does this
// bad
const original = { a : 1 , b : 2 } ;
const copy = Object . assign ( { } , original , { c : 3 } ) ; // copy => { a: 1, b: 2, c: 3 }
// good
const original = { a : 1 , b : 2 } ;
const copy = { ... original , c : 3 } ; // copy => { a: 1, b: 2, c: 3 }
const { a , ... noA } = copy ; // noA => { b: 2, c: 3 }
⬆ กลับไปด้านบน
4.1 ใช้ไวยากรณ์ตามตัวอักษรสำหรับการสร้างอาร์เรย์ eslint: no-array-constructor
// bad
const items = new Array ( ) ;
// good
const items = [ ] ;
4.2 ใช้ Array#push แทนการกำหนดโดยตรงเพื่อเพิ่มรายการลงในอาร์เรย์
const someStack = [ ] ;
// bad
someStack [ someStack . length ] = 'abracadabra' ;
// good
someStack . push ( 'abracadabra' ) ;
4.3 ใช้อาร์เรย์สเปรด ...
เพื่อคัดลอกอาร์เรย์
// bad
const len = items . length ;
const itemsCopy = [ ] ;
let i ;
for ( i = 0 ; i < len ; i += 1 ) {
itemsCopy [ i ] = items [ i ] ;
}
// good
const itemsCopy = [ ... items ] ;
4.4 หากต้องการแปลงอ็อบเจ็กต์ที่ทำซ้ำได้เป็นอาร์เรย์ ให้ใช้สเปรด ...
แทน Array.from
const foo = document . querySelectorAll ( '.foo' ) ;
// good
const nodes = Array . from ( foo ) ;
// best
const nodes = [ ... foo ] ;
4.5 ใช้ Array.from
เพื่อแปลงวัตถุที่มีลักษณะคล้ายอาร์เรย์เป็นอาร์เรย์
const arrLike = { 0 : 'foo' , 1 : 'bar' , 2 : 'baz' , length : 3 } ;
// bad
const arr = Array . prototype . slice . call ( arrLike ) ;
// good
const arr = Array . from ( arrLike ) ;
4.6 ใช้ Array.from
แทน Spread ...
สำหรับการแมปบน Iterable เนื่องจากจะช่วยหลีกเลี่ยงการสร้างอาร์เรย์ระดับกลาง
// bad
const baz = [ ... foo ] . map ( bar ) ;
// good
const baz = Array . from ( foo , bar ) ;
4.7 ใช้คำสั่ง return ในการเรียกกลับวิธีการอาร์เรย์ คุณสามารถละเว้นการส่งคืนได้หากเนื้อหาของฟังก์ชันประกอบด้วยคำสั่งเดียวที่ส่งคืนนิพจน์โดยไม่มีผลข้างเคียง ตามข้อ 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 - no returned value means `acc` becomes undefined after the first iteration
[ [ 0 , 1 ] , [ 2 , 3 ] , [ 4 , 5 ] ] . reduce ( ( acc , item , index ) => {
const flatten = acc . concat ( item ) ;
} ) ;
// good
[ [ 0 , 1 ] , [ 2 , 3 ] , [ 4 , 5 ] ] . reduce ( ( acc , item , index ) => {
const flatten = acc . concat ( item ) ;
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 ;
} ) ;
4.8 ใช้ตัวแบ่งบรรทัดหลังจากเปิดวงเล็บอาร์เรย์ และก่อนปิดวงเล็บอาร์เรย์ หากอาร์เรย์มีหลายบรรทัด
// bad
const arr = [
[ 0 , 1 ] , [ 2 , 3 ] , [ 4 , 5 ] ,
] ;
const objectInArray = [ {
id : 1 ,
} , {
id : 2 ,
} ] ;
const numberInArray = [
1 , 2 ,
] ;
// good
const arr = [ [ 0 , 1 ] , [ 2 , 3 ] , [ 4 , 5 ] ] ;
const objectInArray = [
{
id : 1 ,
} ,
{
id : 2 ,
} ,
] ;
const numberInArray = [
1 ,
2 ,
] ;
⬆ กลับไปด้านบน
5.1 ใช้การทำลายโครงสร้างวัตถุเมื่อเข้าถึงและใช้คุณสมบัติหลายอย่างของวัตถุ eslint: prefer-destructuring
ทำไม การทำลายล้างช่วยให้คุณไม่ต้องสร้างการอ้างอิงชั่วคราวสำหรับคุณสมบัติเหล่านั้น และจากการเข้าถึงอ็อบเจ็กต์ซ้ำๆ การเข้าถึงออบเจ็กต์ซ้ำจะสร้างโค้ดที่ซ้ำกันมากขึ้น ต้องอ่านมากขึ้น และสร้างโอกาสในการผิดพลาดมากขึ้น อ็อบเจ็กต์การทำลายโครงสร้างยังให้คำจำกัดความของโครงสร้างอ็อบเจ็กต์ไซต์เดียวที่ใช้ในบล็อก แทนที่จะต้องอ่านทั้งบล็อกเพื่อกำหนดว่าจะใช้อะไร
// 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 ใช้การทำลายโครงสร้างอาร์เรย์ eslint: prefer-destructuring
const arr = [ 1 , 2 , 3 , 4 ] ;
// bad
const first = arr [ 0 ] ;
const second = arr [ 1 ] ;
// good
const [ first , second ] = arr ;
5.3 ใช้การทำลายโครงสร้างวัตถุสำหรับค่าที่ส่งคืนหลายค่า ไม่ใช่การทำลายโครงสร้างอาร์เรย์
ทำไม คุณสามารถเพิ่มคุณสมบัติใหม่เมื่อเวลาผ่านไปหรือเปลี่ยนลำดับของสิ่งต่าง ๆ โดยไม่ทำให้ไซต์การโทรเสียหาย
// bad
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 ) ;
// good
function processInput ( input ) {
// then a miracle occurs
return { left , right , top , bottom } ;
}
// the caller selects only the data they need
const { left , top } = processInput ( input ) ;
⬆ กลับไปด้านบน
6.1 ใช้เครื่องหมายคำพูดเดี่ยว ''
สำหรับสตริง eslint: quotes
// bad
const name = "Capt. Janeway" ;
// bad - template literals should contain interpolation or newlines
const name = `Capt. Janeway` ;
// good
const name = 'Capt. Janeway' ;
6.2 สตริงที่ทำให้บรรทัดยาวเกิน 100 อักขระไม่ควรเขียนข้ามหลายบรรทัดโดยใช้การต่อสตริง
ทำไม สตริงที่ขาดนั้นสร้างความเจ็บปวดในการทำงานและทำให้โค้ดค้นหาได้น้อยลง
// 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.3 เมื่อสร้างสตริงโดยทางโปรแกรม ให้ใช้สตริงเทมเพลตแทนการต่อข้อมูล eslint: prefer-template
template-curly-spacing
ทำไม สตริงเทมเพลตให้ไวยากรณ์ที่กระชับและอ่านง่าย พร้อมด้วยการขึ้นบรรทัดใหม่ที่เหมาะสมและคุณลักษณะการแก้ไขสตริง
// 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 } ?` ;
}
eval()
กับสตริง มันเปิดช่องโหว่มากเกินไป eslint: no-eval
6.5 อย่าหลีกเลี่ยงอักขระในสตริงโดยไม่จำเป็น eslint: no-useless-escape
ทำไม แบ็กสแลช