Това е "жив" документ и нови идеи за подобряване на кода са винаги добре дошли. Допринесете: fork, clone, branch, commit, push, pull request.
- Rick Waldron @rwaldron, github
- Mathias Bynens @mathias, github
- Schalk Neethling @ossreleasefeed, github
- Kit Cambridge @kitcambridge, github
- Raynos github
- Matias Arriola @MatiasArriola, github
- John Fischer @jfroffice, github
- Idan Gazit @idangazit, github
- Leo Balter @leobalter, github
- Breno Oliveira @garu_rj, github
- Leo Beto Souza @leobetosouza, github
- Ryuichi Okumura @okuryu, github
- Pascal Precht @PascalPrecht, github
- EngForDev engfordev - Hwan Min Hong / MinTaek Kwon @leoinsight / Tw Shim @marocchino, github / Nassol Kim @nassol99, github / Juntai Park @rkJun, github / Minkyu Shim / Gangmin Won / Justin Yoo @justinchronicle / Daeyup Lee
- Marco Trulla @marcotrulla, github
- Alex Navasardyan @alexnavasardyan, github
- Mihai Paun @mihaipaun, github
- Evgeny Mandrikov @_godin_, github
- Sofish Lin @sofish, github
- Дејан Димић @dejan_dimic, github
- Miloš Gavrilović @gavrisimo, github
- Firede @firede github
- monkadd github
- Stephan Lindauer @stephanlindauer, github
- Thomas P @dragon5689 github
- Yotam Ofek @yotamofek github
- Aleksandr Filatov @greybax, github
- Duc Nguyen @ducntq, github
- James Young @jamsyoung, github
- Hao-Wei Jeng @l0ckys, github
- Richard Gibson @gibson042, github
- Fesuy github
- Stephane Moreau github
- Boris Nekezov github
- Akshat Joshi @akshat_joshi, github
Целият код във всяка кодова база трябва да изглежда като писано от един човек, няма значение колко души са допринесли.
Последващия списък изтъква практиките, които използвам в всичкия ми код, на който съм оригинален автор; участия в проекти, които съм създал трябва да следват тези напътствия.
Нямам намерение да налагам моите предпочитания за стил на кода или проекти на други хора; ако има наличие на съществуващ общ стил, той трябва да се спазва.
Rebecca Murphey
"Част от това да си добър ръководител на един успешен проект е да осъзнаете, че писането на код за вас е лоша идея. Ако хиляди души използват вашия код, тогава пишете кода ви максилнално ясно, а не по вашето лично предпочитание."
Idan Gazit
- ORIGINAL
- Bulgarian
- German
- French
- Spanish
- Portuguese - Brazil
- Korean
- 日本語
- Italian
- Russian
- Romanian
- 简体中文
- Serbian - cyrilic alphabet
- Serbian - latin alphabet
- 繁體中文
- Indonesian
- Greek
- Hindi
- JavaScript Plugin for Sonar
- Plato
- jsPerf
- jsFiddle
- Codepen
- jsbin
- JavaScript Lint (JSL)
- jshint
- jslint
- eslint
- jscs
- jscodesniffer
- Editorconfig
- Hound
Последващото трябва да се вземе под внимание 1) недовършено, и 2) ИЗИСКВА СЕ ЧЕТЕНЕ. Не винаги съм съгласен със стила написан от авторите по-долу, но едно нещо е сигурно: Те са консистентни. Нещо повече, това са авторитети на езика.
- Baseline For Front End Developers: 2015
- Eloquent JavaScript
- JavaScript, JavaScript
- Adventures in JavaScript Development
- Perfection Kills
- Douglas Crockford's Wrrrld Wide Web
- JS Assessment
Проектите винаги трябва да включат някои общи средства, чрез които източникът да бъде свързан, тестван и компресиран при подготовка за производствена употреба. За тази задача, grunt на Бен Алман е най-добрата от всички официални заместители на папката "kits/" в това хранилище.
Проектите трябва да включват някаква форма на единица, референция, имплементация или функционално тестване. Демострационни случаи на употреба НЕ СЕ КВАЛИФИЦИРАТ като "тестове". По-долу има списък със тестови рамки, нито една от които не е одобрена повече от другата.
- Whitespace
- Beautiful Syntax
- Type Checking (Courtesy jQuery Core Style Guidelines)
- Conditional Evaluation
- Practical Style
- Naming
- Misc
- Native & Host Objects
- Comments
- One Language Code
Следващите раздели посочват едно разумно ръководство на стил за съвременна разработка на JavaScript и не е предназначен за норматив. Най-важната част е закона за постоянност на стила на кода. Каквото и да изберете като стил за вашия проект, трябва да се счита за закон. Използвайте този документ като напътствие на вашето отдаване към проекта ви за консистентност на стила на кода, четимост и поддържане.
- Никога не смесвайте спейсове и табове.
- Когато започнете един проект, преди да почнете да пишете какъвто и да е код, изберете между меки отсъпи (интервали) или реални табулации, считайте го за закон
- За четимост, винаги препоръчвам да зададете размера на отстъпа на вашия редактор на два знака — което означава два интервала или два интервала, представляващи истинска табулация.
- Винаги работете с включена настройка "покажи скрити", ако редактора ви я поддържа. Предимствата на тази практика са:
- Усилена консистенция
- Премахване на интервал в края на реда
- Премахване на празни редове
- Комитите и разликите в кода са по-лесни за четене
- Използвайте Editorconfig когато е възможно. Поддържа повечето IDEs и обработва повечето настройки за празно пространство.
-
A. Parens, Braces, Linebreaks А. Скоби, Фигурни Скоби, Пренасяне на редовете
// if/else/for/while/try винаги имат интервали между тях, къдравите скоби и разделението между секциите са на множество редове // това подобрява четимостта // 2.A.1.1 // Примери за наистина нечетлив синтаксис if(condition) doSomething(); while(condition) iterating++; for(var i=0;i<100;i++) someIterativeFn(); // 2.A.1.1 // Използвайте интервали за да повишите четимостта if ( condition ) { // изрази } while ( condition ) { // изрази } for ( var i = 0; i < 100; i++ ) { // изрази } // Още по-добре: var i, length = 100; for ( i = 0; i < length; i++ ) { // изрази } // Или... var i = 0, length = 100; for ( ; i < length; i++ ) { // изрази } var prop; for ( prop in object ) { // изрази } if ( true ) { // изрази } else { // изрази }
B. Прислояване, Декларации, Функции ( Именувани, Изрази, Конструктори)
// 2.B.1.1 // Променливи var foo = "bar", num = 1, undef; // Литерална нотация: var array = [], object = {}; // 2.B.1.2 // Използването на само един `var` на всеки обхват (функция) или по един `var` на всяка променлива, // повишава четимостта и пази вашия списък на декларации от безредие. // Използването по един `var` на всяка променлива можете да контролирате по-добре версиите // и освен това улеснява разместването на редовете. // Един `var` на всеки обхват улеснява намирането на недекларирани променливи, // които могат да станат подразбиращи се глобални. // Изберете по-добрия подход за вашия проект и никога не го смесвайте // Лош пример var foo = "", bar = ""; var qux; // Добър пример var foo = ""; var bar = ""; var qux; // или.. var foo = "", bar = "", qux; // или.. var // Коментар на тези foo = "", bar = "", quux; // 2.B.1.3 // Операторите 'var' винаги трябва да са в началото на техния съответен обxват (функция). // Лош пример function foo() { // някакви изрази var bar = "", qux; } // Добър пример function foo() { var bar = "", qux; // всички изрази след декларацията на променливите } // 2.B.1.4 // 'const' и `let`, от ECMAScript 6, също трябва да са в горната част на техния обхват (блоков). // Лош пример function foo() { let foo, bar; if ( condition ) { bar = ""; // изрази } } // Добър пример function foo() { let foo; if ( condition ) { let bar = ""; // изрази } }
// 2.B.2.1 // Деклариране на именувана функция function foo( arg1, argN ) { } // Използване foo( arg1, argN ); // 2.B.2.2 // Деклариране на именувана функция function square( number ) { return number * number; } // Използване square( 10 ); // Много измислен стил на подаване на параметри function square( number, callback ) { callback( number * number ); } square( 10, function( square ) { // изрази с обратно извикване }); // 2.B.2.3 // Функция-Израз var square = function( number ) { // Върнете нещо важно и релевантно return number * number; }; // Функция-Израз с Идентификатор // Тази предпочитана форма има добавената стойност // и името му ще бъде видимо в стека на функционалните обаждания: var factorial = function factorial( number ) { if ( number < 2 ) { return 1; } return number * factorial( number - 1 ); }; // 2.B.2.4 // Деклариране на Конструктор function FooBar( options ) { this.options = options; } // Използване var fooBar = new FooBar({ a: "alpha" }); fooBar.options; // { a: "alpha" }
C. Изключения, леки отклонения
// 2.C.1.1 // Функции с обратно извикване foo(function() { // Забележете, че няма интервали между първата скоба // на изпълненото фунционално извикване и думата "function" }); // Функция приемаща масив като параметър, без интервал foo([ "alpha", "beta" ]); // 2.C.1.2 // Функция приемаща обект като параметър, без интервал foo({ a: "alpha", b: "beta" }); // Единичен низ като параметър, също без интервал foo("bar"); // Съдържание във вътрешни скоби, също без интервал if ( !("foo" in obj) ) { obj = (obj.bar || defaults).baz; }
D. Консистентността винаги побеждава
В секции 2.A-2.C, правилата за интервали са изложени като препоръка с по-проста и по-извисена цел: консистентност. Важно е да се отбележи, че предпочитанията за форматиране, такива като "вътрешно пространство", трябва да се считат за незадължителни, но само един стил трябва да съществува из целия код на вашия проект.
// 2.D.1.1 if (condition) { // изрази } while (condition) { // изрази } for (var i = 0; i < 100; i++) { // изрази } if (true) { // изрази } else { // изрази }
E. Кавички
Независимо от това дали предпочитате единични или двойни кавички, няма разлика в това как JavaScript ги парсва. Това което АБСОЛЮТНО ТРЯБВА да се наложи е консистентност. Никога не смесвайте кавичките в един и същи проект. Изберете един стил и се придържайте към него.
F. Край на Редовете и Празни Редове
Пространствата могат да развалят разликите и да направят промените невъзможни за четене. Помислете да включите "pre-commit" кука, която да премахне автоматично пространството на края на реда и празните пространства на редовете.
-
Type Checking (Courtesy jQuery Core Style Guidelines)
A. Типове
String:
typeof variable === "string"
Number:
typeof variable === "number"
Boolean:
typeof variable === "boolean"
Object:
typeof variable === "object"
Array:
Array.isArray( arrayLikeObject ) (wherever possible)
Node:
elem.nodeType === 1
null:
variable === null
null or undefined:
variable == null
undefined:
Глобални променливи:
typeof variable === "undefined"
Локални променливи:
variable === undefined
Свойства:
object.prop === undefined object.hasOwnProperty( prop ) "prop" in object
B. Прехвърляне на Типове
Представете си следното...
Даден ви е следния HTML:
<input type="text" id="foo-input" value="1">
// 3.B.1.1 // `foo` е деклариран със стойност `0` и неговия тип е `number` var foo = 0; // typeof foo; // "number" ... // Някъде по-късно във вашия код, трябва да обновите `foo` // с нова стойност взета от елемента 'input' foo = document.getElementById("foo-input").value; // Ако сега тествате 'typeof foo`, резултатът ще бъде 'string' // Това означава, че ако имате логика, която тества 'foo' като тази: if ( foo === 1 ) { importantTask(); } // `importantTask()` няма никога да бъде достигнат дори и `foo` да има стойност "1" // 3.B.1.2 // Можете да избегнете проблеми като използвате умно конвертиране в унарните оператори + и -: foo = +document.getElementById("foo-input").value; // ^ унарния оператор + ще преобразува десния си операнд в тип 'number' // typeof foo; // "number" if ( foo === 1 ) { importantTask(); } // `importantTask()` ще бъде извикана
Ето няколко често срещани примера за прехвърляне на типове:
// 3.B.2.1 var number = 1, string = "1", bool = false; number; // 1 number + ""; // "1" string; // "1" +string; // 1 +string++; // 1 string; // 2 bool; // false +bool; // 0 bool + ""; // "false"
// 3.B.2.2 var number = 1, string = "1", bool = true; string === number; // false string === number + ""; // true +string === number; // true bool === number; // false +bool === number; // true bool === string; // false bool === !!string; // true
// 3.B.2.3 var array = [ "a", "b", "c" ]; !!~array.indexOf("a"); // true !!~array.indexOf("b"); // true !!~array.indexOf("c"); // true !!~array.indexOf("d"); // false // Забележете, че горните примери може да се считат за "ненужно умни" // За предпочитане е очевидния подход да сравнявате върнатата стойност на // indexOf, по този начин: if ( array.indexOf( "a" ) >= 0 ) { // ... }
// 3.B.2.4 var num = 2.5; parseInt( num, 10 ); // е съшото като ... ~~num; num >> 0; num >>> 0; // Във всички случаи резултатът е равен на 2 // Помнете, че отрицателните числа ще бъдат обработени различно ... var neg = -2.5; parseInt( neg, 10 ); // е същото като... ~~neg; neg >> 0; // Във всички случаи резултатът е равен на -2 // Въпреки че при... neg >>> 0; // Резултата ще е 4294967294
-
// 4.1.1 // Проверете дали масивът има дължина, // вместо: if ( array.length > 0 ) ... // ...проверявайте за истина, по този начин: if ( array.length ) ... // 4.1.2 // Проверете дали масива е празен, // вместо: if ( array.length === 0 ) ... // ...проверявайте за вярност, по този начин: if ( !array.length ) ... // 4.1.3 // Проверете дали низа не е празен, // вместо: if ( string !== "" ) ... // ...проверявайте за вярност, по този начин: if ( string ) ... // 4.1.4 // Проверете дали низа _е_ празен, // вместо: if ( string === "" ) ... // ...проверявайте дали израдът е неверен, по този начин: if ( !string ) ... // 4.1.5 // Проверете дали тази референция е вярна, // вместо: if ( foo === true ) ... // ...проверете, възползвайки се от вградените възможности: if ( foo ) ... // 4.1.6 // Проверете дали тази референция е невярна, // вместо: if ( foo === false ) ... // ...проверете, използвайки отрицание if ( !foo ) ... // ...Внимавайте, това важи и за: 0, "", null, undefined, NaN // Ако _трябва_ да тествате за булево невярно, тогава използвайте if ( foo === false ) ... // 4.1.7 // Когато проверявате дали е null или undefined, но НЕ Е false, "" или 0, // вместо това: if ( foo === null || foo === undefined ) ... // ...използвайте оператора ==: if ( foo == null ) ... // Помнете, че използвайки оператора == с `null` важи И ЗА ДВЕТЕ `null` и `undefined` // но не `false ', "" или 0 null == undefined
ВИНАГИ проверявайте за най-добрия и точен резултат - горното е ръководство, не догма.
// 4.2.1 // Преобразуване на типове и бележки за проверяване // За предпочитане е да използвате `===` вместо `==` (освен ако конкретния случай не изисква слабо типизирана оценка) // === проверява и типа, т.е.: "1" === 1; // false // == не проверява типа, т.е.: "1" == 1; // true // 4.2.2 // Логически изрази, Вярни & Невярни // Логически изрази: true, false // Вярни: "foo", 1 // Невярни: "", 0, null, undefined, NaN, void 0
-
// 5.1.1 // Практичен модул (function( global ) { var Module = (function() { var data = "secret"; return { // Логическо свойство bool: true, // Стойност от тип низ string: "a string", // Свойство - масив array: [ 1, 2, 3, 4 ], // Свойство - обект object: { lang: "bg-BG" }, getData: function() { // вземете текущата стойност на променливата `data` return data; }, setData: function( value ) { // присвоете стойността на `data` и я върнете return ( data = value ); } }; })(); // Други декларации // добавяме нашия модул към глобалния обект global.Module = Module; })( this );
// 5.2.1 // Практичен Конструктор (function( global ) { function Ctor( foo ) { this.foo = foo; return this; } Ctor.prototype.getFoo = function() { return this.foo; }; Ctor.prototype.setFoo = function( val ) { return ( this.foo = val ); }; // За да извикате конструктора без `new`, можете да направите това: var ctor = function( foo ) { return new Ctor( foo ); }; // добави нашия конструктор към глобалния обект global.ctor = ctor; })( this );
-
A. Вие не сте човешки компилатор/компресор на код, затова не се опитвайте да бъдете такъв.
Последващия код е пример за лошо именуване:
// 6.A.1.1 // Пример за код с лоши имена function q(s) { return document.querySelectorAll(s); } var i,a=[],els=q("#foo"); for(i=0;i<els.length;i++){a.push(els[i]);}
Без съмнение сте писали код като този - дано това приключи днес.
Сега същото парче логика, но с по-добро, по-смислено именуване (и четима структура):
// 6.A.2.1 // Пример на кода с подобрени имена function query( selector ) { return document.querySelectorAll( selector ); } var idx = 0, elements = [], matches = query("#foo"), length = matches.length; for ( ; idx < length; idx++ ) { elements.push( matches[ idx ] ); }
Още няколко съвета за именуване на променливи::
// 6.A.3.1 // Именуване на низове `dog` is a string // 6.A.3.2 // Именуване на масиви `dogs` is an array of `dog` strings // 6.A.3.3 // Именуване на фукнции, обекти, инстанции, и т.н. camelCase; function and var declarations // 6.A.3.4 // Именуване на конструктори, прототипи и т.н. PascalCase; constructor function // 6.A.3.5 // Именуване на регулярни изрази rDesc = //; // 6.A.3.6 // Из Ръководство по стила Google Closure Library functionNamesLikeThis; variableNamesLikeThis; ConstructorNamesLikeThis; EnumNamesLikeThis; methodNamesLikeThis; SYMBOLIC_CONSTANTS_LIKE_THIS;
B. Лица на
this
Извън общоизвестните случаи на
call
иapply
, винаги избирайте.bind( this )
или фунционалния му еквивалент, за създаване на дефиницияBoundFunction
за по-късно извикване. Създаването на псевдоним е в краен случай, ако други решения не са подходящи.// 6.B.1 function Device( opts ) { this.value = null; // Отворете асинхронен поток, // това ще се извиква продължително stream.read( opts.path, function( data ) { // Обновете текущата стойност на инстанцията // с последната стойност от // потока на данни this.value = data; }.bind(this) ); // Ограничете честотата на събитията изпратени от // инстанцията на Device setInterval(function() { // Изпращане на събитие this.emit("event"); }.bind(this), opts.freq || 100 ); } // Просто се преструвайте, че сме наследили EventEmitter ;)
Когато не е достъпен фукнционалния еквивалент на
.bind
съществува в много модерни JavaScript библиотеки.// 6.B.2 // например lodash/underscore, _.bind() function Device( opts ) { this.value = null; stream.read( opts.path, _.bind(function( data ) { this.value = data; }, this) ); setInterval(_.bind(function() { this.emit("event"); }, this), opts.freq || 100 ); } // например jQuery.proxy function Device( opts ) { this.value = null; stream.read( opts.path, jQuery.proxy(function( data ) { this.value = data; }, this) ); setInterval( jQuery.proxy(function() { this.emit("event"); }, this), opts.freq || 100 ); } // например dojo.hitch function Device( opts ) { this.value = null; stream.read( opts.path, dojo.hitch( this, function( data ) { this.value = data; }) ); setInterval( dojo.hitch( this, function() { this.emit("event"); }), opts.freq || 100 ); }
В краен случай, създайте алиас на
this
използвайкиself
като идентификатор. Това е изключително податливо на грешки и трябва да се избягва, когато е възможно.// 6.B.3 function Device( opts ) { var self = this; this.value = null; stream.read( opts.path, function( data ) { self.value = data; }); setInterval(function() { self.emit("event"); }, opts.freq || 100 ); }
C. Използване на
thisArg
Няколко прототипни метода вградени в ES 5.1 идват със специалния
thisArg
, който трябва да се използва когато е възможно// 6.C.1 var obj; obj = { f: "foo", b: "bar", q: "qux" }; Object.keys( obj ).forEach(function( key ) { // |this| сочи към `obj` console.log( this[ key ] ); }, obj ); // <-- последния аргумент е `thisArg` // Ще изпечата... // "foo" // "bar" // "qux"
thisArg
може да се използваArray.prototype.every
,Array.prototype.forEach
,Array.prototype.some
,Array.prototype.map
,Array.prototype.filter
-
Този раздел ще служи за илюстриране на идеи и концепции, които не трябва да се считат за догма, вместо това съществува за да даде под съмнение практиките в опит да се намери по-добри начини за изпълнение, на често срещани JavaScript задачи.
A. Използването на
switch
трябва да се избягва, тъй като съвременния метод за отстраняване на грешки ще скрие повикванията на функции, които го използват.Изглежда че последните версии на браузърите Firefox и Chrome имат значителни подобрения в работата на функциите, които използват
switch
. http://jsperf.com/switch-vs-object-literal-vs-moduleЗабележими подобрения могат да се видят също и тук: #13
// 7.A.1.1 // Пример на оператора switch switch( foo ) { case "alpha": alpha(); break; case "beta": beta(); break; default: // код по подразбиране break; } // 7.A.1.2 // Алтернативен подход, който подкрепя използваемостта и повторната употреба е да // се използва обект за съхранение на "случаи" и функция за делегация var cases, delegator; // Примерът връща само с илюстративни цели. cases = { alpha: function() { // изрази // връщане return [ "Alpha", arguments.length ]; }, beta: function() { // изрази // връщане return [ "Beta", arguments.length ]; }, _default: function() { // изрази // връщане return [ "Default", arguments.length ]; } }; delegator = function() { var args, key, delegate; // Трансформиране на списъка с аргументите в масив args = [].slice.call( arguments ); // Променяне на ключа на случая от аргументите key = args.shift(); // Определяне на ключа по подразбиране delegate = cases._default; // Извличане на метода за да делегиране на операция if ( cases.hasOwnProperty( key ) ) { delegate = cases[ key ]; } // Обхватния аргумент може да бъде зададен към нещо специфични, // в този случай, |null| ще бъде достатъчно return delegate.apply( null, args ); }; // 7.A.1.3 // Сложете Апито в 7.A.1.2 да работи: delegator( "alpha", 1, 2, 3, 4, 5 ); // [ "Alpha", 5 ] // Разбира се, ключовият аргумент на `случая` може да се основава лесно // при някакво друго произволно условие. var caseKey, someUserInput; // Вероятно някакво поле на форма? someUserInput = 9; if ( someUserInput > 10 ) { caseKey = "alpha"; } else { caseKey = "beta"; } // или... caseKey = someUserInput > 10 ? "alpha" : "beta"; // И тогава... delegator( caseKey, someUserInput ); // [ "Beta", 1 ] // И разбира се... delegator(); // [ "Default", 0 ]
B. Ранните връщания подобряват четимостта на кода с незначителна разлика в производителността
// 7.B.1.1 // Лош пример: function returnLate( foo ) { var ret; if ( foo ) { ret = "foo"; } else { ret = "quux"; } return ret; } // Добър пример: function returnEarly( foo ) { if ( foo ) { return "foo"; } return "quux"; }
-
Основния принципт тук е:
За да подсилите тази концепция, моля гледайте последващата презентация:
https://www.youtube.com/watch?v=xL3xCO7CLNM
-
Програмите трябва да бъдат написани на един език, независимо какъв е той, както се изисква от хората, които поддържат кода.
Всеки проект, който се отнася до този документ като основно ръководство за стил, няма да приеме форматирането на кода "първа запетая", освен ако авторът на проекта конкретно не посочи това.
Principles of Writing Consistent, Idiomatic JavaScript by Rick Waldron and Contributors is licensed under a Creative Commons Attribution 3.0 Unported License.
Based on a work at github.com/rwldrn/idiomatic.js.