JavaScript coding specification (Chinese/Airbnb), javascriptair.pdf
Airbnb is a company located in San Francisco, USA. This article is its internal JavaScript code specification, which is well written. on Github, there are 16,686 + Star, 3,080 + fork, front-end developers can refer.
Original article: https://github.com/airbnb/javascript
Note: I have deleted and modified some of the specifications according to my development habits.
Type
Original Value: equivalent to passing value
- String
- Number
- Boolean
- Null
- Undefined
var foo = 1, bar = foo;bar = 9;console.log(foo, bar); // => 1, 9
Complex Type: equivalent to transferring reference
var foo = [1, 2], bar = foo;bar[0] = 9;console.log(foo[0], bar[0]); // => 9, 9
Object
Create an object using the Literal Value
// badvar item = new Object();// goodvar item = {};
Do not use reserved words as the key
// badvar superman = { class: 'superhero', default: { clark: 'kent' }, private: true};// goodvar superman = { klass: 'superhero', defaults: { clark: 'kent' }, hidden: true};
Array
Create an array using the Literal Value
// badvar items = new Array();// goodvar items = [];
If you do not know the length of the array, use push
var someStack = [];// badsomeStack[someStack.length] = 'abracadabra';// goodsomeStack.push('abracadabra');
Use slice. jsPerf when you need to copy the Array
var len = items.length, itemsCopy = [], i;// badfor (i = 0; i < len; i++) { itemsCopy[i] = items[i];}// gooditemsCopy = items.slice();
Use slice to convert the objects of the class array into an array.
function trigger() { var args = Array.prototype.slice.call(arguments); ...}
String
Use single quotes ''for strings''
// badvar name = "Bob Parr";// goodvar name = 'Bob Parr';// badvar fullName = "Bob " + this.lastName;// goodvar fullName = 'Bob ' + this.lastName;
Character strings with more than 80 characters should use string connection to wrap
Note: long string connection may affect the performance if it is over-used. jsPerf & Discussion
// badvar 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.';// badvar 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.';// goodvar 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.';
Use join instead of string join to construct a string during programming, especially IE: jsPerf.
var items, messages, length, i;messages = [{ state: 'success', message: 'This one worked.'},{ state: 'success', message: 'This one worked as well.'},{ state: 'error', message: 'This one did not work.'}];length = messages.length;// badfunction inbox(messages) { items = '<ul>'; for (i = 0; i < length; i++) { items += '<li>' + messages[i].message + '</li>'; } return items + '</ul>';}// goodfunction inbox(messages) { items = []; for (i = 0; i < length; i++) { items[i] = messages[i].message; } return '<ul><li>' + items.join('</li><li>') + '</li></ul>';}
Function
Function expression:
// Anonymous function expression var anonymous = function () {return true ;}; // famous function expression var named = function named () {return true ;}; // call the function expression (function () {console. log ('Welcome to the Internet. please follow me. ');})();
Never declare a function in a non-function block and assign that function to a variable. The Browser allows you to do this, but the parsing is different.
Note: The ECMA-262 definition defines a block as a set of statements, and the function declaration is not a statement. Read the ECMA-262's explanation of this problem.
// badif (currentUser) { function test() { console.log('Nope.'); }}// goodif (currentUser) { var test = function test() { console.log('Yup.'); };}
Do not name the parameter arguments, which will be beyond the arguments object passed in the function scope.
// badfunction nope(name, options, arguments) { // ...stuff...}// goodfunction yup(name, options, args) { // ...stuff...}
Attribute
Brackets are used to access attributes using variables.
var luke = { jedi: true, age: 28};function getProp(prop) { return luke[prop];}var isJedi = getProp('jedi');
Variable
We always use var to declare variables. If we do not do this, global variables will be generated, so we should avoid contamination of the global namespace.
// badsuperPower = new SuperPower();// goodvar superPower = new SuperPower();
Declare multiple variables using one var and a new line, and indent four spaces.
// badvar items = getItems();var goSportsTeam = true;var dragonball = 'z';// goodvar items = getItems(), goSportsTeam = true, dragonball = 'z';
Finally, declare unassigned variables. It is useful when you want to reference previously assigned variables.
// badvar i, len, dragonball, items = getItems(), goSportsTeam = true;// badvar i, items = getItems(), dragonball, goSportsTeam = true, len;// goodvar items = getItems(), goSportsTeam = true, dragonball, length, i;
Declare variables at the top of the scope to avoid issues related to variable declaration and assignment.
// badfunction() { test(); console.log('doing stuff..'); //..other stuff.. var name = getName(); if (name === 'test') { return false; } return name;}// goodfunction() { var name = getName(); test(); console.log('doing stuff..'); //..other stuff.. if (name === 'test') { return false; } return name;}// badfunction() { var name = getName(); if (!arguments.length) { return false; } return true;}// goodfunction() { if (!arguments.length) { return false; } var name = getName(); return true;}
Conditional expressions and equal signs
- Use = and! = And! =.
The forced type conversion of conditional expressions follows the following rules:
- The object is calculated as true.
- Undefined is calculated as false.
- Null is calculated as false.
- The Boolean value is calculated as a Boolean value.
- If the number is + 0,-0, or NaN, it is calculated as false. Otherwise, it is true.
- If the string is an empty string '', the value is calculated as false; otherwise, the value is true.
if ([0]) { // true // An array is an object, objects evaluate to true}
Use shortcuts.
// badif (name !== '') { // ...stuff...}// goodif (name) { // ...stuff...}// badif (collection.length > 0) { // ...stuff...}// goodif (collection.length) { // ...stuff...}
Learn more about Truth Equality and JavaScript
Block
Note
Use/**... */to annotate multiple rows, including descriptions, specified types, parameter values, and returned values.
// bad// make() returns a new element// based on the passed in tag name//// @param <String> tag// @return <Element> elementfunction make(tag) { // ...stuff... return element;}// good/** * make() returns a new element * based on the passed in tag name * * @param <String> tag * @return <Element> element */function make(tag) { // ...stuff... return element;}
Use // to comment on a single line, comment on a single line on the comment object, and leave a blank line before the comment.
// badvar active = true; // is current tab// good// is current tabvar active = true;// badfunction getType() { console.log('fetching type...'); // set the default type to 'no type' var type = this._type || 'no type'; return type;}// goodfunction getType() { console.log('fetching type...'); // set the default type to 'no type' var type = this._type || 'no type'; return type;}
If you have a problem that you need to review or if you suggest a solution that needs to be implemented, you need to add FIXME or TODO before your annotations to help others quickly understand.
function Calculator() { // FIXME: shouldn't use a global here total = 0; return this;}
function Calculator() { // TODO: total should be configurable by an options param this.total = 0; return this;}
Blank
Set tab to 4 spaces
// badfunction() {∙∙var name;}// badfunction() {∙var name;}// goodfunction() {∙∙∙∙var name;}
Place a space in front of the braces
// badfunction test(){ console.log('test');}// goodfunction test() { console.log('test');}// baddog.set('attr',{ age: '1 year', breed: 'Bernese Mountain Dog'});// gooddog.set('attr', { age: '1 year', breed: 'Bernese Mountain Dog'});
Use indentation for long method chains.
// bad$('#items').find('.selected').highlight().end().find('.open').updateCount();// good$('#items') .find('.selected') .highlight() .end() .find('.open') .updateCount();// badvar leds = stage.selectAll('.led').data(data).enter().append('svg:svg').class('led', true) .attr('width', (radius + margin) * 2).append('svg:g') .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') .call(tron.led);// goodvar leds = stage.selectAll('.led') .data(data) .enter().append('svg:svg') .class('led', true) .attr('width', (radius + margin) * 2) .append('svg:g') .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') .call(tron.led);
Comma
Do not put commas in front
// badvar once , upon , aTime;// goodvar once, upon, aTime;// badvar hero = { firstName: 'Bob' , lastName: 'Parr' , heroName: 'Mr. Incredible' , superPower: 'strength'};// goodvar hero = { firstName: 'Bob', lastName: 'Parr', heroName: 'Mr. Incredible', superPower: 'strength'};
Do not add extra commas, which may cause errors in IE. If you add one more comma, some ES3 implementations will calculate the length of multiple arrays.
// badvar hero = { firstName: 'Kevin', lastName: 'Flynn',};var heroes = [ 'Batman', 'Superman',];// goodvar hero = { firstName: 'Kevin', lastName: 'Flynn'};var heroes = [ 'Batman', 'Superman'];
Semicolon
The statement must end with a plus sign.
// bad(function() { var name = 'Skywalker' return name})()// good(function() { var name = 'Skywalker'; return name;})();// good;(function() { var name = 'Skywalker'; return name;})();
Type conversion
- Type conversion is performed at the beginning of the statement.
String:
// => this.reviewScore = 9;// badvar totalScore = this.reviewScore + '';// goodvar totalScore = '' + this.reviewScore;// badvar totalScore = '' + this.reviewScore + ' total score';// goodvar totalScore = this.reviewScore + ' total score';
Use parseInt for a number and always carry the base number of type conversion.
var inputValue = '4';// badvar val = new Number(inputValue);// badvar val = +inputValue;// badvar val = inputValue >> 0;// badvar val = parseInt(inputValue);// goodvar val = Number(inputValue);// goodvar val = parseInt(inputValue, 10);// good/** * parseInt was the reason my code was slow. * Bitshifting the String to coerce it to a * Number made it a lot faster. */var val = inputValue >> 0;
Boolean value:
var age = 0;// badvar hasAge = new Boolean(age);// goodvar hasAge = Boolean(age);// goodvar hasAge = !!age;
Naming Conventions
Avoid a single character name and make your variable name descriptive.
// badfunction q() { // ...stuff...}// goodfunction query() { // ..stuff..}
Use the hump naming rules when naming objects, functions, and instances
// badvar OBJEcttsssss = {};var this_is_my_object = {};var this-is-my-object = {};function c() {};var u = new user({ name: 'Bob Parr'});// goodvar thisIsMyObject = {};function thisIsMyFunction() {};var user = new User({ name: 'Bob Parr'});
Use the upper case of the hump when naming constructors or classes
// badfunction user(options) { this.name = options.name;}var bad = new user({ name: 'nope'});// goodfunction User(options) { this.name = options.name;}var good = new User({ name: 'yup'});
Add an underscore _
// badthis.__firstName__ = 'Panda';this.firstName_ = 'Panda';// goodthis._firstName = 'Panda';
Use _ this when saving the reference to this.
// badfunction() { var self = this; return function() { console.log(self); };}// badfunction() { var that = this; return function() { console.log(that); };}// goodfunction() { var _this = this; return function() { console.log(_this); };}
Accessors
- Attribute accessors are not required
If you do have accessors, use getVal () and setVal ('hello ')
// baddragon.age();// gooddragon.getAge();// baddragon.age(25);// gooddragon.setAge(25);
If the attribute is a Boolean value, use isVal () or hasVal ()
// badif (!dragon.age()) { return false;}// goodif (!dragon.hasAge()) { return false;}
You can create get () and set () functions, but they must be consistent.
function Jedi(options) { options || (options = {}); var lightsaber = options.lightsaber || 'blue'; this.set('lightsaber', lightsaber);}Jedi.prototype.set = function(key, val) { this[key] = val;};Jedi.prototype.get = function(key) { return this[key];};
Constructor
Instead of overwriting the prototype with a new object, overwriting the prototype will cause inheritance problems.
function Jedi() { console.log('new jedi');}// badJedi.prototype = { fight: function fight() { console.log('fighting'); }, block: function block() { console.log('blocking'); }};// goodJedi.prototype.fight = function fight() { console.log('fighting');};Jedi.prototype.block = function block() { console.log('blocking');};
Methods can return this help method chain.
// badJedi.prototype.jump = function() { this.jumping = true; return true;};Jedi.prototype.setHeight = function(height) { this.height = height;};var luke = new Jedi();luke.jump(); // => trueluke.setHeight(20) // => undefined// goodJedi.prototype.jump = function() { this.jumping = true; return this;};Jedi.prototype.setHeight = function(height) { this.height = height; return this;};var luke = new Jedi();luke.jump() .setHeight(20);
You can write a custom toString () method, but make sure it works normally without any side effects.
function Jedi(options) { options || (options = {}); this.name = options.name || 'no name';}Jedi.prototype.getName = function getName() { return this.name;};Jedi.prototype.toString = function toString() { return 'Jedi - ' + this.getName();};
Event
When attaching data to an event, a hash is passed in instead of the original value. This allows later contributors to add more data to the event data, instead of finding and updating the event processor.
// bad$(this).trigger('listingUpdated', listing.id);...$(this).on('listingUpdated', function(e, listingId) { // do something with listingId});
Better:
// good$(this).trigger('listingUpdated', { listingId : listing.id });...$(this).on('listingUpdated', function(e, data) { // do something with data.listingId});
Module
- The module should start! This ensures that if a faulty module forgets to include the last semicolon, no error will occur after merging.
- This file should be named after the camper and in a folder of the same name, and the exported files must have the same name.
- Add a method named noConflict () to set the exported module to a previous version and return it.
Always declare 'use strict 'At the top of the module ';
// fancyInput/fancyInput.js!function(global) { 'use strict'; var previousFancyInput = global.FancyInput; function FancyInput(options) { this.options = options || {}; } FancyInput.noConflict = function noConflict() { global.FancyInput = previousFancyInput; return FancyInput; }; global.FancyInput = FancyInput;}(this);
JQuery
Cache jQuery queries
// badfunction setSidebar() { $('.sidebar').hide(); // ...stuff... $('.sidebar').css({ 'background-color': 'pink' });}// goodfunction setSidebar() { var $sidebar = $('.sidebar'); $sidebar.hide(); // ...stuff... $sidebar.css({ 'background-color': 'pink' });}
$ ('. Sidebar ul') or $ ('. sidebar ul'), jsPerf
Use find to query jQuery objects with scopes
// bad$('.sidebar', 'ul').hide();// bad$('.sidebar').find('ul').hide();// good$('.sidebar ul').hide();// good$('.sidebar > ul').hide();// good (slower)$sidebar.find('ul');// good (faster)$($sidebar[0]).find('ul');
Via: https://github.com/adamlu/javascript-style-guide