#变量
Bad: 1 const yyyymmdstr = moment().format("YYYY/MM/DD" )
Good: 1 const currentDate = moment().format("YYYY/MM/DD" )
Bad: 1 2 setTimeout(blastOff , 86400000) ;
Good: 1 2 3 4 const MILLISECONDS_IN_A_DAY = 86400000 ;setTimeout (blastOff, MILLISECONDS_IN_A_DAY );
Bad: 1 2 3 4 5 6 const address = "One Infinite Loop, Cupertino 95014" ;const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/ ;saveCityZipCode ( address.match (cityZipCodeRegex)[1 ], address.match (cityZipCodeRegex)[2 ] );
Good: 1 2 3 4 const address = "One Infinite Loop, Cupertino 95014" ;const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/ ;const [, city, zipCode] = address.match (cityZipCodeRegex) || [];saveCityZipCode (city, zipCode);
Bad: 1 2 3 4 5 6 7 8 9 const Car = { carMake: "Honda" , carModel: "Accord" , carColor: "Blue" }; function paintCar (car) { car.carColor = "Red" ; }
Good: 1 2 3 4 5 6 7 8 9 const Car = { make: "Honda" , model: "Accord" , color : "Blue" }; function paintCar (car) { car.color = "Red" ; }
Bad: 1 2 3 4 function createMicrobrewery (name) { const breweryName = name || "Hipster Brew Co." ; }
Good: 1 2 3 function createMicrobrewery(name = "Hipster Brew Co." ) { }
函数
一个或两个参数是理想的情况,如果可能的话应该避免三个。 应该整合除此之外的任何东西。 通常,如果你有两个以上的参数,那么你的函数试图做太多(秉承一个函数做一件事)。
Bad: 1 2 3 function createMenu(title , body , buttonText , cancellable ) { }
Good: 1 2 3 4 5 6 7 8 9 10 function createMenu ({ title , body , buttonText , cancellable }) { } createMenu ({ title : "Foo" , body : "Bar" , buttonText : "Baz" , cancellable : true });
Bad:
1 2 3 4 5 6 7 8 function emailClients (clients) { clients.forEach (client => { const clientRecord = database.lookup(client); if (clientRecord.isActive()) { email (client); } }); }
Good:
1 2 3 4 5 6 7 8 function emailActiveClients(clients ) { clients.filter(isActiveClient).for Each(email ) ; } function isActiveClient(client ) { const clientRecord = database.lookup(client); return clientRecord.isActive() ; }
Bad:
1 2 3 4 5 6 7 8 function emailClients (clients) { clients.forEach (client => { const clientRecord = database.lookup(client); if (clientRecord.isActive()) { email (client); } }); }
Good:
1 2 3 4 5 6 7 8 function emailActiveClients(clients ) { clients.filter(isActiveClient).for Each(email ) ; } function isActiveClient(client ) { const clientRecord = database.lookup(client); return clientRecord.isActive() ; }
Bad:
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 function showDeveloperList (developers ) { developers.forEach (developer => { const expectedSalary = developer.calculateExpectedSalary (); const experience = developer.getExperience (); const githubLink = developer.getGithubLink (); const data = { expectedSalary, experience, githubLink }; render (data); }); } function showManagerList (managers ) { managers.forEach (manager => { const expectedSalary = manager.calculateExpectedSalary (); const experience = manager.getExperience (); const portfolio = manager.getMBAProjects (); const data = { expectedSalary, experience, portfolio }; render (data); }); }
Good:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 function showEmployeeList (employees ) { employees.forEach (employee => { const expectedSalary = employee.calculateExpectedSalary (); const experience = employee.getExperience (); const data = { expectedSalary, experience }; switch (employee.type ) { case "manager" : data.portfolio = employee.getMBAProjects (); break ; case "developer" : data.githubLink = employee.getGithubLink (); break ; } render (data); }); }
Bad:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const menuConfig = { title: null, body: "Bar" , buttonText: null, cancellable: true }; function createMenu (config) { config.title = config.title || "Foo" ; config.body = config.body || "Bar" ; config.buttonText = config.buttonText || "Baz" ; config.cancellable = config.cancellable !== undefined ? config.cancellable : true ; } createMenu (menuConfig);
Good:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 const menuConfig = { title: "Order" , buttonText: "Send" , cancellable: true }; function createMenu (config ) { config = Object.assign ( { title : "Foo" , body : "Bar" , buttonText : "Baz" , cancellable : true }, config ); } createMenu (menuConfig);
Bad:
1 2 3 if (fsm.state === "fetching" && isEmpty(listNode)) { // ... }
Good:
1 2 3 4 5 6 7 function shouldShowSpinner(fsm , listNode ) { return fsm.state === "fetching" && isEmpty(listNode ) ; } if (shouldShowSpinner(fsmInstance , listNodeInstance ) ) { }
Bad:
1 2 3 4 5 for (let i = 0 , len = list.length ; i < len; i++) { }
Good:
1 2 3 for (let i = 0; i < list.length; i++) { // ... }
Bad:
1 2 3 4 5 6 7 8 9 10 function oldRequestModule(url ) { } function new RequestModule(url ) { } const req = newRequestModule; inventoryTracker("apples" , req , "www.inventory-awesome.io" ) ;
Good:
1 2 3 4 5 6 function new RequestModule(url ) { } const req = newRequestModule; inventoryTracker("apples" , req , "www.inventory-awesome.io" ) ;
对象和数据结构
Bad:
1 2 3 4 5 6 7 8 9 10 11 function makeBankAccount () { return { balance: 0 }; } const account = makeBankAccount();account.balance = 100 ;
Good:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 function makeBankAccount ( ) { let balance = 0 ; function getBalance ( ) { return balance; } function setBalance (amount ) { balance = amount; } return { getBalance, setBalance }; } const account = makeBankAccount ();account.setBalance (100 );
Bad:
1 2 3 4 5 6 7 8 9 10 11 12 const Employee = function (name ) { this .name = name; }; Employee .prototype .getName = function getName ( ) { return this .name ; }; const employee = new Employee ("John Doe" );console .log (`Employee name: ${employee.getName()} ` ); delete employee.name ;console .log (`Employee name: ${employee.getName()} ` );
Good:
1 2 3 4 5 6 7 8 9 10 11 12 function makeEmployee (name ) { return { getName ( ) { return name; } }; } const employee = makeEmployee ("John Doe" );console .log (`Employee name: ${employee.getName()} ` ); delete employee.name ;console .log (`Employee name: ${employee.getName()} ` );
这里我觉得用原型链也不错,存在疑惑。
Bad:
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 const Animal = function (age ) { if (!(this instanceof Animal )) { throw new Error ("Instantiate Animal with `new`" ); } this .age = age; }; Animal .prototype .move = function move ( ) {};const Mammal = function (age, furColor ) { if (!(this instanceof Mammal )) { throw new Error ("Instantiate Mammal with `new`" ); } Animal .call (this , age); this .furColor = furColor; }; Mammal .prototype = Object .create (Animal .prototype );Mammal .prototype .constructor = Mammal ;Mammal .prototype .liveBirth = function liveBirth ( ) {};const Human = function (age, furColor, languageSpoken ) { if (!(this instanceof Human )) { throw new Error ("Instantiate Human with `new`" ); } Mammal .call (this , age, furColor); this .languageSpoken = languageSpoken; }; Human .prototype = Object .create (Mammal .prototype );Human .prototype .constructor = Human ;Human .prototype .speak = function speak ( ) {};
Good:
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 class Animal { constructor(age) { this .age = age; } move() { } } class Mammal extends Animal { constructor(age, furColor) { super (age); this .furColor = furColor; } liveBirth() { } } class Human extends Mammal { constructor(age, furColor, languageSpoken) { super (age, furColor); this .languageSpoken = languageSpoken; } speak() { } }
class虽然看起来比原型链更简洁,但是不懂原型链何来 class
Bad:
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 class Car { constructor (make, model, color) { this .make = make; this .model = model; this .color = color; } setMake(make) { this .make = make; } setModel(model) { this .model = model; } setColor(color) { this .color = color; } save() { console.log(this .make, this .model, this .color); } } const car = new Car("Ford" , "F-150" , "red" );car.setColor("pink" ); car.save();
Good:
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 class Car { constructor (make, model, color) { this .make = make; this .model = model; this .color = color; } setMake(make) { this .make = make; return this ; } setModel(model) { this .model = model; return this ; } setColor(color) { this .color = color; return this ; } save() { console.log(this .make, this .model, this .color); return this ; } } const car = new Car("Ford" , "F-150" , "red" ).setColor("pink" ).save();
Bad:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Employee { constructor(name, email) { this .name = name; this .email = email; } } class EmployeeTaxData extends Employee { constructor(ssn, salary) { super (); this .ssn = ssn; this .salary = salary; } }
Good:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class EmployeeTaxData { constructor (ssn , salary ) { this .ssn = ssn ; this .salary = salary ; } } class Employee { constructor (name , email ) { this .name = name ; this .email = email ; } setTaxData (ssn , salary ) { this .taxData = new EmployeeTaxData (ssn , salary ); } }
Bad:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class DOMTraverser { constructor (settings ) { this .settings = settings; this .setup (); } setup ( ) { this .rootNode = this .settings .rootNode ; this .animationModule .setup (); } traverse ( ) { } } const $ = new DOMTraverser ({ rootNode : document .getElementsByTagName ("body" ), animationModule ( ) {} });
Good:
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 class DOMTraverser { constructor (settings ) { this .settings = settings; this .options = settings.options ; this .setup (); } setup ( ) { this .rootNode = this .settings .rootNode ; this .setupOptions (); } setupOptions ( ) { if (this .options .animationModule ) { } } traverse ( ) { } } const $ = new DOMTraverser ({ rootNode : document .getElementsByTagName ("body" ), options : { animationModule ( ) {} } });
测试
Bad:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import assert from "assert";describe("MakeMomentJSGreatAgain", () => { it("handles date boundaries", () => { let date ; date = new MakeMomentJSGreatAgain("1/1/2015"); date .addDays(30 ); assert .equal("1/31/2015", date ); date = new MakeMomentJSGreatAgain("2/1/2016"); date .addDays(28 ); assert .equal("02/29/2016", date ); date = new MakeMomentJSGreatAgain("2/1/2015"); date .addDays(28 ); assert .equal("03/01/2015", date ); }); });
Good:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import assert from "assert" ;describe("MakeMomentJSGreatAgain" , () => { it("handles 30-day months" , () => { const date = new MakeMomentJSGreatAgain("1/1/2015" ); date.addDays(30 ); assert .equal("1/31/2015" , date); }); it("handles leap year" , () => { const date = new MakeMomentJSGreatAgain("2/1/2016" ); date.addDays(28 ); assert .equal("02/29/2016" , date); }); it("handles non-leap year" , () => { const date = new MakeMomentJSGreatAgain("2/1/2015" ); date.addDays(28 ); assert .equal("03/01/2015" , date); }); });
错误捕获
Bad:
1 2 3 4 5 try { functionThatMightThrow(); } catch (error ) { console.log (error ); }
Good:
1 2 3 4 5 6 7 8 9 10 11 try { functionThatMightThrow (); } catch (error) { console.error (error); notifyUserOfError (error); reportErrorToService (error); }
Bad:
1 2 3 4 5 6 7 getdata() .then(data => { functionThatMightThrow(data); }) .catch(error => { console .log (error); });
Good:
1 2 3 4 5 6 7 8 9 10 11 12 13 getdata () .then (data => { functionThatMightThrow(data); }) .catch (error => { // One option (more noisy than console.log): console.error (error); notifyUserOfError (error); reportErrorToService (error); });