Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Перевод «JavaScript Style Guide» от Airbnb

NotificationsYou must be signed in to change notification settings

leonidlebedev/javascript-airbnb

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Наиболее разумный подход к написанию JavaScript-кода

Замечание: это руководство подразумевает использованиеBabel вместе сbabel-preset-airbnb или аналогом. Оно также предполагает установленный shims/polyfills в вашем приложении, такой какairbnb-browser-shims или аналог.

СкачатьСкачатьGitter

Это руководство также доступно на других языках. СмотритеПереводы.

Другие руководства

Оглавление

  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. jQuery
  27. Поддержка ECMAScript 5
  28. Возможности ECMAScript 6+ (ES 2015+)
  29. Стандартная библиотека
  30. Тестирование
  31. Производительность
  32. Ресурсы
  33. В реальной жизни
  34. Переводы
  35. Пообщаться с разработчиками Airbnb
  36. Участники перевода
  37. Лицензия
  38. Поправки

  • 1.1Простые типы: Когда вы взаимодействуете с простым типом, вы напрямую работаете с его значением.

    • string
    • number
    • boolean
    • null
    • undefined
    • symbol
    • bigint
    constfoo=1;letbar=foo;bar=9;console.log(foo,bar);// => 1, 9
    • Symbol и BigInt не могут быть полностью заполифиллены, поэтому они не должны использоваться, если разработка ведётся для браузеров или других сред, которые не поддерживают их нативно.

  • 1.2Сложные типы: Когда вы взаимодействуете со сложным типом, вы работаете со ссылкой на его значение.

    • object
    • array
    • function
    constfoo=[1,2];constbar=foo;bar[0]=9;console.log(foo[0],bar[0]);// => 9, 9

⬆ к оглавлению

  • 2.1 Используйтеconst для объявления переменных; избегайтеvar. eslint:prefer-const,no-const-assign

    Почему? Это гарантирует, что вы не сможете переопределять значения, т.к. это может привести к ошибкам и к усложнению понимания кода.

    // плохоvara=1;varb=2;// хорошоconsta=1;constb=2;

  • 2.2 Если вам необходимо переопределять значения, то используйтеlet вместоvar. eslint:no-var

    Почему? Область видимостиlet — блок, уvar — функция.

    // плохоvarcount=1;if(true){count+=1;}// хорошо, используйте let.letcount=1;if(true){count+=1;}

  • 2.3 Помните, что уlet иconst блочная область видимости, в то время какvar имеет функциональную область видимости.

    // const и let существуют только в том блоке, в котором они определены.{leta=1;constb=1;varc=1;}console.log(a);// ReferenceErrorconsole.log(b);// ReferenceErrorconsole.log(c);// 1

    В приведённом выше коде вы можете видеть, что ссылки наa иb приведут к ошибкеReferenceError, в то время какc содержит число. Это связано с тем, чтоa иb имеют блочную область видимости, в то время как уc функциональная.

⬆ к оглавлению

  • 3.1 Для создания объекта используйте литеральную нотацию. eslint:no-new-object

    // плохоconstitem=newObject();// хорошоconstitem={};

  • 3.2 Используйте вычисляемые имена свойств, когда создаёте объекты с динамическими именами свойств.

    Почему? Они позволяют вам определить все свойства объекта в одном месте.

    functiongetKey(k){return`a key named${k}`;}// плохоconstobj={id:5,name:'San Francisco',};obj[getKey('enabled')]=true;// хорошоconstobj={id:5,name:'San Francisco',[getKey('enabled')]:true,};

  • 3.3 Используйте сокращённую запись метода объекта. eslint:object-shorthand

    // плохоconstatom={value:1,addValue:function(value){returnatom.value+value;},};// хорошоconstatom={value:1,addValue(value){returnatom.value+value;},};

  • 3.4 Используйте сокращённую запись свойств объекта. eslint:object-shorthand

    Почему? Это короче и понятнее.

    constlukeSkywalker='Luke Skywalker';// плохоconstobj={lukeSkywalker:lukeSkywalker,};// хорошоconstobj={  lukeSkywalker,};

  • 3.5 Группируйте ваши сокращённые записи свойств в начале объявления объекта.

    Почему? Так легче сказать, какие свойства используют сокращённую запись.

    constanakinSkywalker='Anakin Skywalker';constlukeSkywalker='Luke Skywalker';// плохоconstobj={episodeOne:1,twoJediWalkIntoACantina:2,  lukeSkywalker,episodeThree:3,mayTheFourth:4,  anakinSkywalker,};// хорошоconstobj={  lukeSkywalker,  anakinSkywalker,episodeOne:1,twoJediWalkIntoACantina:2,episodeThree:3,mayTheFourth:4,};

  • 3.6 Только недопустимые идентификаторы помещаются в кавычки. eslint:quote-props

    Почему? На наш взгляд, такой код легче читать. Это улучшает подсветку синтаксиса, а также облегчает оптимизацию для многих JS-движков.

    // плохоconstbad={'foo':3,'bar':4,'data-blah':5,};// хорошоconstgood={foo:3,bar:4,'data-blah':5,};

  • 3.7 Не вызывайте напрямую методыObject.prototype, такие какhasOwnProperty,propertyIsEnumerable, иisPrototypeOf. eslint:no-prototype-builtins

    Почему? Эти методы могут быть переопределены в свойствах объекта, который мы проверяем{ hasOwnProperty: false }, или этот объект может бытьnull (Object.create(null)).

    // плохоconsole.log(object.hasOwnProperty(key));// хорошоconsole.log(Object.prototype.hasOwnProperty.call(object,key));// отличноconsthas=Object.prototype.hasOwnProperty;// Кэшируем запрос в рамках модуля.console.log(has.call(object,key));/* или */importhasfrom'has';// https://www.npmjs.com/package/hasconsole.log(has(object,key));

  • 3.8 Используйте синтаксис расширения вместоObject.assign для поверхностного копирования объектов. Используйте параметр оставшихся свойств, чтобы получить новый объект с некоторыми опущенными свойствами. eslint:prefer-object-spread

    // очень плохоconstoriginal={a:1,b:2};constcopy=Object.assign(original,{c:3});// эта переменная изменяет `original` ಠ_ಠdeletecopy.a;// если сделать так// плохоconstoriginal={a:1,b:2};constcopy=Object.assign({},original,{c:3});// copy => { a: 1, b: 2, c: 3 }// хорошоconstoriginal={a:1,b:2};constcopy={ ...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

    // плохоconstitems=newArray();// хорошоconstitems=[];

  • 4.2 Для добавления элемента в массив используйтеArray#push вместо прямого присваивания.

    constsomeStack=[];// плохоsomeStack[someStack.length]='abracadabra';// хорошоsomeStack.push('abracadabra');

  • 4.3 Для копирования массивов используйте оператор расширения....

    // плохоconstlen=items.length;constitemsCopy=[];leti;for(i=0;i<len;i+=1){itemsCopy[i]=items[i];}// хорошоconstitemsCopy=[...items];

  • 4.4 Для преобразования итерируемого объекта в массив используйте оператор расширения... вместоArray.from.

    constfoo=document.querySelectorAll('.foo');// хорошоconstnodes=Array.from(foo);// отличноconstnodes=[...foo];

  • 4.5 ИспользуйтеArray.from для преобразования массивоподобного объекта в массив.

    constarrLike={0:'foo',1:'bar',2:'baz',length:3};// плохоconstarr=Array.prototype.slice.call(arrLike);// хорошоconstarr=Array.from(arrLike);

  • 4.6 ИспользуйтеArray.from вместо оператора расширения... для маппинга итерируемых объектов, это позволяет избежать создания промежуточного массива.

    // плохоconstbaz=[...foo].map(bar);// хорошоconstbaz=Array.from(foo,bar);

  • 4.7 Используйте операторыreturn внутри функций обратного вызова в методах массива. Можно опуститьreturn, когда тело функции состоит из одной инструкции, возвращающей выражение без побочных эффектов.8.2. eslint:array-callback-return

    // хорошо[1,2,3].map((x)=>{consty=x+1;returnx*y;});// хорошо[1,2,3].map((x)=>x+1);// плохо - нет возвращаемого значения, следовательно, `acc` становится `undefined` после первой итерации[[0,1],[2,3],[4,5]].reduce((acc,item,index)=>{constflatten=acc.concat(item);});// хорошо[[0,1],[2,3],[4,5]].reduce((acc,item,index)=>{constflatten=acc.concat(item);returnflatten;});// плохоinbox.filter((msg)=>{const{ subject, author}=msg;if(subject==='Mockingbird'){returnauthor==='Harper Lee';}else{returnfalse;}});// хорошоinbox.filter((msg)=>{const{ subject, author}=msg;if(subject==='Mockingbird'){returnauthor==='Harper Lee';}returnfalse;});

  • 4.8 Если массив располагается на нескольких строках, то используйте разрывы строк после открытия и перед закрытием скобок.

    // плохоconstarr=[[0,1],[2,3],[4,5],];constobjectInArray=[{id:1,},{id:2,}];constnumberInArray=[1,2,];// хорошоconstarr=[[0,1],[2,3],[4,5]];constobjectInArray=[{id:1,},{id:2,},];constnumberInArray=[1,2,];

⬆ к оглавлению

  • 5.1 При обращении к нескольким свойствам объекта используйте деструктуризацию объекта. eslint:prefer-destructuring

    Почему? Деструктуризация сохраняет вас от создания временных переменных для этих свойств и от повторного доступа к объекту. Повторный доступ к объектам создаёт более повторяющийся код, требует большего чтения и создаёт больше возможностей для ошибок. Деструктуризация объектов также обеспечивает единое местоположение определения структуры объекта, которое используется в блоке, вместо того, чтобы требовать чтения всего блока для определения того, что используется.

    // плохоfunctiongetFullName(user){constfirstName=user.firstName;constlastName=user.lastName;return`${firstName}${lastName}`;}// хорошоfunctiongetFullName(user){const{ firstName, lastName}=user;return`${firstName}${lastName}`;}// отличноfunctiongetFullName({ firstName, lastName}){return`${firstName}${lastName}`;}

  • 5.2 Используйте деструктуризацию массивов. eslint:prefer-destructuring

    constarr=[1,2,3,4];// плохоconstfirst=arr[0];constsecond=arr[1];// хорошоconst[first,second]=arr;

  • 5.3 Используйте деструктуризацию объекта для множества возвращаемых значений, но не делайте тоже самое с массивами.

    Почему? Вы сможете добавить новые свойства через некоторое время или изменить порядок без последствий.

    // плохоfunctionprocessInput(input){// затем происходит чудоreturn[left,right,top,bottom];}// при вызове нужно подумать о порядке возвращаемых данныхconst[left,__,top]=processInput(input);// хорошоfunctionprocessInput(input){// затем происходит чудоreturn{ left, right, top, bottom};}// при вызове выбираем только необходимые данныеconst{ left, top}=processInput(input);

⬆ к оглавлению

  • 6.1 Используйте одинарные кавычки'' для строк. eslint:quotes

    // плохоconstname="Capt. Janeway";// плохо - литерал шаблонной строки должен содержать интерполяцию или переводы строкconstname=`Capt. Janeway`;// хорошоconstname='Capt. Janeway';

  • 6.2 Строки, у которых в строчке содержится более 100 символов, не пишутся на нескольких строчках с использованием конкатенации.

    Почему? Работать с разбитыми строками неудобно и это затрудняет поиск по коду.

    // плохоconsterrorMessage='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.';// плохоconsterrorMessage='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.';// хорошоconsterrorMessage='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-templatetemplate-curly-spacing

    Почему? Шаблонные строки дают вам читабельность, лаконичный синтаксис с правильными символами перевода строк и функции интерполяции строки.

    // плохоfunctionsayHi(name){return'How are you, '+name+'?';}// плохоfunctionsayHi(name){return['How are you, ',name,'?'].join();}// плохоfunctionsayHi(name){return`How are you,${name}?`;}// хорошоfunctionsayHi(name){return`How are you,${name}?`;}

  • 6.4 Никогда не используйтеeval(), т.к. это открывает множество уязвимостей. eslint:no-eval

  • 6.5 Не используйте в строках необязательные экранирующие символы. eslint:no-useless-escape

    Почему? Обратные слеши ухудшают читабельность, поэтому они должны быть только при необходимости.

    // плохоconstfoo='\'this\' \i\s \"quoted\"';// хорошоconstfoo='\'this\' is "quoted"';constfoo=`my name is '${name}'`;

⬆ к оглавлению

  • 7.1 Используйте функциональные выражения вместо объявлений функций. eslint:func-style

    Почему? У объявлений функций есть подъём. Это означает, что можно использовать функцию до того, как она определена в файле, но это вредит читабельности и поддержке. Если вы обнаружили, что определение функции настолько большое или сложное, что мешает понимать остальную часть файла, то, возможно, пришло время извлечь его в отдельный модуль. Не забудьте явно назвать функциональное выражение, независимо от того, подразумевается ли имя из содержащейся переменной (такое часто бывает в современных браузерах или при использовании компиляторов, таких как Babel). Это помогает точнее определять место ошибки по стеку вызовов. (Обсуждение)

    // плохоfunctionfoo(){// ...}// плохоconstfoo=function(){// ...};// хорошо// лексическое имя, отличное от вызываемой(-ых) переменной(-ых)constfoo=functionuniqueMoreDescriptiveLexicalFoo(){// ...};

  • 7.2 Оборачивайте в скобки немедленно вызываемые функции. eslint:wrap-iife

    Почему? Немедленно вызываемая функция представляет собой единый блок. Чтобы чётко показать это — оберните функцию и вызывающие скобки в ещё одни скобки. Обратите внимание, что в мире с модулями вам больше не нужны немедленно вызываемые функции.

    // Немедленно вызываемая функция(function(){console.log('Welcome to the Internet. Please follow me.');}());

  • 7.3 Никогда не объявляйте функции в нефункциональном блоке (if,while, и т.д.). Вместо этого присвойте функцию переменной. Браузеры позволяют выполнить ваш код, но все они интерпретируют его по-разному. eslint:no-loop-func

  • 7.4Примечание: ECMA-262 определяетблок как список инструкций. Объявление функции не является инструкцией.

    // плохоif(currentUser){functiontest(){console.log('Nope.');}}// хорошоlettest;if(currentUser){test=()=>{console.log('Yup.');};}

  • 7.5 Никогда не называйте параметрarguments. Он будет иметь приоритет над объектомarguments, который доступен для каждой функции.

    // плохоfunctionfoo(name,options,arguments){// ...}// хорошоfunctionfoo(name,options,args){// ...}

  • 7.6 Никогда не используйтеarguments, вместо этого используйте синтаксис оставшихся параметров.... eslint:prefer-rest-params

    Почему?... явно говорит о том, какие именно аргументы вы хотите извлечь. Кроме того, такой синтаксис создаёт настоящий массив, а не массивоподобный объект какarguments.

    // плохоfunctionconcatenateAll(){constargs=Array.prototype.slice.call(arguments);returnargs.join('');}// хорошоfunctionconcatenateAll(...args){returnargs.join('');}

  • 7.7 Используйте синтаксис записи аргументов по умолчанию, а не изменяйте аргументы функции.

    // очень плохоfunctionhandleThings(opts){// Нет! Мы не должны изменять аргументы функции.// Плохо вдвойне: если переменная opts будет ложной,// то ей присвоится пустой объект, а не то что вы хотели.// Это приведёт к коварным ошибкам.opts=opts||{};// ...}// всё ещё плохоfunctionhandleThings(opts){if(opts===void0){opts={};}// ...}// хорошоfunctionhandleThings(opts={}){// ...}

  • 7.8 Избегайте побочных эффектов с параметрами по умолчанию.

    Почему? И так всё понятно.

    varb=1;// плохоfunctioncount(a=b++){console.log(a);}count();// 1count();// 2count(3);// 3count();// 3

  • 7.9 Всегда вставляйте последними параметры по умолчанию. eslint:default-param-last

    // плохоfunctionhandleThings(opts={},name){// ...}// хорошоfunctionhandleThings(name,opts={}){// ...}

  • 7.10 Никогда не используйте конструктор функций для создания новых функий. eslint:no-new-func

    Почему? Создание функции в таком духе вычисляет строку подобноeval(), из-за чего открываются уязвимости.

    // плохоvaradd=newFunction('a','b','return a + b');// всё ещё плохоvarsubtract=Function('a','b','return a - b');

  • 7.11 Отступы при определении функции. eslint:space-before-function-parenspace-before-blocks

    Почему? Однородность кода — это хорошо. Вам не надо будет добавлять или удалять пробел при манипуляции с именем.

    // плохоconstf=function(){};constg=function(){};consth=function(){};// хорошоconstx=function(){};consty=functiona(){};

  • 7.12 Никогда не изменяйте параметры. eslint:no-param-reassign

    Почему? Манипуляция объектами, приходящими в качестве параметров, может вызывать нежелательные побочные эффекты в вызывающей функции.

    // плохоfunctionf1(obj){obj.key=1;}// хорошоfunctionf2(obj){constkey=Object.prototype.hasOwnProperty.call(obj,'key') ?obj.key :1;}

  • 7.13 Никогда не переназначайте параметры. eslint:no-param-reassign

    Почему? Переназначенные параметры могут привести к неожиданному поведению, особенно при обращении кarguments. Это также может вызывать проблемы оптимизации, особенно в V8.

    // плохоfunctionf1(a){a=1;// ...}functionf2(a){if(!a){a=1;}// ...}// хорошоfunctionf3(a){constb=a||1;// ...}functionf4(a=1){// ...}

  • 7.14 Отдавайте предпочтение использованию синтаксис расширения... при вызове вариативной функции. eslint:prefer-spread

    Почему? Это чище, вам не нужно предоставлять контекст, и не так просто составитьnew сapply.

    // плохоconstx=[1,2,3,4,5];console.log.apply(console,x);// хорошоconstx=[1,2,3,4,5];console.log(...x);// плохоnew(Function.prototype.bind.apply(Date,[null,2016,8,5]));// хорошоnewDate(...[2016,8,5]);

  • 7.15 Функции с многострочным определением или запуском должны содержать такие же отступы, как и другие многострочные списки в этом руководстве: с каждым элементом на отдельной строке, с запятой в конце элемента. eslint:function-paren-newline

    // плохоfunctionfoo(bar,baz,quux){// ...}// хорошоfunctionfoo(bar,baz,quux,){// ...}// плохоconsole.log(foo,bar,baz);// хорошоconsole.log(foo,bar,baz,);

⬆ к оглавлению

  • 8.1 Когда вам необходимо использовать анонимную функцию (например, при передаче встроенной функции обратного вызова), используйте стрелочную функцию. eslint:prefer-arrow-callback,arrow-spacing

    Почему? Таким образом создаётся функция, которая выполняется в контекстеthis, который мы обычно хотим, а также это более короткий синтаксис.

    Почему бы и нет? Если у вас есть довольно сложная функция, вы можете переместить эту логику внутрь её собственного именованного функционального выражения.

    // плохо[1,2,3].map(function(x){consty=x+1;returnx*y;});// хорошо[1,2,3].map((x)=>{consty=x+1;returnx*y;});

  • 8.2 Если тело функции состоит из одного оператора, возвращающеговыражение без побочных эффектов, то опустите фигурные скобки и используйте неявное возвращение. В противном случае, сохраните фигурные скобки и используйте операторreturn. eslint:arrow-parens,arrow-body-style

    Почему? Синтаксический сахар. Когда несколько функций соединены вместе, то это лучше читается.

    // плохо[1,2,3].map((number)=>{constnextNumber=number+1;`A string containing the${nextNumber}.`;});// хорошо[1,2,3].map((number)=>`A string containing the${number+1}.`);// хорошо[1,2,3].map((number)=>{constnextNumber=number+1;return`A string containing the${nextNumber}.`;});// хорошо[1,2,3].map((number,index)=>({[index]:number,}));// Неявный возврат с побочными эффектамиfunctionfoo(callback){constval=callback();if(val===true){// Сделать что-то, если функция обратного вызова вернёт true}}letbool=false;// плохоfoo(()=>bool=true);// хорошоfoo(()=>{bool=true;});

  • 8.3 В случае, если выражение располагается на нескольких строках, то необходимо обернуть его в скобки для лучшей читаемости.

    Почему? Это чётко показывает, где функция начинается и где заканчивается.

    // плохо['get','post','put'].map((httpMethod)=>Object.prototype.hasOwnProperty.call(httpMagicObjectWithAVeryLongName,httpMethod,));// хорошо['get','post','put'].map((httpMethod)=>(Object.prototype.hasOwnProperty.call(httpMagicObjectWithAVeryLongName,httpMethod,)));

  • 8.4 Всегда оборачивайте аргументы круглыми скобками для ясности и согласованности. eslint:arrow-parens

    Почему? Минимизирует различия при удалении или добавлении аргументов.

    // плохо[1,2,3].map(x=>x*x);// хорошо[1,2,3].map((x)=>x*x);// плохо[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!`));// хорошо[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!`));// плохо[1,2,3].map(x=>{consty=x+1;returnx*y;});// хорошо[1,2,3].map((x)=>{consty=x+1;returnx*y;});

  • 8.5 Избегайте схожести стрелочной функции (=>) с операторами сравнения (<=,>=). eslint:no-confusing-arrow

    // плохоconstitemHeight=(item)=>item.height<=256 ?item.largeSize :item.smallSize;// плохоconstitemHeight=(item)=>item.height>=256 ?item.largeSize :item.smallSize;// хорошоconstitemHeight=(item)=>(item.height<=256 ?item.largeSize :item.smallSize);// хорошоconstitemHeight=(item)=>{const{ height, largeSize, smallSize}=item;returnheight<=256 ?largeSize :smallSize;};

  • 8.6 Зафиксируйте расположение тела стрелочной функции с неявным возвратом. eslint:implicit-arrow-linebreak

    // плохо(foo)=>bar;(foo)=>(bar);// хорошо(foo)=>bar;(foo)=>(bar);(foo)=>(bar)

⬆ к оглавлению

  • 9.1 Всегда используйтеclass. Избегайте прямых манипуляций сprototype.

    Почему? Синтаксисclass является кратким и понятным.

    // плохоfunctionQueue(contents=[]){this.queue=[...contents];}Queue.prototype.pop=function(){constvalue=this.queue[0];this.queue.splice(0,1);returnvalue;};// хорошоclassQueue{constructor(contents=[]){this.queue=[...contents];}pop(){constvalue=this.queue[0];this.queue.splice(0,1);returnvalue;}}

  • 9.2 Используйтеextends для наследования.

    Почему? Это встроенный способ наследовать функциональность прототипа, не нарушаяinstanceof.

    // плохоconstinherits=require('inherits');functionPeekableQueue(contents){Queue.apply(this,contents);}inherits(PeekableQueue,Queue);PeekableQueue.prototype.peek=function(){returnthis.queue[0];};// хорошоclassPeekableQueueextendsQueue{peek(){returnthis.queue[0];}}

  • 9.3 Методы могут возвращатьthis, чтобы делать цепочки вызовов.

    // плохоJedi.prototype.jump=function(){this.jumping=true;returntrue;};Jedi.prototype.setHeight=function(height){this.height=height;};constluke=newJedi();luke.jump();// => trueluke.setHeight(20);// => undefined// хорошоclassJedi{jump(){this.jumping=true;returnthis;}setHeight(height){this.height=height;returnthis;}}constluke=newJedi();luke.jump().setHeight(20);

  • 9.4 Вы можете определить свой собственный методtoString(), просто убедитесь, что он успешно работает и не создаёт никаких побочных эффектов.

    classJedi{constructor(options={}){this.name=options.name||'no name';}getName(){returnthis.name;}toString(){return`Jedi -${this.getName()}`;}}

  • 9.5 У классов есть конструктор по умолчанию, если он не задан явно. Можно опустить пустой конструктор или конструктор, который только делегирует выполнение родительскому классу. eslint:no-useless-constructor

    // плохоclassJedi{constructor(){}getName(){returnthis.name;}}// плохоclassReyextendsJedi{constructor(...args){super(...args);}}// хорошоclassReyextendsJedi{constructor(...args){super(...args);this.name='Rey';}}

  • 9.6 Избегайте дублирующих членов класса. eslint:no-dupe-class-members

    Почему? Если объявление члена класса повторяется, без предупреждения будет использовано последнее. Наличие дубликатов скорее всего приведёт к ошибке.

    // плохоclassFoo{bar(){return1;}bar(){return2;}}// хорошоclassFoo{bar(){return1;}}// хорошоclassFoo{bar(){return2;}}

  • 9.7 Метод класса должен использоватьthis или быть преобразованным в статический метод, если только внешняя библиотека или фреймворк не требуют использования определённых нестатических методов. Будучи методом экземпляра, следует указать, что он ведёт себя по-разному в зависимости от свойств получателя. eslint:class-methods-use-this

    // плохоclassFoo{bar(){console.log('bar');}}// хорошо - используется thisclassFoo{bar(){console.log(this.bar);}}// хорошо - конструктор освобождаетсяclassFoo{constructor(){// ...}}// хорошо - статические методы не должны использовать thisclassFoo{staticbar(){console.log('bar');}}

⬆ к оглавлению

  • 10.1 Всегда используйте модули (import/export) вместо нестандартных модульных систем. Вы всегда сможете транспилировать код в вашу любимую модульную систему.

    Почему? Модули — это будущее. Давайте начнём использовать будущее уже сейчас!

    // плохоconstAirbnbStyleGuide=require('./AirbnbStyleGuide');module.exports=AirbnbStyleGuide.es6;// okimportAirbnbStyleGuidefrom'./AirbnbStyleGuide';exportdefaultAirbnbStyleGuide.es6;// отличноimport{es6}from'./AirbnbStyleGuide';exportdefaultes6;

  • 10.2 Не используйте импорт через*.

    Почему? Это гарантирует, что у вас есть единственный экспорт по умолчанию.

    // плохоimport*asAirbnbStyleGuidefrom'./AirbnbStyleGuide';// хорошоimportAirbnbStyleGuidefrom'./AirbnbStyleGuide';

  • 10.3 Не экспортируйте прямо из импорта.

    Почему? Несмотря на то, что запись в одну строку является краткой, разделение на отдельные строки делает вещи последовательными.

    // плохо// файл es6.jsexport{es6asdefault}from'./AirbnbStyleGuide';// хорошо// файл es6.jsimport{es6}from'./AirbnbStyleGuide';exportdefaultes6;

  • 10.4 Импортируйте из пути только один раз.eslint:no-duplicate-imports

    Почему? Наличие нескольких строк, которые импортируют из одного и того же файла, может сделать код неподдерживаемым.

    // плохоimportfoofrom'foo';// … какие-то другие импорты … //import{named1,named2}from'foo';// хорошоimportfoo,{named1,named2}from'foo';// хорошоimportfoo,{named1,named2,}from'foo';

  • 10.5 Не экспортируйте изменяемые переменные.eslint:import/no-mutable-exports

    Почему? Вообще, следует избегать мутации, в особенности при экспорте изменяемых переменных. Несмотря на то, что эта техника может быть необходима в редких случаях, в основном только константа должна быть экспортирована.

    // плохоletfoo=3;export{foo};// хорошоconstfoo=3;export{foo};

  • 10.6 В модулях с единственным экспортом предпочтительнее использовать экспорт по умолчанию, а не экспорт по имени.eslint:import/prefer-default-export

    Почему? Для того чтобы поощрять создание множества файлов, которые бы экспортировали одну сущность, т.к. это лучше для читабельности и поддержки кода.

    // плохоexportfunctionfoo(){}// хорошоexportdefaultfunctionfoo(){}

  • 10.7 Поместите все импорты выше остальных инструкций.eslint:import/first

    Почему? Так какimport обладает подъёмом, то хранение их всех в начале файла предотвращает от неожиданного поведения.

    // плохоimportfoofrom'foo';foo.init();importbarfrom'bar';// хорошоimportfoofrom'foo';importbarfrom'bar';foo.init();

  • 10.8 Импорты на нескольких строках должны быть с отступами как у многострочных литералов массива и объекта. eslint:object-curly-newline

    Почему? Фигурные скобки следуют тем же правилам отступа как и любая другая фигурная скобка блока в этом руководстве, тоже самое касается висячих запятых.

    // плохоimport{longNameA,longNameB,longNameC,longNameD,longNameE}from'path';// хорошоimport{longNameA,longNameB,longNameC,longNameD,longNameE,}from'path';

  • 10.9 Запретите синтаксис загрузчика Webpack в импорте.eslint:import/no-webpack-loader-syntax

    Почему? Использование Webpack синтаксиса связывает код с упаковщиком модулей. Предпочтительно использовать синтаксис загрузчика вwebpack.config.js.

    // плохоimportfooSassfrom'css!sass!foo.scss';importbarCssfrom'style!css!bar.css';// хорошоimportfooSassfrom'foo.scss';importbarCssfrom'bar.css';

  • 10.10 Не указывайте JavaScript расширения файловeslint:import/extensions

    Почему? Добавление расширений препятствует рефакторингу и нецелесообразно жёстко программируются детали реализации модуля, который вы импортируете в каждом потребителе.

    // плохоimportfoofrom'./foo.js';importbarfrom'./bar.jsx';importbazfrom'./baz/index.jsx';// хорошоimportfoofrom'./foo';importbarfrom'./bar';importbazfrom'./baz';

⬆ к оглавлению

  • 11.1 Не используйте итераторы. Применяйте функции высшего порядка вместо таких циклов какfor-in илиfor-of. eslint:no-iteratorno-restricted-syntax

    Почему? Это обеспечивает соблюдение нашего правила о неизменности переменных. Работать с чистыми функциями, которые возвращают значение, проще, чем с функциями с побочными эффектами.

    Используйтеmap() /every() /filter() /find() /findIndex() /reduce() /some() / ... для итерации по массивам, аObject.keys() /Object.values() /Object.entries() для создания массивов, с помощью которых можно итерироваться по объектам.

    constnumbers=[1,2,3,4,5];// плохоletsum=0;for(letnumofnumbers){sum+=num;}sum===15;// хорошоletsum=0;numbers.forEach((num)=>{sum+=num;});sum===15;// отлично (используйте силу функций)constsum=numbers.reduce((total,num)=>total+num,0);sum===15;// плохоconstincreasedByOne=[];for(leti=0;i<numbers.length;i++){increasedByOne.push(numbers[i]+1);}// хорошоconstincreasedByOne=[];numbers.forEach((num)=>{increasedByOne.push(num+1);});// отлично (продолжайте в том же духе)constincreasedByOne=numbers.map((num)=>num+1);

  • 11.2 Не используйте пока генераторы.

    Почему? Они не очень хорошо транспилируются в ES5.

  • 11.3 Если всё-таки необходимо использовать генераторы, или вы не обратили внимания нанаш совет, убедитесь, что* у функции генератора расположена должным образом. eslint:generator-star-spacing

    Почему?function и* являются частью одного и того же ключевого слова.* не является модификатором дляfunction,function* является уникальной конструкцией, отличной отfunction.

    // плохоfunction*foo(){// ...}constbar=function*(){// ...};constbaz=function*(){// ...};constquux=function*(){// ...};function*foo(){// ...}function*foo(){// ...}// очень плохоfunction*foo(){// ...}constwat=function*(){// ...};// хорошоfunction*foo(){// ...}constfoo=function*(){// ...};

⬆ к оглавлению

  • 12.1 Используйте точечную нотацию для доступа к свойствам. eslint:dot-notation

    constluke={jedi:true,age:28,};// плохоconstisJedi=luke['jedi'];// хорошоconstisJedi=luke.jedi;

  • 12.2 Используйте скобочную нотацию[], когда название свойства хранится в переменной.

    constluke={jedi:true,age:28,};functiongetProp(prop){returnluke[prop];}constisJedi=getProp('jedi');

  • 12.3 Используйте оператор** для возведения в степень. eslint:no-restricted-properties.

    // плохоconstbinary=Math.pow(2,10);// хорошоconstbinary=2**10;

⬆ к оглавлению

  • 13.1 Всегда используйтеconst илиlet для объявления переменных. Невыполнение этого требования приведёт к появлению глобальных переменных. Необходимо избегать загрязнения глобального пространства имён. eslint:no-undefprefer-const

    // плохоsuperPower=newSuperPower();// хорошоconstsuperPower=newSuperPower();

  • 13.2 Используйте объявлениеconst илиlet для каждой переменной или присвоения. eslint:one-var

    Почему? Таким образом проще добавить новые переменные. Также вы никогда не будете беспокоиться о перемещении; и, и об отображении изменений в пунктуации. Вы также можете пройтись по каждому объявлению с помощью отладчика, вместо того, чтобы прыгать через все сразу.

    // плохоconstitems=getItems(),goSportsTeam=true,dragonball='z';// плохо// (сравните с кодом выше и попытайтесь найти ошибку)constitems=getItems(),goSportsTeam=true;dragonball='z';// хорошоconstitems=getItems();constgoSportsTeam=true;constdragonball='z';

  • 13.3 В первую очередь группируйтеconst, а затемlet.

    Почему? Это полезно, когда в будущем вам понадобится создать переменную, зависимую от предыдущих.

    // плохоleti,len,dragonball,items=getItems(),goSportsTeam=true;// плохоleti;constitems=getItems();letdragonball;constgoSportsTeam=true;letlen;// хорошоconstgoSportsTeam=true;constitems=getItems();letdragonball;leti;letlength;

  • 13.4 Создавайте переменные там, где они вам необходимы, но помещайте их в подходящее место.

    Почему?let иconst имеют блочную область видимости, а не функциональную.

    // плохо - вызов ненужной функцииfunctioncheckName(hasName){constname=getName();if(hasName==='test'){returnfalse;}if(name==='test'){this.setName('');returnfalse;}returnname;}// хорошоfunctioncheckName(hasName){if(hasName==='test'){returnfalse;}constname=getName();if(name==='test'){this.setName('');returnfalse;}returnname;}

  • 13.5 Не создавайте цепочки присваивания переменных. eslint:no-multi-assign

    Почему? Такие цепочки создают неявные глобальные переменные.

    // плохо(functionexample(){// JavaScript интерпретирует это, как// let a = ( b = ( c = 1 ) );// Ключевое слово let применится только к переменной a;// переменные b и c станут глобальными.leta=b=c=1;}());console.log(a);// throws ReferenceErrorconsole.log(b);// 1console.log(c);// 1// хорошо(functionexample(){leta=1;letb=a;letc=a;}());console.log(a);// throws ReferenceErrorconsole.log(b);// throws ReferenceErrorconsole.log(c);// throws ReferenceError// тоже самое и для `const`

  • 13.6 Избегайте использования унарных инкрементов и декрементов (++,--). eslintno-plusplus

    Почему? Согласно документации eslint, унарные инкремент и декремент автоматически вставляют точку с запятой, что может стать причиной трудноуловимых ошибок при инкрементировании и декрементировании значений. Также нагляднее изменять ваши значения таким образомnum += 1 вместоnum++ илиnum ++. Запрет на унарные инкремент и декремент ограждает вас от непреднамеренных преждевременных инкрементаций/декрементаций значений, которые могут привести к непредсказуемому поведению вашей программы.

    // плохоconstarray=[1,2,3];letnum=1;num++;--num;letsum=0;lettruthyCount=0;for(leti=0;i<array.length;i++){letvalue=array[i];sum+=value;if(value){truthyCount++;}}// хорошоconstarray=[1,2,3];letnum=1;num+=1;num-=1;constsum=array.reduce((a,b)=>a+b,0);consttruthyCount=array.filter(Boolean).length;

  • 13.7 В присвоении избегайте разрывов строк до и после=. Если ваше присвоение нарушает правилоmax-len, оберните значение в круглые скобки. eslintoperator-linebreak.

    Почему? Разрывы строк до и после= могут приводить к путанице в понимании значения.

    // плохоconstfoo=superLongLongLongLongLongLongLongLongFunctionName();// плохоconstfoo='superLongLongLongLongLongLongLongLongString';// хорошоconstfoo=(superLongLongLongLongLongLongLongLongFunctionName());// хорошоconstfoo='superLongLongLongLongLongLongLongLongString';

  • 13.8 Запретить неиспользуемые переменные. eslint:no-unused-vars

    Почему? Переменные, которые объявлены и не используются в коде, скорее всего, являются ошибкой из-за незавершённого рефакторинга. Такие переменные занимают место в коде и могут привести к путанице при чтении.

    // плохоvarsome_unused_var=42;// Переменные, которые используются только для записи, не считаются используемыми.vary=10;y=5;// Чтение для собственной модификации.varz=0;z=z+1;// Неиспользуемые аргументы функции.functiongetX(x,y){returnx;}// хорошоfunctiongetXPlusY(x,y){returnx+y;}varx=1;vary=a+2;alert(getXPlusY(x,y));// Переменная 'type' игнорируется, даже если она не испольуется, потому что рядом есть rest-свойство.// Эта форма извлечения объекта, которая опускает указанные ключи.var{ type, ...coords}=data;// 'coords' теперь 'data' объект без свойства 'type'.

⬆ к оглавлению

  • 14.1 Объявленияvar поднимаются в начало области видимости ближайшей закрывающей их функции, а их присвоение нет. Объявленияconst иlet работают по новой концепции называемойВременные Мёртвые Зоны (Temporal Dead Zone). Важно знать, почему использоватьtypeof больше не безопасно.

    // мы знаем, что это не будет работать// (если нет глобальной переменной notDefined)functionexample(){console.log(notDefined);// => выбросит ошибку ReferenceError}// обращение к переменной до её создания// будет работать из-за подъёма.// Примечание: значение true не поднимается.functionexample(){console.log(declaredButNotAssigned);// => undefinedvardeclaredButNotAssigned=true;}// интерпретатор понимает объявление// переменной в начало области видимости.// это означает, что наш пример// можно переписать таким образом:functionexample(){letdeclaredButNotAssigned;console.log(declaredButNotAssigned);// => undefineddeclaredButNotAssigned=true;}// использование const и letfunctionexample(){console.log(declaredButNotAssigned);// => выбросит ошибку ReferenceErrorconsole.log(typeofdeclaredButNotAssigned);// => выбросит ошибку ReferenceErrorconstdeclaredButNotAssigned=true;}

  • 14.2 Для анонимных функциональных выражений наверх области видимости поднимается название переменной, но не её значение.

    functionexample(){console.log(anonymous);// => undefinedanonymous();// => TypeError anonymous не является функциейvaranonymous=function(){console.log('anonymous function expression');};}

  • 14.3 Для именованных функциональных выражений наверх области видимости поднимается название переменной, но не имя или тело функции.

    functionexample(){console.log(named);// => undefinednamed();// => TypeError named не является функциейsuperPower();// => ReferenceError superPower не определенаvarnamed=functionsuperPower(){console.log('Flying');};}// тоже самое справедливо, когда имя функции// совпадает с именем переменной.functionexample(){console.log(named);// => undefinednamed();// => TypeError named не является функциейvarnamed=functionnamed(){console.log('named');};}

  • 14.4 При объявлении функции её имя и тело поднимаются наверх области видимости.

    functionexample(){superPower();// => FlyingfunctionsuperPower(){console.log('Flying');}}
  • Более подробно можно прочитать в статьеJavaScript Scoping & Hoisting отBen Cherry.

⬆ к оглавлению

  • 15.1 Используйте=== и!== вместо== и!=. eslint:eqeqeq

  • 15.2 Условные операторы, такие какif, вычисляются путём приведения к логическому типуBoolean через абстрактный методToBoolean и всегда следуют следующим правилам:

    • Object соответствуетtrue
    • Undefined соответствуетfalse
    • Null соответствуетfalse
    • Boolean соответствуетзначению булева типа
    • Number соответствуетfalse, если+0, -0, or NaN, в остальных случаяхtrue
    • String соответствуетfalse, если строка пустая'', в остальных случаяхtrue
    if([0]&&[]){// true// Массив (даже пустой) является объектом, а объекты возвращают true}

  • 15.3 Используйте сокращения для булевских типов, а для строк и чисел применяйте явное сравнение.

    // плохоif(isValid===true){// ...}// хорошоif(isValid){// ...}// плохоif(name){// ...}// хорошоif(name!==''){// ...}// плохоif(collection.length){// ...}// хорошоif(collection.length>0){// ...}

  • 15.5 Используйте фигурные скобки дляcase иdefault, если они содержат лексические декларации (например,let,const,function, иclass). eslint:no-case-declarations.

    Почему? Лексические декларации видны во всемswitch блоке, но инициализируются только при присваивании, которое происходит при входе в блокcase. Возникают проблемы, когда множествоcase пытаются определить одно и то же.

    // плохоswitch(foo){case1:letx=1;break;case2:consty=2;break;case3:functionf(){// ...}break;default:classC{}}// хорошоswitch(foo){case1:{letx=1;break;}case2:{consty=2;break;}case3:{functionf(){// ...}break;}case4:bar();break;default:{classC{}}}

  • 15.6 Тернарные операторы не должны быть вложены и в большинстве случаев должны быть расположены на одной строке. eslint:no-nested-ternary.

    // плохоconstfoo=maybe1>maybe2  ?"bar"  :value1>value2 ?"baz" :null;// разбит на два отдельных тернарных выраженияconstmaybeNull=value1>value2 ?'baz' :null;constfoo=maybe1>maybe2  ?'bar'  :maybeNull;// отличноconstfoo=maybe1>maybe2 ?'bar' :maybeNull;

  • 15.7 Избегайте ненужных тернарных операторов. eslint:no-unneeded-ternary.

    // плохоconstfoo=a ?a :b;constbar=c ?true :false;constbaz=c ?false :true;// хорошоconstfoo=a||b;constbar=!!c;constbaz=!c;

  • 15.8 При смешивании операторов, помещайте их в круглые скобки. Единственным исключением являются стандартные арифметические операторы:+,- и**, так как их приоритет широко известен. Мы рекомендуем заключить/ и* в круглые скобки, поскольку их приоритет может быть неоднозначным, когда они смешиваются. eslint:no-mixed-operators

    Почему? Это улучшает читаемость и уточняет намерения разработчика.

    // плохоconstfoo=a&&b<0||c>0||d+1===0;// плохоconstbar=a**b-5%d;// плохо// можно ошибиться, думая что это (a || b) && cif(a||b&&c){returnd;}// плохоconstbar=a+b/c*d;// хорошоconstfoo=(a&&b<0)||c>0||(d+1===0);// хорошоconstbar=a**b-(5%d);// хорошоif(a||(b&&c)){returnd;}// хорошоconstbar=a+(b/c)*d;

⬆ к оглавлению

  • 16.1 Используйте фигурные скобки, когда блок кода занимает несколько строк. eslint:nonblock-statement-body-position

    // плохоif(test)returnfalse;// хорошоif(test)returnfalse;// хорошоif(test){returnfalse;}// плохоfunctionfoo(){returnfalse;}// хорошоfunctionbar(){returnfalse;}

  • 16.2 Если блоки кода в условииif иelse занимают несколько строк, расположите операторelse на той же строчке, где находится закрывающая фигурная скобка блокаif. eslint:brace-style

    // плохоif(test){thing1();thing2();}else{thing3();}// хорошоif(test){thing1();thing2();}else{thing3();}

  • 16.3 Если в блокеif всегда выполняется операторreturn, последующий блокelse не нужен.return внутри блокаelse if, следующем за блокомif, который содержитreturn, может быть разделён на несколько блоковif. eslint:no-else-return

    // плохоfunctionfoo(){if(x){returnx;}else{returny;}}// плохоfunctioncats(){if(x){returnx;}elseif(y){returny;}}// плохоfunctiondogs(){if(x){returnx;}else{if(y){returny;}}}// хорошоfunctionfoo(){if(x){returnx;}returny;}// хорошоfunctioncats(){if(x){returnx;}if(y){returny;}}// хорошоfunctiondogs(x){if(x){if(z){returny;}}else{returnz;}}

⬆ к оглавлению

  • 17.1 Если ваш управляющий оператор (if,while и т.д.) слишком длинный или превышает максимальную длину строки, то каждое (сгруппированное) условие можно поместить на новую строку. Логический оператор должен располагаться в начале строки.

    Почему? Наличие операторов в начале строки приводит к выравниванию операторов и напоминает цепочку методов. Это также улучшает читаемость, упрощая визуальное отслеживание сложной логики.

    // плохоif((foo===123||bar==='abc')&&doesItLookGoodWhenItBecomesThatLong()&&isThisReallyHappening()){thing1();}// плохоif(foo===123&&bar==='abc'){thing1();}// плохоif(foo===123&&bar==='abc'){thing1();}// плохоif(foo===123&&bar==='abc'){thing1();}// хорошоif(foo===123&&bar==='abc'){thing1();}// хорошоif((foo===123||bar==='abc')&&doesItLookGoodWhenItBecomesThatLong()&&isThisReallyHappening()){thing1();}// хорошоif(foo===123&&bar==='abc'){thing1();}

  • 17.2 Не используйте операторы выбора вместо управляющих операторов.

    // плохо!isRunning&&startRunning();// хорошоif(!isRunning){startRunning();}

⬆ к оглавлению

  • 18.1 Используйте конструкцию/** ... */ для многострочных комментариев.

    // плохо// make() возвращает новый элемент// соответствующий переданному названию тега////@param {String} tag//@return {Element} elementfunctionmake(tag){// ...returnelement;}// хорошо/** * make() возвращает новый элемент * соответствующий переданному названию тега */functionmake(tag){// ...returnelement;}

  • 18.2 Используйте двойной слеш// для однострочных комментариев. Располагайте такие комментарии отдельной строкой над кодом, который хотите пояснить. Если комментарий не является первой строкой блока, добавьте сверху пустую строку.

    // плохоconstactive=true;// это текущая вкладка// хорошо// это текущая вкладкаconstactive=true;// плохоfunctiongetType(){console.log('fetching type...');// установить по умолчанию тип 'no type'consttype=this.type||'no type';returntype;}// хорошоfunctiongetType(){console.log('fetching type...');// установить по умолчанию тип 'no type'consttype=this.type||'no type';returntype;}// тоже хорошоfunctiongetType(){// установить по умолчанию тип 'no type'consttype=this.type||'no type';returntype;}

  • 18.3 Начинайте все комментарии с пробела, так их проще читать. eslint:spaced-comment

    // плохо//это текущая вкладкаconstactive=true;// хорошо// это текущая вкладкаconstactive=true;// плохо/** *make() возвращает новый элемент *соответствующий переданному названию тега */functionmake(tag){// ...returnelement;}// хорошо/** * make() возвращает новый элемент * соответствующий переданному названию тега */functionmake(tag){// ...returnelement;}

  • 18.4 Если комментарий начинается со словFIXME илиTODO, то это помогает другим разработчикам быстро понять, когда вы хотите указать на проблему, которую надо решить, или когда вы предлагаете решение проблемы, которое надо реализовать. Такие комментарии, в отличие от обычных, побуждают к действию:FIXME: -- нужно разобраться с этим илиTODO: -- нужно реализовать.

  • 18.5 Используйте// FIXME:, чтобы описать проблему.

    classCalculatorextendsAbacus{constructor(){super();// FIXME: здесь не должна использоваться глобальная переменнаяtotal=0;}}

  • 18.6 Используйте// TODO:, чтобы описать решение проблемы.

    classCalculatorextendsAbacus{constructor(){super();// TODO: нужна возможность задать total через параметрыthis.total=0;}}

⬆ к оглавлению

  • 19.1 Используйте мягкую табуляцию (символ пробела) шириной в 2 пробела. eslint:indent

    // плохоfunctionfoo(){∙∙∙∙letname;}// плохоfunctionbar(){∙letname;}// хорошоfunctionbaz(){∙∙letname;}

  • 19.2 Ставьте 1 пробел перед открывающей фигурной скобкой у блока. eslint:space-before-blocks

    // плохоfunctiontest(){console.log('test');}// хорошоfunctiontest(){console.log('test');}// плохоdog.set('attr',{age:'1 year',breed:'Bernese Mountain Dog',});// хорошоdog.set('attr',{age:'1 year',breed:'Bernese Mountain Dog',});

  • 19.3 Ставьте 1 пробел перед открывающей круглой скобкой в операторах управления (if,while и т.п.). Не оставляйте пробелов между списком аргументов и названием в объявлениях и вызовах функций. eslint:keyword-spacing

    // плохоif(isJedi){fight();}// хорошоif(isJedi){fight();}// плохоfunctionfight(){console.log('Swooosh!');}// хорошоfunctionfight(){console.log('Swooosh!');}

  • 19.4 Разделяйте операторы пробелами. eslint:space-infix-ops

    // плохоconstx=y+5;// хорошоconstx=y+5;

  • 19.5 В конце файла оставляйте одну пустую строку. eslint:eol-last

    // плохоimport{es6}from'./AirbnbStyleGuide';// ...exportdefaultes6;
    // плохоimport{es6}from'./AirbnbStyleGuide';// ...exportdefaultes6;
    // хорошоimport{es6}from'./AirbnbStyleGuide';// ...exportdefaultes6;

  • 19.6 Используйте переносы строк и отступы, когда делаете длинные цепочки методов (больше 2 методов). Ставьте точку в начале строки, чтобы дать понять, что это не новая инструкция, а продолжение цепочки. eslint:newline-per-chained-callno-whitespace-before-property

    // плохо$('#items').find('.selected').highlight().end().find('.open').updateCount();// плохо$('#items').find('.selected').highlight().end().find('.open').updateCount();// хорошо$('#items').find('.selected').highlight().end().find('.open').updateCount();// плохоconstleds=stage.selectAll('.led').data(data).enter().append('svg:svg').classed('led',true).attr('width',(radius+margin)*2).append('svg:g').attr('transform',`translate(${radius+margin},${radius+margin})`).call(tron.led);// хорошоconstleds=stage.selectAll('.led').data(data).enter().append('svg:svg').classed('led',true).attr('width',(radius+margin)*2).append('svg:g').attr('transform',`translate(${radius+margin},${radius+margin})`).call(tron.led);// хорошоconstleds=stage.selectAll('.led').data(data);constsvg=leds.enter().append('svg:svg');svg.classed('led',true).attr('width',(radius+margin)*2);constg=svg.append('svg:g');g.attr('transform',`translate(${radius+margin},${radius+margin})`).call(tron.led);

  • 19.7 Оставляйте пустую строку между блоком кода и следующей инструкцией.

    // плохоif(foo){returnbar;}returnbaz;// хорошоif(foo){returnbar;}returnbaz;// плохоconstobj={foo(){},bar(){},};returnobj;// хорошоconstobj={foo(){},bar(){},};returnobj;// плохоconstarr=[functionfoo(){},functionbar(){},];returnarr;// хорошоconstarr=[functionfoo(){},functionbar(){},];returnarr;

  • 19.8 Не добавляйте отступы до или после кода внутри блока. eslint:padded-blocks

    // плохоfunctionbar(){console.log(foo);}// тоже плохоif(baz){console.log(qux);}else{console.log(foo);}// хорошоfunctionbar(){console.log(foo);}// хорошоif(baz){console.log(qux);}else{console.log(foo);}

  • 19.9 Не используйте множество пустых строк для заполнения кода. eslint:no-multiple-empty-lines

    // плохоclassPerson{constructor(fullName,email,birthday){this.fullName=fullName;this.email=email;this.setAge(birthday);}setAge(birthday){consttoday=newDate();constage=this.getAge(today,birthday);this.age=age;}getAge(today,birthday){// ..}}// хорошоclassPerson{constructor(fullName,email,birthday){this.fullName=fullName;this.email=email;this.setAge(birthday);}setAge(birthday){consttoday=newDate();constage=getAge(today,birthday);this.age=age;}getAge(today,birthday){// ..}}

  • 19.10 Не добавляйте пробелы между круглыми скобками и их содержимым. eslint:space-in-parens

    // плохоfunctionbar(foo){returnfoo;}// хорошоfunctionbar(foo){returnfoo;}// плохоif(foo){console.log(foo);}// хорошоif(foo){console.log(foo);}

  • 19.11 Не добавляйте пробелы между квадратными скобками и их содержимым. eslint:array-bracket-spacing

    // плохоconstfoo=[1,2,3];console.log(foo[0]);// хорошоconstfoo=[1,2,3];console.log(foo[0]);

  • 19.12 Добавляйте пробелы между фигурными скобками и их содержимым. eslint:object-curly-spacing

    // плохоconstfoo={clark:'kent'};// хорошоconstfoo={clark:'kent'};

  • 19.13 Старайтесь не допускать, чтобы строки были длиннее 100 символов (включая пробелы). Замечание: согласнопункту выше, длинные строки с текстом освобождаются от этого правила и не должны разбиваться на несколько строк. eslint:max-len

    Почему? Это обеспечивает удобство чтения и поддержки кода.

    // плохоconstfoo=jsonData&&jsonData.foo&&jsonData.foo.bar&&jsonData.foo.bar.baz&&jsonData.foo.bar.baz.quux&&jsonData.foo.bar.baz.quux.xyzzy;// плохо$.ajax({method:'POST',url:'https://airbnb.com/',data:{name:'John'}}).done(()=>console.log('Congratulations!')).fail(()=>console.log('You have failed this city.'));// хорошоconstfoo=jsonData&&jsonData.foo&&jsonData.foo.bar&&jsonData.foo.bar.baz&&jsonData.foo.bar.baz.quux&&jsonData.foo.bar.baz.quux.xyzzy;// хорошо$.ajax({method:'POST',url:'https://airbnb.com/',data:{name:'John'},}).done(()=>console.log('Congratulations!')).fail(()=>console.log('You have failed this city.'));

  • 19.14 Требуйте согласованного расстояния между открывающим символом блока и следующим символом на одной и той же строке. Тоже самое касается расстояния между закрывающим символом блока и предыдущим символом. eslint:block-spacing

    // плохоfunctionfoo(){returntrue;}if(foo){bar=0;}// хорошоfunctionfoo(){returntrue;}if(foo){bar=0;}

  • 19.15 Избегайте пробелов перед запятыми и ставьте его после. eslint:comma-spacing

    // плохоvarfoo=1,bar=2;vararr=[1,2];// хорошоvarfoo=1,bar=2;vararr=[1,2];

  • 19.16 Избегайте пробелов внутри скобок вычисляемого свойства. eslint:computed-property-spacing

    // плохоobj[foo]obj['foo']varx={[b]:a}obj[foo[bar]]// хорошоobj[foo]obj['foo']varx={[b]:a}obj[foo[bar]]

  • 19.17 Избегайте пробелов между функциями и их вызовами. eslint:func-call-spacing

    // плохоfunc();func();// хорошоfunc();

  • 19.18 Обеспечьте согласованное расстояние между ключами и значениями в свойствах литералов объекта. eslint:key-spacing

    // плохоvarobj={foo :42};varobj2={foo:42};// хорошоvarobj={foo:42};

  • 19.20 Избегайте множества пустых строк и новой строки в начале файлов. Разрешайте только одну пустую строку в конце файла. eslint:no-multiple-empty-lines

    // плохо - множество пустых строкvarx=1;vary=2;// плохо - 2+ новых строк в конце файлаvarx=1;vary=2;// плохо - 1+ новая строка в начале файлаvarx=1;vary=2;// хорошоvarx=1;vary=2;

⬆ к оглавлению

  • 20.1 Не начинайте строку с запятой. eslint:comma-style

    // плохоconststory=[once,upon,aTime];// хорошоconststory=[once,upon,aTime,];// плохоconsthero={firstName:'Ada',lastName:'Lovelace',birthYear:1815,superPower:'computers'};// хорошоconsthero={firstName:'Ada',lastName:'Lovelace',birthYear:1815,superPower:'computers',};

  • 20.2 Добавляйте висячие запятые. eslint:comma-dangle

    Почему? Такой подход даёт понятную разницу при просмотре изменений. Кроме того, транспиляторы типа Babel удалят висячие запятые из собранного кода, поэтому вы можете не беспокоиться опроблемах в старых браузерах.

    // плохо - git diff без висячей запятойconst hero = {     firstName: 'Florence',-    lastName: 'Nightingale'+    lastName: 'Nightingale',+    inventorOf: ['coxcomb chart', 'modern nursing']};// хорошо - git diff с висячей запятойconst hero = {     firstName: 'Florence',     lastName: 'Nightingale',+    inventorOf: ['coxcomb chart', 'modern nursing'],};
    // плохоconsthero={firstName:'Dana',lastName:'Scully'};constheroes=['Batman','Superman'];// хорошоconsthero={firstName:'Dana',lastName:'Scully',};constheroes=['Batman','Superman',];// плохоfunctioncreateHero(firstName,lastName,inventorOf){// ничего не делает}// хорошоfunctioncreateHero(firstName,lastName,inventorOf,){// ничего не делает}// хорошо (обратите внимание, что висячей запятой не должно быть после rest-параметра)functioncreateHero(firstName,lastName,inventorOf,  ...heroArgs){// ничего не делает}// плохоcreateHero(firstName,lastName,inventorOf);// хорошоcreateHero(firstName,lastName,inventorOf,);// хорошо (обратите внимание, что висячей запятой не должно быть после rest-аргумента)createHero(firstName,lastName,inventorOf,  ...heroArgs);

⬆ к оглавлению

  • 21.1Да. eslint:semi

    Почему? Когда JavaScript встречает перенос строки без точки с запятой, он использует правило под названиемАвтоматическая Вставка Точки с запятой (Automatic Semicolon Insertion), чтобы определить, стоит ли считать этот перенос строки как конец выражения и (как следует из названия) поместить точку с запятой в вашем коде до переноса строки. Однако, ASI содержит несколько странных форм поведения, и ваш код может быть сломан, если JavaScript неверно истолкует ваш перенос строки. Эти правила станут сложнее, когда новые возможности станут частью JavaScript. Явное завершение ваших выражений и настройка вашего линтера для улавливания пропущенных точек с запятыми помогут вам предотвратить возникновение проблем.

    // плохо - выбрасывает исключениеconstluke={}constleia={}[luke,leia].forEach((jedi)=>jedi.father='vader')// плохо - выбрасывает исключениеconstreaction="No! That’s impossible!"(asyncfunctionmeanwhileOnTheFalcon(){// переносимся к `leia`, `lando`, `chewie`, `r2`, `c3p0`// ...}())// плохо - возвращает `undefined` вместо значения на следующей строке. Так всегда происходит, когда `return` расположен сам по себе, потому что ASI (Автоматическая Вставка Точки с запятой)!functionfoo(){return'search your feelings, you know it to be foo'}// хорошоconstluke={};constleia={};[luke,leia].forEach((jedi)=>{jedi.father='vader';});// хорошоconstreaction="No! That’s impossible!";(asyncfunctionmeanwhileOnTheFalcon(){// переносимся к `leia`, `lando`, `chewie`, `r2`, `c3p0`// ...}());// хорошоfunctionfoo(){return'search your feelings, you know it to be foo';}

    Читать подробнее.

⬆ к оглавлению

  • 22.1 Выполняйте приведение типов в начале инструкции.

  • 22.2 Строки: eslint:no-new-wrappers

    // => this.reviewScore = 9;// плохоconsttotalScore=newString(this.reviewScore);// тип totalScore будет "object", а не "string"// плохоconsttotalScore=this.reviewScore+'';// вызывает this.reviewScore.valueOf()// плохоconsttotalScore=this.reviewScore.toString();// нет гарантии что вернётся строка// хорошоconsttotalScore=String(this.reviewScore);

  • 22.3 Числа: ИспользуйтеNumber иparseInt с основанием системы счисления. eslint:radixno-new-wrappers

    Почему? ФункцияparseInt выдаёт целочисленное значение на основе интерпретации содержимого строкового аргумента в соответствии с указанным основанием. Начальный пробел в строке игнорируется. Если основание "не определено" или "0", предполагается, что оно равно "10", за исключением случаев, когда число начинается с пар символов "0x" или "0X", в этом случае предполагается, что основание равно 16. Это отличается от ECMAScript 3, который просто не поощрял (но разрешал) восьмеричную интерпретацию. Многие реализации не приняли такое поведение по состоянию на 2013 год. И, поскольку должны поддерживаться более старые браузеры, всегда указывайте основание.

    constinputValue='4';// плохоconstval=newNumber(inputValue);// плохоconstval=+inputValue;// плохоconstval=inputValue>>0;// плохоconstval=parseInt(inputValue);// хорошоconstval=Number(inputValue);// хорошоconstval=parseInt(inputValue,10);

  • 22.4 Если по какой-то причине вы делаете что-то настолько безумное, чтоparseInt является слабым местом и вам нужно использовать побитовый сдвиг из-завопросов производительности, оставьте комментарий, объясняющий почему и что вы делаете.

    // хорошо/** * этот код медленно работал из-за parseInt. * побитовый сдвиг строки для приведения её к числу * работает значительно быстрее. */constval=inputValue>>0;

  • 22.5Примечание: Будьте осторожны с побитовыми операциями. Числа в JavaScript являются64-битными значениями, но побитовые операции всегда возвращают 32-битные значения (источник). Побитовый сдвиг может привести к неожиданному поведению для значений больше, чем 32 бита.Обсуждение. Верхний предел — 2 147 483 647:

    2147483647>>0;// => 21474836472147483648>>0;// => -21474836482147483649>>0;// => -2147483647

  • 22.6 Логические типы: eslint:no-new-wrappers

    constage=0;// плохоconsthasAge=newBoolean(age);// хорошоconsthasAge=Boolean(age);// отличноconsthasAge=!!age;

⬆ к оглавлению

  • 23.1 Избегайте названий из одной буквы. Имя должно быть наглядным. eslint:id-length

    // плохоfunctionq(){// ...}// хорошоfunctionquery(){// ...}

  • 23.2 ИспользуйтеcamelCase для именования объектов, функций и экземпляров. eslint:camelcase

    // плохоconstOBJEcttsssss={};constthis_is_my_object={};functionc(){}// хорошоconstthisIsMyObject={};functionthisIsMyFunction(){}

  • 23.3 ИспользуйтеPascalCase только для именования конструкторов и классов. eslint:new-cap

    // плохоfunctionuser(options){this.name=options.name;}constbad=newuser({name:'nope',});// хорошоclassUser{constructor(options){this.name=options.name;}}constgood=newUser({name:'yup',});

  • 23.4 Не используйте_ в начале или в конце названий. eslint:no-underscore-dangle

    Почему? JavaScript не имеет концепции приватности свойств или методов. Хотя подчёркивание в начале имени является распространённым соглашением, которое показывает «приватность», фактически эти свойства являются такими же доступными, как и часть вашего публичного API. Это соглашение может привести к тому, что разработчики будут ошибочно думать, что изменения не приведут к поломке или что тесты не нужны. Итог: если вы хотите, чтобы что-то было «приватным», то оно не должно быть доступно извне.

    // плохоthis.__firstName__='Panda';this.firstName_='Panda';this._firstName='Panda';// хорошоthis.firstName='Panda';// хорошо, в средах, где поддерживается WeakMaps// смотрите https://kangax.github.io/compat-table/es6/#test-WeakMapconstfirstNames=newWeakMap();firstNames.set(this,'Panda');

  • 23.5 Не сохраняйте ссылку наthis. Используйте стрелочные функции илиметод bind().

    // плохоfunctionfoo(){constself=this;returnfunction(){console.log(self);};}// плохоfunctionfoo(){constthat=this;returnfunction(){console.log(that);};}// хорошоfunctionfoo(){return()=>{console.log(this);};}

  • 23.6 Название файла точно должно совпадать с именем его экспорта по умолчанию.

    // содержание файла 1classCheckBox{// ...}exportdefaultCheckBox;// содержание файла 2exportdefaultfunctionfortyTwo(){return42;}// содержание файла 3exportdefaultfunctioninsideDirectory(){}// в других файлах// плохоimportCheckBoxfrom'./checkBox';// PascalCase import/export, camelCase filenameimportFortyTwofrom'./FortyTwo';// PascalCase import/filename, camelCase exportimportInsideDirectoryfrom'./InsideDirectory';// PascalCase import/filename, camelCase export// плохоimportCheckBoxfrom'./check_box';// PascalCase import/export, snake_case filenameimportforty_twofrom'./forty_two';// snake_case import/filename, camelCase exportimportinside_directoryfrom'./inside_directory';// snake_case import, camelCase exportimportindexfrom'./inside_directory/index';// requiring the index file explicitlyimportinsideDirectoryfrom'./insideDirectory/index';// requiring the index file explicitly// хорошоimportCheckBoxfrom'./CheckBox';// PascalCase export/import/filenameimportfortyTwofrom'./fortyTwo';// camelCase export/import/filenameimportinsideDirectoryfrom'./insideDirectory';// camelCase export/import/directory name/implicit "index"// ^ поддерживает оба варианта: insideDirectory.js и insideDirectory/index.js

  • 23.7 ИспользуйтеcamelCase, когда экспортируете функцию по умолчанию. Ваш файл должен называться так же, как и имя функции.

    functionmakeStyleGuide(){// ...}exportdefaultmakeStyleGuide;

  • 23.8 ИспользуйтеPascalCase, когда экспортируете конструктор / класс / синглтон / библиотечную функцию / объект.

    constAirbnbStyleGuide={es6:{},};exportdefaultAirbnbStyleGuide;

  • 23.9 Сокращения или буквенные аббревиатуры всегда должны быть в верхнем или нижнем регистре.

    Почему? Имена предназначены для удобства чтения.

    // плохоimportSmsContainerfrom'./containers/SmsContainer';// плохоconstHttpRequests=[// ...];// хорошоimportSMSContainerfrom'./containers/SMSContainer';// хорошоconstHTTPRequests=[// ...];// также хорошоconsthttpRequests=[// ...];// отличноimportTextMessageContainerfrom'./containers/TextMessageContainer';// отличноconstrequests=[// ...];

⬆ к оглавлению

  • 24.1 Функции-аксессоры для свойств объекта больше не нужны.

  • 24.2 Не используйте геттеры/сеттеры, т.к. они вызывают неожиданные побочные эффекты, а также их тяжело тестировать, поддерживать и понимать. Вместо этого создавайте методыgetVal() иsetVal('hello').

    // плохоclassDragon{getage(){// ...}setage(value){// ...}}// хорошоclassDragon{getAge(){// ...}setAge(value){// ...}}

  • 24.3 Если свойство/метод возвращает логический тип, то используйте названияisVal() илиhasVal().

    // плохоif(!dragon.age()){returnfalse;}// хорошоif(!dragon.hasAge()){returnfalse;}

  • 24.4 Можно создавать функцииget() иset(), но нужно быть последовательным.

    classJedi{constructor(options={}){constlightsaber=options.lightsaber||'blue';this.set('lightsaber',lightsaber);}set(key,val){this[key]=val;}get(key){returnthis[key];}}

⬆ к оглавлению

  • 25.1 Когда привязываете данные к событию (например, событияDOM или какие-то собственные события, какBackbone события), передавайте литерал объекта (также известный как «хэш») вместо простого значения. Это позволяет другим разработчикам добавлять больше данных без поиска и изменения каждого обработчика события. К примеру, вместо:

    // плохо$(this).trigger('listingUpdated',listing.id);// ...$(this).on('listingUpdated',(e,listingID)=>{// делает что-то с listingID});

    предпочитайте:

    // хорошо$(this).trigger('listingUpdated',{listingID:listing.id});// ...$(this).on('listingUpdated',(e,data)=>{// делает что-то с data.listingID});

⬆ к оглавлению

  • 26.1 Начинайте названия переменных, хранящих объект jQuery, со знака$.

    // плохоconstsidebar=$('.sidebar');// хорошоconst$sidebar=$('.sidebar');// хорошоconst$sidebarBtn=$('.sidebar-btn');

  • 26.2 Кэшируйте jQuery-поиски.

    // плохоfunctionsetSidebar(){$('.sidebar').hide();// ...$('.sidebar').css({'background-color':'pink',});}// хорошоfunctionsetSidebar(){const$sidebar=$('.sidebar');$sidebar.hide();// ...$sidebar.css({'background-color':'pink',});}

  • 26.3 Для поиска в DOM используйте каскады$('.sidebar ul') или селектор родитель > ребёнок$('.sidebar > ul').jsPerf

  • 26.4 Используйте функциюfind для поиска в сохранённых jQuery-объектах.

    // плохо$('ul','.sidebar').hide();// плохо$('.sidebar').find('ul').hide();// хорошо$('.sidebar ul').hide();// хорошо$('.sidebar > ul').hide();// хорошо$sidebar.find('ul').hide();

⬆ к оглавлению

⬆ к оглавлению

  • 28.1 Здесь собраны ссылки на различные возможности ES6.
  1. Стрелочные функции
  2. Классы и конструкторы
  3. Сокращённая запись методов объекта
  4. Сокращённая запись свойств объекта
  5. Вычисляемые имена свойств объекта
  6. Шаблонные строки
  7. Деструктуризация
  8. Параметры по умолчанию
  9. Оставшиеся параметры
  10. Оператор расширения
  11. Let и Const
  12. Итераторы и генераторы
  13. Модули

  • 28.2 Не используйтепредложения TC39, которые не перешли на 3-ю стадию.

    Почему?Они ещё не закончены и могут быть изменены или полностью изъяты. Мы хотим использовать JavaScript, а предложения ещё не стали частью JavaScript.

⬆ к оглавлению

Стандартная библиотекасодержит утилиты, функциональность которых сломана, но они остались для поддержки старого кода.

  • 29.1 ИспользуйтеNumber.isNaN вместо глобальногоisNaN.eslint:no-restricted-globals

    Почему? Глобальная функцияisNaN приводит не-числа к числам, возвращаяtrue для всего, что приводится кNaN.Если такое поведение необходимо, сделайте его явным.

    // плохоisNaN('1.2');// falseisNaN('1.2.3');// true// хорошоNumber.isNaN('1.2.3');// falseNumber.isNaN(Number('1.2.3'));// true

  • 29.2 ИспользуйтеNumber.isFinite вместо глобальногоisFinite.eslint:no-restricted-globals

    Почему? Глобальная функцияisFinite приводит не-числа к числам, возвращаяtrue для всего, что приводится к конечному числу.Если такое поведение необходимо, сделайте его явным.

    // плохоisFinite('2e3');// true// хорошоNumber.isFinite('2e3');// falseNumber.isFinite(parseInt('2e3',10));// true

⬆ к оглавлению

  • 30.1Ага.

    functionfoo(){returntrue;}

  • 30.2Нет, но серьёзно:
    • Какой бы фреймворк вы не использовали, вы должны писать тесты!
    • Стремитесь к тому, чтобы написать много маленьких чистых функций, и к тому, чтобы свести к минимуму места, где происходят мутации.
    • Будьте осторожны со стабами (stubs) и моками (mocks) — они могут сделать ваше тестирование хрупким.
    • Мы в первую очередь советуем вам использоватьmocha иjest от Airbnb.tape также иногда используется для небольших, отдельных модулей.
    • 100% покрытие тестами — это хорошая цель, к которой надо стремиться, даже если это не всегда практично.
    • Всякий раз, когда вы исправляете ошибку,пишите регрессионный тест. Исправленная ошибка без регрессионного тестирования почти наверняка всплывёт в будущем.

⬆ к оглавлению

⬆ к оглавлению

Изучение ES6+

Почитайте это

Инструменты

Другие руководства

Другие стили

Дальнейшее чтение

Книги

Блоги

Подкасты

⬆ к оглавлению

Это список организаций, которые используют данное руководство. Отправьте нам пулреквест, и мы добавим вас в этот список.

⬆ к оглавлению

Это руководство также переведено на другие языки:

⬆ к оглавлению

  • Найдите их наgitter.

(The MIT License)

Copyright (c) 2012 Airbnb

Permission is hereby granted, free of charge, to any person obtaininga copy of this software and associated documentation files (the'Software'), to deal in the Software without restriction, includingwithout limitation the rights to use, copy, modify, merge, publish,distribute, sublicense, and/or sell copies of the Software, and topermit persons to whom the Software is furnished to do so, subject tothe following conditions:

The above copyright notice and this permission notice shall beincluded in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OFMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANYCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THESOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

⬆ к оглавлению

Мы рекомендуем вам сделать форк этого руководства и изменить его правила под стиль вашей команды. Ниже вы можете перечислить свои изменения в руководстве. Это позволит вам обновлять его время от времени, не сталкиваясь с конфликтами слияний.

};

Releases

No releases published

Packages

No packages published

Contributors8


[8]ページ先頭

©2009-2025 Movatter.jp