local socket = require("socket")


local maf = require 'maf'
local vec3 = maf.vec3
local quat = maf.quat

local samem = require 'SAMemory'

samem.require 'CVehicle'
samem.require 'tHandlingData'
samem.require 'CPlane'
samem.require 'CTransmission'

-- Constants for controlling plane steering behavior
local PITCH_UP_THRESHOLD = -15          -- Pitch angle threshold in degrees
local FINE_STEERING_ANGLE = 15           -- Angle for fine steering adjustments in degrees
local YAW_FACTOR = 1                    -- Multiplicative factor for yaw adjustment
local ROLL_FACTOR = 1                -- Multiplicative factor for roll adjustment
local STEERING_SPEED = 100.0             -- Speed of steering control in units per second

-- Direction vectors
local DIRECTION = {
    FORWARD = vec3(0, 1, 0),
    BACKWARD = vec3(0, -1, 0),
    RIGHT = vec3(1, 0, 0),
    LEFT = vec3(-1, 0, 0),
    UP = vec3(0, 0, 1),
    DOWN = vec3(0, 0, -1)
}

-- Calculate the signed angle between two vectors with respect to a reference axis
local function calculateSignedAngle(from, to, axis)
    local fromNormalized = from:clone():normalize()
    local toNormalized = to:clone():normalize()
    local dotProduct = fromNormalized:dot(toNormalized)
    local angle = math.acos(math.max(-1, math.min(1, dotProduct)))  -- Clamp the dot product
    local crossProduct = fromNormalized:cross(toNormalized)
    local sign = crossProduct:dot(axis) < 0 and -1 or 1
    return angle * sign
end

-- Calculate the angle between two vectors in degrees
local function calculateAngleBetweenVectors(v1, v2)
    local normV1, normV2 = v1:normalize(), v2:normalize()
    local dotProduct = math.max(-1, math.min(1, normV1:dot(normV2)))  -- Clamp the dot product
    return math.deg(math.acos(dotProduct))
end

-- Obtain the conjugate of a quaternion
local function conjugateQuaternion(q)
    return quat(-q.x, -q.y, -q.z, q.w)
end

local function smoothstep(edge0, edge1, x)
	-- Scale, bias and saturate x to 0..1 range
	x = math.max(0, math.min(1, (x - edge0) / (edge1 - edge0)))
	-- Evaluate polynomial
	return x * x * (3 - 2 * x)
end

-- Compute the steering input for the plane to move towards a target position
local function computeSteeringInput(plane, dt, targetPosition)
    local planePosition = vec3(getCarCoordinates(plane))
    local planeRotation = quat(getVehicleQuaternion(plane))

    local positionError = targetPosition - planePosition
    local distanceError = positionError:length()
    local localError = conjugateQuaternion(planeRotation) * positionError

    local errorDir = localError:normalize()
    local pitchError, rollError, yawError
    pitchError = vec3(0, localError.y, localError.z):normalize()
    rollError = vec3(localError.x, localError.y, 0):normalize()
    yawError = vec3(localError.x, 0, localError.z):normalize()

    local targetInput = vec3()
    local pitch = calculateSignedAngle(DIRECTION.FORWARD, pitchError, DIRECTION.RIGHT)

    if -pitch < PITCH_UP_THRESHOLD then
        pitch = (pitch + 360) % 360
    end
    targetInput.x = pitch

    local angleError = calculateAngleBetweenVectors(DIRECTION.FORWARD, rollError)

	local minAngle = -5
	local maxAngle = FINE_STEERING_ANGLE

	local angleError = calculateAngleBetweenVectors(DIRECTION.FORWARD, rollError)
	local dynamicYawFactor = smoothstep(minAngle, maxAngle, angleError) * YAW_FACTOR

	if angleError < FINE_STEERING_ANGLE then
		local yaw = -calculateSignedAngle(DIRECTION.FORWARD, yawError, DIRECTION.UP)
		targetInput.y = yaw * dynamicYawFactor
	else
		local roll = calculateSignedAngle(DIRECTION.UP, rollError, DIRECTION.FORWARD)
		targetInput.z = roll * ROLL_FACTOR
	end
	

    targetInput.x = math.max(-1, math.min(1, targetInput.x))
    targetInput.y = math.max(-1, math.min(1, targetInput.y))
    targetInput.z = math.max(-1, math.min(1, targetInput.z))

    local input = vec3()
    input.x = math.max(-1, math.min(1, input.x + (targetInput.x - input.x) * STEERING_SPEED * dt))
    input.y = math.max(-1, math.min(1, input.y + (targetInput.y - input.y) * STEERING_SPEED * dt))
    input.z = math.max(-1, math.min(1, input.z + (targetInput.z - input.z) * STEERING_SPEED * dt))

	

    return input
end


function main()
    local lastTime = socket.gettime() -- Initial time setup

    while true do
        wait(0)  -- Wait time before loop reiteration

        local currentTime = socket.gettime()
        local dt = currentTime - lastTime
        lastTime = currentTime

        local plane = storeCarCharIsInNoSave(playerPed)
        local planePosition = vec3(getCarCoordinates(plane))
        local camPosition = vec3(getActiveCameraCoordinates())
		camPosition.z = camPosition.z - 0.1
        local camDirection = (vec3(getActiveCameraPointAt()) - camPosition):normalize() * 10
        local targetPosition = planePosition + camDirection
		
		local wposX, wposY = convert3DCoordsToScreen(targetPosition:unpack())
		--dxDrawRing(posX, posY, radius, width, startAngle, amount, color, absoluteAmount, anglesPerLine)
		dxDrawRing(wposX, wposY-65, 15, 0.75, 0, 1, 285212671, false, 1 )

        local pitchInput, yawInput, rollInput = computeSteeringInput(plane, dt * 10, targetPosition):unpack()

        if doesVehicleExist(plane) then
            local carPointer = getCarPointer(plane)
            local vehicle = samem.cast("CVehicle *", carPointer)

            if vehicle ~= samem.nullptr and vehicle.nVehicleClass == 0 then
                local plane = samem.cast('CPlane *', vehicle)
				
                plane.field_988 = yawInput --* 2^30
				plane.field_98C = pitchInput
				plane.field_990 = rollInput 
               -- plane.field_9C4 = 1
			   printStringNow(yawInput, 1)
                
            --print(yawInput, pitchInput, math.sign(rollInput))
			end
        end
    end
end


function math.sign(x)
    if x > 0 then
        return 1
    elseif x < 0 then
        return -1
    else
        return 0
    end
end



function dxDrawRing(posX, posY, radius, width, startAngle, amount, color, absoluteAmount, anglesPerLine)
    if type(posX) ~= "number" or type(posY) ~= "number" or type(startAngle) ~= "number" or type(amount) ~= "number" then
        print("Error: One of the required parameters (posX, posY, startAngle, amount) is not a number.")
        return false
    end

    radius = type(radius) == "number" and radius or 50
    width = type(width) == "number" and width or 5
    color = color or 0xFFFFFFFF
    absoluteAmount = type(absoluteAmount) == "boolean" and absoluteAmount or false
    anglesPerLine = type(anglesPerLine) == "number" and anglesPerLine or 1

    local stopAngle = absoluteAmount and (amount + startAngle) or ((amount * 360) + startAngle)

    for angle = startAngle, stopAngle, anglesPerLine do
        local radAngle = math.rad(angle)
        local startX = math.cos(radAngle) * (radius - width)
        local startY = math.sin(radAngle) * (radius - width)
        local endX = math.cos(radAngle) * (radius + width)
        local endY = math.sin(radAngle) * (radius + width)

        renderDrawLine(startX + posX, startY + posY, endX + posX, endY + posY, width, color)
    end

    return math.floor((stopAngle - startAngle) / anglesPerLine)
end


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

Skip to content
Navigation Menu
leonidlebedev
/
javascript-airbnb

Type / to search
Code
Issues
1
Pull requests
Actions
Security
Insights
Owner avatar
javascript-airbnb
Public
leonidlebedev/javascript-airbnb
Go to file
t
Add file
Folders and files
Name		
Latest commit
leonidlebedev
leonidlebedev
Merge branch 'master' of github.com:leonidlebedev/javascript-airbnb
1450c30
 · 
3 years ago
History
css-in-javascript
Исправления (#81)
4 years ago
linters
Правило расширения файлов в импорте
3 years ago
react
Новая последовательность в классах
3 years ago
.gitignore
Добавлены проверки на файлы
6 years ago
.npmrc
Добавлены проверки на файлы
6 years ago
README.md
Merge branch 'master' of github.com:leonidlebedev/javascript-airbnb
3 years ago
_config.yml
Set theme jekyll-theme-cayman
6 years ago
package.json
Обновление пакетов
3 years ago
Repository files navigation
README
Руководство по написанию JavaScript-кода от Airbnb() {
Наиболее разумный подход к написанию JavaScript-кода

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

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

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

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

React
CSS-in-JavaScript
Оглавление
Типы
Объявление переменных
Объекты
Массивы
Деструктуризация
Строки
Функции
Стрелочные функции
Классы и конструкторы
Модули
Итераторы и генераторы
Свойства
Переменные
Подъём
Операторы сравнения и равенства
Блоки
Управляющие операторы
Комментарии
Пробелы
Запятые
Точка с запятой
Приведение типов
Соглашение об именовании
Аксессоры
События
jQuery
Поддержка ECMAScript 5
Возможности ECMAScript 6+ (ES 2015+)
Стандартная библиотека
Тестирование
Производительность
Ресурсы
В реальной жизни
Переводы
Пообщаться с разработчиками Airbnb
Участники перевода
Лицензия
Поправки
Типы

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

string
number
boolean
null
undefined
symbol
bigint
const foo = 1;
let bar = foo;

bar = 9;

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

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

object
array
function
const foo = [1, 2];
const bar = foo;

bar[0] = 9;

console.log(foo[0], bar[0]); // => 9, 9
⬆ к оглавлению

Объявление переменных

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

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

// плохо
var a = 1;
var b = 2;

// хорошо
const a = 1;
const b = 2;

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

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

// плохо
var count = 1;
if (true) {
  count += 1;
}

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

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

// const и let существуют только в том блоке, в котором они определены.
{
  let a = 1;
  const b = 1;
  var c = 1;
}
console.log(a); // ReferenceError
console.log(b); // ReferenceError
console.log(c); // 1
В приведённом выше коде вы можете видеть, что ссылки на a и b приведут к ошибке ReferenceError, в то время как c содержит число. Это связано с тем, что a и b имеют блочную область видимости, в то время как у c функциональная.

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

Объекты

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

// плохо
const item = new Object();

// хорошо
const item = {};

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

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

function getKey(k) {
  return `a key named ${k}`;
}

// плохо
const obj = {
  id: 5,
  name: 'San Francisco',
};
obj[getKey('enabled')] = true;

// хорошо
const obj = {
  id: 5,
  name: 'San Francisco',
  [getKey('enabled')]: true,
};

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

// плохо
const atom = {
  value: 1,

  addValue: function (value) {
    return atom.value + value;
  },
};

// хорошо
const atom = {
  value: 1,

  addValue(value) {
    return atom.value + value;
  },
};

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

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

const lukeSkywalker = 'Luke Skywalker';

// плохо
const obj = {
  lukeSkywalker: lukeSkywalker,
};

// хорошо
const obj = {
  lukeSkywalker,
};

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

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

const anakinSkywalker = 'Anakin Skywalker';
const lukeSkywalker = 'Luke Skywalker';

// плохо
const obj = {
  episodeOne: 1,
  twoJediWalkIntoACantina: 2,
  lukeSkywalker,
  episodeThree: 3,
  mayTheFourth: 4,
  anakinSkywalker,
};

// хорошо
const obj = {
  lukeSkywalker,
  anakinSkywalker,
  episodeOne: 1,
  twoJediWalkIntoACantina: 2,
  episodeThree: 3,
  mayTheFourth: 4,
};

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

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

// плохо
const bad = {
  'foo': 3,
  'bar': 4,
  'data-blah': 5,
};

// хорошо
const good = {
  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));

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

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

// очень плохо
const original = { a: 1, b: 2 };
const copy = Object.assign(original, { c: 3 }); // эта переменная изменяет `original` ಠ_ಠ
delete copy.a; // если сделать так

// плохо
const original = { a: 1, b: 2 };
const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 }

// хорошо
const original = { a: 1, b: 2 };
const copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 }

const { a, ...noA } = copy; // noA => { b: 2, c: 3 }
⬆ к оглавлению

Массивы

4.1 Для создания массива используйте литеральную нотацию. eslint: no-array-constructor

// плохо
const items = new Array();

// хорошо
const items = [];

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

const someStack = [];

// плохо
someStack[someStack.length] = 'abracadabra';

// хорошо
someStack.push('abracadabra');

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

// плохо
const len = items.length;
const itemsCopy = [];
let i;

for (i = 0; i < len; i += 1) {
  itemsCopy[i] = items[i];
}

// хорошо
const itemsCopy = [...items];

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

const foo = document.querySelectorAll('.foo');

// хорошо
const nodes = Array.from(foo);

// отлично
const nodes = [...foo];

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

const arrLike = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 };

// плохо
const arr = Array.prototype.slice.call(arrLike);

// хорошо
const arr = Array.from(arrLike);

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

// плохо
const baz = [...foo].map(bar);

// хорошо
const baz = Array.from(foo, bar);

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

// хорошо
[1, 2, 3].map((x) => {
  const y = x + 1;
  return x * y;
});

// хорошо
[1, 2, 3].map((x) => x + 1);

// плохо - нет возвращаемого значения, следовательно, `acc` становится `undefined` после первой итерации
[[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => {
  const flatten = acc.concat(item);
});

// хорошо
[[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => {
  const flatten = acc.concat(item);
  return flatten;
});

// плохо
inbox.filter((msg) => {
  const { subject, author } = msg;
  if (subject === 'Mockingbird') {
    return author === 'Harper Lee';
  } else {
    return false;
  }
});

// хорошо
inbox.filter((msg) => {
  const { subject, author } = msg;
  if (subject === 'Mockingbird') {
    return author === 'Harper Lee';
  }

  return false;
});

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

// плохо
const arr = [
  [0, 1], [2, 3], [4, 5],
];

const objectInArray = [{
  id: 1,
}, {
  id: 2,
}];

const numberInArray = [
  1, 2,
];

// хорошо
const arr = [[0, 1], [2, 3], [4, 5]];

const objectInArray = [
  {
    id: 1,
  },
  {
    id: 2,
  },
];

const numberInArray = [
  1,
  2,
];
⬆ к оглавлению

Деструктуризация

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

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

// плохо
function getFullName(user) {
  const firstName = user.firstName;
  const lastName = user.lastName;

  return `${firstName} ${lastName}`;
}

// хорошо
function getFullName(user) {
  const { firstName, lastName } = user;
  return `${firstName} ${lastName}`;
}

// отлично
function getFullName({ firstName, lastName }) {
  return `${firstName} ${lastName}`;
}

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

const arr = [1, 2, 3, 4];

// плохо
const first = arr[0];
const second = arr[1];

// хорошо
const [first, second] = arr;

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

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

// плохо
function processInput(input) {
  // затем происходит чудо
  return [left, right, top, bottom];
}

// при вызове нужно подумать о порядке возвращаемых данных
const [left, __, top] = processInput(input);

// хорошо
function processInput(input) {
  // затем происходит чудо
  return { left, right, top, bottom };
}

// при вызове выбираем только необходимые данные
const { left, top } = processInput(input);
⬆ к оглавлению

Строки

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

// плохо
const name = "Capt. Janeway";

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

// хорошо
const name = 'Capt. Janeway';

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

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

// плохо
const errorMessage = 'This is a super long error that was thrown because \
of Batman. When you stop to think about how Batman had anything to do \
with this, you would get nowhere \
fast.';

// плохо
const errorMessage = 'This is a super long error that was thrown because ' +
  'of Batman. When you stop to think about how Batman had anything to do ' +
  'with this, you would get nowhere fast.';

// хорошо
const errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.';

6.3 При создании строки программным путём используйте шаблонные строки вместо конкатенации. eslint: prefer-template template-curly-spacing

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

// плохо
function sayHi(name) {
  return 'How are you, ' + name + '?';
}

// плохо
function sayHi(name) {
  return ['How are you, ', name, '?'].join();
}

// плохо
function sayHi(name) {
  return `How are you, ${ name }?`;
}

// хорошо
function sayHi(name) {
  return `How are you, ${name}?`;
}

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

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

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

// плохо
const foo = '\'this\' \i\s \"quoted\"';

// хорошо
const foo = '\'this\' is "quoted"';
const foo = `my name is '${name}'`;
⬆ к оглавлению

Функции

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

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

// плохо
function foo() {
  // ...
}

// плохо
const foo = function () {
  // ...
};

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

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) {
  function test() {
    console.log('Nope.');
  }
}

// хорошо
let test;
if (currentUser) {
  test = () => {
    console.log('Yup.');
  };
}

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

// плохо
function foo(name, options, arguments) {
  // ...
}

// хорошо
function foo(name, options, args) {
  // ...
}

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

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

// плохо
function concatenateAll() {
  const args = Array.prototype.slice.call(arguments);
  return args.join('');
}

// хорошо
function concatenateAll(...args) {
  return args.join('');
}

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

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

// всё ещё плохо
function handleThings(opts) {
  if (opts === void 0) {
    opts = {};
  }
  // ...
}

// хорошо
function handleThings(opts = {}) {
  // ...
}

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

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

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

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

// плохо
function handleThings(opts = {}, name) {
  // ...
}

// хорошо
function handleThings(name, opts = {}) {
  // ...
}

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

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

// плохо
var add = new Function('a', 'b', 'return a + b');

// всё ещё плохо
var subtract = Function('a', 'b', 'return a - b');

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

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

// плохо
const f = function(){};
const g = function (){};
const h = function() {};

// хорошо
const x = function () {};
const y = function a() {};

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

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

// плохо
function f1(obj) {
  obj.key = 1;
}

// хорошо
function f2(obj) {
  const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1;
}

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

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

// плохо
function f1(a) {
  a = 1;
  // ...
}

function f2(a) {
  if (!a) { a = 1; }
  // ...
}

// хорошо
function f3(a) {
  const b = a || 1;
  // ...
}

function f4(a = 1) {
  // ...
}

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

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

// плохо
const x = [1, 2, 3, 4, 5];
console.log.apply(console, x);

// хорошо
const x = [1, 2, 3, 4, 5];
console.log(...x);

// плохо
new (Function.prototype.bind.apply(Date, [null, 2016, 8, 5]));

// хорошо
new Date(...[2016, 8, 5]);

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

// плохо
function foo(bar,
             baz,
             quux) {
  // ...
}

// хорошо
function foo(
  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) {
  const y = x + 1;
  return x * y;
});

// хорошо
[1, 2, 3].map((x) => {
  const y = x + 1;
  return x * y;
});

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

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

// плохо
[1, 2, 3].map((number) => {
  const nextNumber = number + 1;
  `A string containing the ${nextNumber}.`;
});

// хорошо
[1, 2, 3].map((number) => `A string containing the ${number + 1}.`);

// хорошо
[1, 2, 3].map((number) => {
  const nextNumber = number + 1;
  return `A string containing the ${nextNumber}.`;
});

// хорошо
[1, 2, 3].map((number, index) => ({
  [index]: number,
}));

// Неявный возврат с побочными эффектами
function foo(callback) {
  const val = callback();
  if (val === true) {
    // Сделать что-то, если функция обратного вызова вернёт true
  }
}

let bool = 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 => {
  const y = x + 1;
  return x * y;
});

// хорошо
[1, 2, 3].map((x) => {
  const y = x + 1;
  return x * y;
});

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

// плохо
const itemHeight = (item) => item.height <= 256 ? item.largeSize : item.smallSize;

// плохо
const itemHeight = (item) => item.height >= 256 ? item.largeSize : item.smallSize;

// хорошо
const itemHeight = (item) => (item.height <= 256 ? item.largeSize : item.smallSize);

// хорошо
const itemHeight = (item) => {
  const { height, largeSize, smallSize } = item;
  return height <= 256 ? largeSize : smallSize;
};

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

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

// хорошо
(foo) => bar;
(foo) => (bar);
(foo) => (
   bar
)
⬆ к оглавлению

Классы и конструкторы

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

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

// плохо
function Queue(contents = []) {
  this.queue = [...contents];
}
Queue.prototype.pop = function () {
  const value = this.queue[0];
  this.queue.splice(0, 1);
  return value;
};

// хорошо
class Queue {
  constructor(contents = []) {
    this.queue = [...contents];
  }
  pop() {
    const value = this.queue[0];
    this.queue.splice(0, 1);
    return value;
  }
}

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

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

// плохо
const inherits = require('inherits');
function PeekableQueue(contents) {
  Queue.apply(this, contents);
}
inherits(PeekableQueue, Queue);
PeekableQueue.prototype.peek = function () {
  return this.queue[0];
};

// хорошо
class PeekableQueue extends Queue {
  peek() {
    return this.queue[0];
  }
}

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

// плохо
Jedi.prototype.jump = function () {
  this.jumping = true;
  return true;
};

Jedi.prototype.setHeight = function (height) {
  this.height = height;
};

const luke = new Jedi();
luke.jump(); // => true
luke.setHeight(20); // => undefined

// хорошо
class Jedi {
  jump() {
    this.jumping = true;
    return this;
  }

  setHeight(height) {
    this.height = height;
    return this;
  }
}

const luke = new Jedi();

luke.jump()
  .setHeight(20);

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

class Jedi {
  constructor(options = {}) {
    this.name = options.name || 'no name';
  }

  getName() {
    return this.name;
  }

  toString() {
    return `Jedi - ${this.getName()}`;
  }
}

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

// плохо
class Jedi {
  constructor() {}

  getName() {
    return this.name;
  }
}

// плохо
class Rey extends Jedi {
  constructor(...args) {
    super(...args);
  }
}

// хорошо
class Rey extends Jedi {
  constructor(...args) {
    super(...args);
    this.name = 'Rey';
  }
}

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

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

// плохо
class Foo {
  bar() { return 1; }
  bar() { return 2; }
}

// хорошо
class Foo {
  bar() { return 1; }
}

// хорошо
class Foo {
  bar() { return 2; }
}

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

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

Модули

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

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

// плохо
const AirbnbStyleGuide = require('./AirbnbStyleGuide');
module.exports = AirbnbStyleGuide.es6;

// ok
import AirbnbStyleGuide from './AirbnbStyleGuide';
export default AirbnbStyleGuide.es6;

// отлично
import { es6 } from './AirbnbStyleGuide';
export default es6;

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

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

// плохо
import * as AirbnbStyleGuide from './AirbnbStyleGuide';

// хорошо
import AirbnbStyleGuide from './AirbnbStyleGuide';

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

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

// плохо
// файл es6.js
export { es6 as default } from './AirbnbStyleGuide';

// хорошо
// файл es6.js
import { es6 } from './AirbnbStyleGuide';
export default es6;

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

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

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

// хорошо
import foo, { named1, named2 } from 'foo';

// хорошо
import foo, {
  named1,
  named2,
} from 'foo';

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

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

// плохо
let foo = 3;
export { foo };

// хорошо
const foo = 3;
export { foo };

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

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

// плохо
export function foo() {}

// хорошо
export default function foo() {}

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

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

// плохо
import foo from 'foo';
foo.init();

import bar from 'bar';

// хорошо
import foo from 'foo';
import bar from '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.

// плохо
import fooSass from 'css!sass!foo.scss';
import barCss from 'style!css!bar.css';

// хорошо
import fooSass from 'foo.scss';
import barCss from 'bar.css';

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

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

// плохо
import foo from './foo.js';
import bar from './bar.jsx';
import baz from './baz/index.jsx';

// хорошо
import foo from './foo';
import bar from './bar';
import baz from './baz';
⬆ к оглавлению

Итераторы и генераторы

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

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

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

const numbers = [1, 2, 3, 4, 5];

// плохо
let sum = 0;
for (let num of numbers) {
  sum += num;
}
sum === 15;

// хорошо
let sum = 0;
numbers.forEach((num) => {
  sum += num;
});
sum === 15;

// отлично (используйте силу функций)
const sum = numbers.reduce((total, num) => total + num, 0);
sum === 15;

// плохо
const increasedByOne = [];
for (let i = 0; i < numbers.length; i++) {
  increasedByOne.push(numbers[i] + 1);
}

// хорошо
const increasedByOne = [];
numbers.forEach((num) => {
  increasedByOne.push(num + 1);
});

// отлично (продолжайте в том же духе)
const increasedByOne = numbers.map((num) => num + 1);

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

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


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

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

// плохо
function * foo() {
  // ...
}

const bar = function * () {
  // ...
};

const baz = function *() {
  // ...
};

const quux = function*() {
  // ...
};

function*foo() {
  // ...
}

function *foo() {
  // ...
}

// очень плохо
function
*
foo() {
  // ...
}

const wat = function
*
() {
  // ...
};

// хорошо
function* foo() {
  // ...
}

const foo = function* () {
  // ...
};
⬆ к оглавлению

Свойства

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

const luke = {
  jedi: true,
  age: 28,
};

// плохо
const isJedi = luke['jedi'];

// хорошо
const isJedi = luke.jedi;

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

const luke = {
  jedi: true,
  age: 28,
};

function getProp(prop) {
  return luke[prop];
}

const isJedi = getProp('jedi');

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

// плохо
const binary = Math.pow(2, 10);

// хорошо
const binary = 2 ** 10;
⬆ к оглавлению

Переменные

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

// плохо
superPower = new SuperPower();

// хорошо
const superPower = new SuperPower();

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

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

// плохо
const items = getItems(),
    goSportsTeam = true,
    dragonball = 'z';

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

// хорошо
const items = getItems();
const goSportsTeam = true;
const dragonball = 'z';

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

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

// плохо
let i, len, dragonball,
    items = getItems(),
    goSportsTeam = true;

// плохо
let i;
const items = getItems();
let dragonball;
const goSportsTeam = true;
let len;

// хорошо
const goSportsTeam = true;
const items = getItems();
let dragonball;
let i;
let length;

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

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

// плохо - вызов ненужной функции
function checkName(hasName) {
  const name = getName();

  if (hasName === 'test') {
    return false;
  }

  if (name === 'test') {
    this.setName('');
    return false;
  }

  return name;
}

// хорошо
function checkName(hasName) {
  if (hasName === 'test') {
    return false;
  }

  const name = getName();

  if (name === 'test') {
    this.setName('');
    return false;
  }

  return name;
}

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

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

// плохо
(function example() {
  // JavaScript интерпретирует это, как
  // let a = ( b = ( c = 1 ) );
  // Ключевое слово let применится только к переменной a;
  // переменные b и c станут глобальными.
  let a = b = c = 1;
}());

console.log(a); // throws ReferenceError
console.log(b); // 1
console.log(c); // 1

// хорошо
(function example() {
  let a = 1;
  let b = a;
  let c = a;
}());

console.log(a); // throws ReferenceError
console.log(b); // throws ReferenceError
console.log(c); // throws ReferenceError

// тоже самое и для `const`

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

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

// плохо

const array = [1, 2, 3];
let num = 1;
num++;
--num;

let sum = 0;
let truthyCount = 0;
for (let i = 0; i < array.length; i++) {
  let value = array[i];
  sum += value;
  if (value) {
    truthyCount++;
  }
}

// хорошо

const array = [1, 2, 3];
let num = 1;
num += 1;
num -= 1;

const sum = array.reduce((a, b) => a + b, 0);
const truthyCount = array.filter(Boolean).length;

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

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

// плохо
const foo =
  superLongLongLongLongLongLongLongLongFunctionName();

// плохо
const foo
  = 'superLongLongLongLongLongLongLongLongString';

// хорошо
const foo = (
  superLongLongLongLongLongLongLongLongFunctionName()
);

// хорошо
const foo = 'superLongLongLongLongLongLongLongLongString';

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

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

// плохо

var some_unused_var = 42;

// Переменные, которые используются только для записи, не считаются используемыми.
var y = 10;
y = 5;

// Чтение для собственной модификации.
var z = 0;
z = z + 1;

// Неиспользуемые аргументы функции.
function getX(x, y) {
    return x;
}

// хорошо

function getXPlusY(x, y) {
  return x + y;
}

var x = 1;
var y = 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)
function example() {
  console.log(notDefined); // => выбросит ошибку ReferenceError
}

// обращение к переменной до её создания
// будет работать из-за подъёма.
// Примечание: значение true не поднимается.
function example() {
  console.log(declaredButNotAssigned); // => undefined
  var declaredButNotAssigned = true;
}

// интерпретатор понимает объявление
// переменной в начало области видимости.
// это означает, что наш пример
// можно переписать таким образом:
function example() {
  let declaredButNotAssigned;
  console.log(declaredButNotAssigned); // => undefined
  declaredButNotAssigned = true;
}

// использование const и let
function example() {
  console.log(declaredButNotAssigned); // => выбросит ошибку ReferenceError
  console.log(typeof declaredButNotAssigned); // => выбросит ошибку ReferenceError
  const declaredButNotAssigned = true;
}

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

function example() {
  console.log(anonymous); // => undefined

  anonymous(); // => TypeError anonymous не является функцией

  var anonymous = function () {
    console.log('anonymous function expression');
  };
}

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

function example() {
  console.log(named); // => undefined

  named(); // => TypeError named не является функцией

  superPower(); // => ReferenceError superPower не определена

  var named = function superPower() {
    console.log('Flying');
  };
}

// тоже самое справедливо, когда имя функции
// совпадает с именем переменной.
function example() {
  console.log(named); // => undefined

  named(); // => TypeError named не является функцией

  var named = function named() {
    console.log('named');
  };
}

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

function example() {
  superPower(); // => Flying

  function superPower() {
    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.4 Более подробную информацию можно узнать в статье Truth Equality and JavaScript от Angus Croll.

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

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

// плохо
switch (foo) {
  case 1:
    let x = 1;
    break;
  case 2:
    const y = 2;
    break;
  case 3:
    function f() {
      // ...
    }
    break;
  default:
    class C {}
}

// хорошо
switch (foo) {
  case 1: {
    let x = 1;
    break;
  }
  case 2: {
    const y = 2;
    break;
  }
  case 3: {
    function f() {
      // ...
    }
    break;
  }
  case 4:
    bar();
    break;
  default: {
    class C {}
  }
}

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

// плохо
const foo = maybe1 > maybe2
  ? "bar"
  : value1 > value2 ? "baz" : null;

// разбит на два отдельных тернарных выражения
const maybeNull = value1 > value2 ? 'baz' : null;

const foo = maybe1 > maybe2
  ? 'bar'
  : maybeNull;

// отлично
const foo = maybe1 > maybe2 ? 'bar' : maybeNull;

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

// плохо
const foo = a ? a : b;
const bar = c ? true : false;
const baz = c ? false : true;

// хорошо
const foo = a || b;
const bar = !!c;
const baz = !c;

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

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

// плохо
const foo = a && b < 0 || c > 0 || d + 1 === 0;

// плохо
const bar = a ** b - 5 % d;

// плохо
// можно ошибиться, думая что это (a || b) && c
if (a || b && c) {
  return d;
}

// плохо
const bar = a + b / c * d;

// хорошо
const foo = (a && b < 0) || c > 0 || (d + 1 === 0);

// хорошо
const bar = a ** b - (5 % d);

// хорошо
if (a || (b && c)) {
  return d;
}

// хорошо
const bar = a + (b / c) * d;
⬆ к оглавлению

Блоки

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

// плохо
if (test)
  return false;

// хорошо
if (test) return false;

// хорошо
if (test) {
  return false;
}

// плохо
function foo() { return false; }

// хорошо
function bar() {
  return false;
}

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

// плохо
function foo() {
  if (x) {
    return x;
  } else {
    return y;
  }
}

// плохо
function cats() {
  if (x) {
    return x;
  } else if (y) {
    return y;
  }
}

// плохо
function dogs() {
  if (x) {
    return x;
  } else {
    if (y) {
      return y;
    }
  }
}

// хорошо
function foo() {
  if (x) {
    return x;
  }

  return y;
}

// хорошо
function cats() {
  if (x) {
    return x;
  }

  if (y) {
    return y;
  }
}

// хорошо
function dogs(x) {
  if (x) {
    if (z) {
      return y;
    }
  } else {
    return z;
  }
}
⬆ к оглавлению

Управляющие операторы

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} element
function make(tag) {

  // ...

  return element;
}

// хорошо
/**
 * make() возвращает новый элемент
 * соответствующий переданному названию тега
 */
function make(tag) {

  // ...

  return element;
}

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

// плохо
const active = true;  // это текущая вкладка

// хорошо
// это текущая вкладка
const active = true;

// плохо
function getType() {
  console.log('fetching type...');
  // установить по умолчанию тип 'no type'
  const type = this.type || 'no type';

  return type;
}

// хорошо
function getType() {
  console.log('fetching type...');

  // установить по умолчанию тип 'no type'
  const type = this.type || 'no type';

  return type;
}

// тоже хорошо
function getType() {
  // установить по умолчанию тип 'no type'
  const type = this.type || 'no type';

  return type;
}

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

// плохо
//это текущая вкладка
const active = true;

// хорошо
// это текущая вкладка
const active = true;

// плохо
/**
 *make() возвращает новый элемент
 *соответствующий переданному названию тега
 */
function make(tag) {

  // ...

  return element;
}

// хорошо
/**
 * make() возвращает новый элемент
 * соответствующий переданному названию тега
 */
function make(tag) {

  // ...

  return element;
}

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

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

class Calculator extends Abacus {
  constructor() {
    super();

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

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

class Calculator extends Abacus {
  constructor() {
    super();

    // TODO: нужна возможность задать total через параметры
    this.total = 0;
  }
}
⬆ к оглавлению

Пробелы

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

// плохо
function foo() {
∙∙∙∙let name;
}

// плохо
function bar() {
∙let name;
}

// хорошо
function baz() {
∙∙let name;
}

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

// плохо
function test(){
  console.log('test');
}

// хорошо
function test() {
  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();
}

// плохо
function fight () {
  console.log ('Swooosh!');
}

// хорошо
function fight() {
  console.log('Swooosh!');
}

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

// плохо
const x=y+5;

// хорошо
const x = y + 5;

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

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

19.6 Используйте переносы строк и отступы, когда делаете длинные цепочки методов (больше 2 методов). Ставьте точку в начале строки, чтобы дать понять, что это не новая инструкция, а продолжение цепочки. eslint: newline-per-chained-call no-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();

// плохо
const leds = 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);

// хорошо
const leds = 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);

// хорошо
const leds = stage.selectAll('.led').data(data);
const svg = leds.enter().append('svg:svg');
svg.classed('led', true).attr('width', (radius + margin) * 2);
const g = svg.append('svg:g');
g.attr('transform', `translate(${radius + margin},${radius + margin})`).call(tron.led);

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

// плохо
if (foo) {
  return bar;
}
return baz;

// хорошо
if (foo) {
  return bar;
}

return baz;

// плохо
const obj = {
  foo() {
  },
  bar() {
  },
};
return obj;

// хорошо
const obj = {
  foo() {
  },

  bar() {
  },
};

return obj;

// плохо
const arr = [
  function foo() {
  },
  function bar() {
  },
];
return arr;

// хорошо
const arr = [
  function foo() {
  },

  function bar() {
  },
];

return arr;

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

// плохо
function bar() {

  console.log(foo);

}

// тоже плохо
if (baz) {

  console.log(qux);
} else {
  console.log(foo);

}

// хорошо
function bar() {
  console.log(foo);
}

// хорошо
if (baz) {
  console.log(qux);
} else {
  console.log(foo);
}

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

// плохо
class Person {
  constructor(fullName, email, birthday) {
    this.fullName = fullName;


    this.email = email;


    this.setAge(birthday);
  }


  setAge(birthday) {
    const today = new Date();


    const age = this.getAge(today, birthday);


    this.age = age;
  }


  getAge(today, birthday) {
    // ..
  }
}

// хорошо
class Person {
  constructor(fullName, email, birthday) {
    this.fullName = fullName;
    this.email = email;
    this.setAge(birthday);
  }

  setAge(birthday) {
    const today = new Date();
    const age = getAge(today, birthday);
    this.age = age;
  }

  getAge(today, birthday) {
    // ..
  }
}

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

// плохо
function bar( foo ) {
  return foo;
}

// хорошо
function bar(foo) {
  return foo;
}

// плохо
if ( foo ) {
  console.log(foo);
}

// хорошо
if (foo) {
  console.log(foo);
}

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

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

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

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

// плохо
const foo = {clark: 'kent'};

// хорошо
const foo = { clark: 'kent' };

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

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

// плохо
const foo = 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.'));

// хорошо
const foo = 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

// плохо
function foo() {return true;}
if (foo) { bar = 0;}

// хорошо
function foo() { return true; }
if (foo) { bar = 0; }

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

// плохо
var foo = 1,bar = 2;
var arr = [1 , 2];

// хорошо
var foo = 1, bar = 2;
var arr = [1, 2];

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

// плохо
obj[foo ]
obj[ 'foo']
var x = {[ b ]: a}
obj[foo[ bar ]]

// хорошо
obj[foo]
obj['foo']
var x = { [b]: a }
obj[foo[bar]]

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

// плохо
func ();

func
();

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

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

// плохо
var obj = { foo : 42 };
var obj2 = { foo:42 };

// хорошо
var obj = { foo: 42 };

19.19 Избегайте пробелов в конце строки. eslint: no-trailing-spaces

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

// плохо - множество пустых строк
var x = 1;


var y = 2;

// плохо - 2+ новых строк в конце файла
var x = 1;
var y = 2;


// плохо - 1+ новая строка в начале файла

var x = 1;
var y = 2;

// хорошо
var x = 1;
var y = 2;
⬆ к оглавлению

Запятые

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

// плохо
const story = [
    once
  , upon
  , aTime
];

// хорошо
const story = [
  once,
  upon,
  aTime,
];

// плохо
const hero = {
    firstName: 'Ada'
  , lastName: 'Lovelace'
  , birthYear: 1815
  , superPower: 'computers'
};

// хорошо
const hero = {
  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'],
};
// плохо
const hero = {
  firstName: 'Dana',
  lastName: 'Scully'
};

const heroes = [
  'Batman',
  'Superman'
];

// хорошо
const hero = {
  firstName: 'Dana',
  lastName: 'Scully',
};

const heroes = [
  'Batman',
  'Superman',
];

// плохо
function createHero(
  firstName,
  lastName,
  inventorOf
) {
  // ничего не делает
}

// хорошо
function createHero(
  firstName,
  lastName,
  inventorOf,
) {
  // ничего не делает
}

// хорошо (обратите внимание, что висячей запятой не должно быть после rest-параметра)
function createHero(
  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. Явное завершение ваших выражений и настройка вашего линтера для улавливания пропущенных точек с запятыми помогут вам предотвратить возникновение проблем.

// плохо - выбрасывает исключение
const luke = {}
const leia = {}
[luke, leia].forEach((jedi) => jedi.father = 'vader')

// плохо - выбрасывает исключение
const reaction = "No! That’s impossible!"
(async function meanwhileOnTheFalcon() {
  // переносимся к `leia`, `lando`, `chewie`, `r2`, `c3p0`
  // ...
}())

// плохо - возвращает `undefined` вместо значения на следующей строке. Так всегда происходит, когда `return` расположен сам по себе, потому что ASI (Автоматическая Вставка Точки с запятой)!
function foo() {
  return
    'search your feelings, you know it to be foo'
}

// хорошо
const luke = {};
const leia = {};
[luke, leia].forEach((jedi) => {
  jedi.father = 'vader';
});

// хорошо
const reaction = "No! That’s impossible!";
(async function meanwhileOnTheFalcon() {
  // переносимся к `leia`, `lando`, `chewie`, `r2`, `c3p0`
  // ...
  }());

// хорошо
function foo() {
  return 'search your feelings, you know it to be foo';
}
Читать подробнее.

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

Приведение типов

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

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

// => this.reviewScore = 9;

// плохо
const totalScore = new String(this.reviewScore); // тип totalScore будет "object", а не "string"

// плохо
const totalScore = this.reviewScore + ''; // вызывает this.reviewScore.valueOf()

// плохо
const totalScore = this.reviewScore.toString(); // нет гарантии что вернётся строка

// хорошо
const totalScore = String(this.reviewScore);

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

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

const inputValue = '4';

// плохо
const val = new Number(inputValue);

// плохо
const val = +inputValue;

// плохо
const val = inputValue >> 0;

// плохо
const val = parseInt(inputValue);

// хорошо
const val = Number(inputValue);

// хорошо
const val = parseInt(inputValue, 10);

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

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

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

2147483647 >> 0; // => 2147483647
2147483648 >> 0; // => -2147483648
2147483649 >> 0; // => -2147483647

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

const age = 0;

// плохо
const hasAge = new Boolean(age);

// хорошо
const hasAge = Boolean(age);

// отлично
const hasAge = !!age;
⬆ к оглавлению

Соглашение об именовании

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

// плохо
function q() {
  // ...
}

// хорошо
function query() {
  // ...
}

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

// плохо
const OBJEcttsssss = {};
const this_is_my_object = {};
function c() {}

// хорошо
const thisIsMyObject = {};
function thisIsMyFunction() {}

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

// плохо
function user(options) {
  this.name = options.name;
}

const bad = new user({
  name: 'nope',
});

// хорошо
class User {
  constructor(options) {
    this.name = options.name;
  }
}

const good = new User({
  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-WeakMap
const firstNames = new WeakMap();
firstNames.set(this, 'Panda');

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

// плохо
function foo() {
  const self = this;
  return function () {
    console.log(self);
  };
}

// плохо
function foo() {
  const that = this;
  return function () {
    console.log(that);
  };
}

// хорошо
function foo() {
  return () => {
    console.log(this);
  };
}

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

// содержание файла 1
class CheckBox {
  // ...
}
export default CheckBox;

// содержание файла 2
export default function fortyTwo() { return 42; }

// содержание файла 3
export default function insideDirectory() {}

// в других файлах
// плохо
import CheckBox from './checkBox'; // PascalCase import/export, camelCase filename
import FortyTwo from './FortyTwo'; // PascalCase import/filename, camelCase export
import InsideDirectory from './InsideDirectory'; // PascalCase import/filename, camelCase export

// плохо
import CheckBox from './check_box'; // PascalCase import/export, snake_case filename
import forty_two from './forty_two'; // snake_case import/filename, camelCase export
import inside_directory from './inside_directory'; // snake_case import, camelCase export
import index from './inside_directory/index'; // requiring the index file explicitly
import insideDirectory from './insideDirectory/index'; // requiring the index file explicitly

// хорошо
import CheckBox from './CheckBox'; // PascalCase export/import/filename
import fortyTwo from './fortyTwo'; // camelCase export/import/filename
import insideDirectory from './insideDirectory'; // camelCase export/import/directory name/implicit "index"
// ^ поддерживает оба варианта: insideDirectory.js и insideDirectory/index.js

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

function makeStyleGuide() {
  // ...
}

export default makeStyleGuide;

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

const AirbnbStyleGuide = {
  es6: {
  },
};

export default AirbnbStyleGuide;

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

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

// плохо
import SmsContainer from './containers/SmsContainer';

// плохо
const HttpRequests = [
  // ...
];

// хорошо
import SMSContainer from './containers/SMSContainer';

// хорошо
const HTTPRequests = [
  // ...
];

// также хорошо
const httpRequests = [
  // ...
];

// отлично
import TextMessageContainer from './containers/TextMessageContainer';

// отлично
const requests = [
  // ...
];
⬆ к оглавлению

Аксессоры

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

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

// плохо
class Dragon {
  get age() {
    // ...
  }

  set age(value) {
    // ...
  }
}

// хорошо
class Dragon {
  getAge() {
    // ...
  }

  setAge(value) {
    // ...
  }
}

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

// плохо
if (!dragon.age()) {
  return false;
}

// хорошо
if (!dragon.hasAge()) {
  return false;
}

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

class Jedi {
  constructor(options = {}) {
    const lightsaber = options.lightsaber || 'blue';
    this.set('lightsaber', lightsaber);
  }

  set(key, val) {
    this[key] = val;
  }

  get(key) {
    return this[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
});
⬆ к оглавлению

jQuery

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

// плохо
const sidebar = $('.sidebar');

// хорошо
const $sidebar = $('.sidebar');

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

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

// плохо
function setSidebar() {
  $('.sidebar').hide();

  // ...

  $('.sidebar').css({
    'background-color': 'pink',
  });
}

// хорошо
function setSidebar() {
  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();
⬆ к оглавлению

Поддержка ECMAScript 5

27.1 Можно посмотреть в таблице поддержки ES5 от пользователя Kangax .
⬆ к оглавлению

Возможности ECMAScript 6+ (ES 2015+)

28.1 Здесь собраны ссылки на различные возможности ES6.
Стрелочные функции
Классы и конструкторы
Сокращённая запись методов объекта
Сокращённая запись свойств объекта
Вычисляемые имена свойств объекта
Шаблонные строки
Деструктуризация
Параметры по умолчанию
Оставшиеся параметры
Оператор расширения
Let и Const
Итераторы и генераторы
Модули

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

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

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

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


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

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

// плохо
isNaN('1.2'); // false
isNaN('1.2.3'); // true

// хорошо
Number.isNaN('1.2.3'); // false
Number.isNaN(Number('1.2.3')); // true

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

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

// плохо
isFinite('2e3'); // true

// хорошо
Number.isFinite('2e3'); // false
Number.isFinite(parseInt('2e3', 10)); // true
⬆ к оглавлению

Тестирование

30.1 Ага.

function foo() {
  return true;
}

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

Производительность
On Layout & Web Performance
String vs Array Concat
Try/Catch Cost In a Loop
Bang Function
jQuery Find vs Context, Selector
innerHTML vs textContent for script text
Long String Concatenation
Are JavaScript functions like map(), reduce(), and filter() optimized for traversing arrays?
Загрузка...
⬆ к оглавлению

Ресурсы
Изучение ES6+

Последняя спецификация ECMA
ExploringJS
ES6 Compatibility Table
Comprehensive Overview of ES6 Features
Почитайте это

Standard ECMA-262
Инструменты

Линтеры
ESlint - Airbnb Style .eslintrc
JSHint - Airbnb Style .jshintrc
Neutrino Preset - @neutrinojs/airbnb
Другие руководства

Google JavaScript Style Guide
Google JavaScript Style Guide (Old)
jQuery Core Style Guidelines
Principles of Writing Consistent, Idiomatic JavaScript
StandardJS
Другие стили

Naming this in nested functions - Christian Johansen
Conditional Callbacks - Ross Allen
Popular JavaScript Coding Conventions on GitHub - JeongHoon Byun
Multiple var statements in JavaScript, not superfluous - Ben Alman
Дальнейшее чтение

Understanding JavaScript Closures - Angus Croll
Basic JavaScript for the impatient programmer - Dr. Axel Rauschmayer
You Might Not Need jQuery - Zack Bloom & Adam Schwartz
ES6 Features - Luke Hoban
Frontend Guidelines - Benjamin De Cock
Книги

JavaScript: The Good Parts - Douglas Crockford
JavaScript Patterns - Stoyan Stefanov
Pro JavaScript Design Patterns - Ross Harmes and Dustin Diaz
High Performance Web Sites: Essential Knowledge for Front-End Engineers - Steve Souders
Maintainable JavaScript - Nicholas C. Zakas
JavaScript Web Applications - Alex MacCaw
Pro JavaScript Techniques - John Resig
Smashing Node.js: JavaScript Everywhere - Guillermo Rauch
Secrets of the JavaScript Ninja - John Resig and Bear Bibeault
Human JavaScript - Henrik Joreteg
Superhero.js - Kim Joar Bekkelund, Mads Mobæk, & Olav Bjorkoy
JSBooks - Julien Bouquillon
Third Party JavaScript - Ben Vinegar and Anton Kovalyov
Effective JavaScript: 68 Specific Ways to Harness the Power of JavaScript - David Herman
Eloquent JavaScript - Marijn Haverbeke
You Don’t Know JS: ES6 & Beyond - Kyle Simpson
Блоги

JavaScript Weekly
JavaScript, JavaScript...
Bocoup Weblog
Adequately Good
NCZOnline
Perfection Kills
Ben Alman
Dmitry Baranovskiy
nettuts
Подкасты

JavaScript Air
JavaScript Jabber
⬆ к оглавлению

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

123erfasst: 123erfasst/javascript
4Catalyzer: 4Catalyzer/javascript
Aan Zee: AanZee/javascript
Airbnb: airbnb/javascript
AloPeyk: AloPeyk
AltSchool: AltSchool/javascript
Apartmint: apartmint/javascript
Ascribe: ascribe/javascript
Avant: avantcredit/javascript
Axept: axept/javascript
Billabong: billabong/javascript
Bisk: bisk
Bonhomme: bonhommeparis/javascript
Brainshark: brainshark/javascript
CaseNine: CaseNine/javascript
Cerner: Cerner
Chartboost: ChartBoost/javascript-style-guide
Coeur d'Alene Tribe: www.cdatribe-nsn.gov
ComparaOnline: comparaonline/javascript
Compass Learning: compasslearning/javascript-style-guide
DailyMotion: dailymotion/javascript
DoSomething: DoSomething/eslint-config
Digitpaint digitpaint/javascript
Drupal: www.drupal.org
Ecosia: ecosia/javascript
Evernote: evernote/javascript-style-guide
Evolution Gaming: evolution-gaming/javascript
EvozonJs: evozonjs/javascript
ExactTarget: ExactTarget/javascript
Flexberry: Flexberry/javascript-style-guide
Gawker Media: gawkermedia
General Electric: GeneralElectric/javascript
Generation Tux: GenerationTux/javascript
GoodData: gooddata/gdc-js-style
GreenChef: greenchef/javascript
Grooveshark: grooveshark/javascript
Grupo-Abraxas: Grupo-Abraxas/javascript
Happeo: happeo/javascript
Honey: honeyscience/javascript
How About We: howaboutwe/javascript
HubSpot: HubSpot/javascript
Hyper: hyperoslo/javascript-playbook
InterCity Group: intercitygroup/javascript-style-guide
Jam3: Jam3/Javascript-Code-Conventions
JSSolutions: JSSolutions/javascript
Kaplan Komputing: kaplankomputing/javascript
KickorStick: kickorstick
Kinetica Solutions: kinetica/javascript
LEINWAND: LEINWAND/javascript
Lonely Planet: lonelyplanet/javascript
M2GEN: M2GEN/javascript
Mighty Spring: mightyspring/javascript
MinnPost: MinnPost/javascript
MitocGroup: MitocGroup/javascript
Muber: muber
National Geographic: natgeo
NullDev: NullDevCo/JavaScript-Styleguide
Nulogy: nulogy/javascript
Orange Hill Development: orangehill/javascript
Orion Health: orionhealth/javascript
OutBoxSoft: OutBoxSoft/javascript
Peerby: Peerby/javascript
Pier 1: Pier1/javascript
Qotto: Qotto/javascript-style-guide
React: facebook.github.io/react/contributing/how-to-contribute.html#style-guide
REI: reidev/js-style-guide
Ripple: ripple/javascript-style-guide
Sainsbury’s Supermarkets: jsainsburyplc
Shutterfly: shutterfly/javascript
Sourcetoad: sourcetoad/javascript
Springload: springload
StratoDem Analytics: stratodem/javascript
SteelKiwi Development: steelkiwi/javascript
StudentSphere: studentsphere/javascript
SwoopApp: swoopapp/javascript
SysGarage: sysgarage/javascript-style-guide
Syzygy Warsaw: syzygypl/javascript
Target: target/javascript
Terra: terra
TheLadders: TheLadders/javascript
The Nerdery: thenerdery/javascript-standards
Tomify: tomprats
Traitify: traitify/eslint-config-traitify
T4R Technology: T4R-Technology/javascript
UrbanSim: urbansim
VoxFeed: VoxFeed/javascript-style-guide
WeBox Studio: weboxstudio/javascript
Weggo: Weggo/javascript
Zillow: zillow/javascript
ZocDoc: ZocDoc/javascript
⬆ к оглавлению

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

br Бразильский вариант португальского языка: armoucar/javascript-style-guide
bg Болгарский: borislavvv/javascript
ca Каталонский: fpmweb/javascript-style-guide
cn Китайский (Упрощённый): lin-123/javascript
tw Китайский (Традиционный): jigsawye/javascript
fr Французский: nmussy/javascript-style-guide
de Немецкий: timofurrer/javascript-style-guide
it Итальянский: sinkswim/javascript-style-guide
jp Японский: mitsuruog/javascript-style-guide
kr Корейский: ParkSB/javascript-style-guide
ru Русский: leonidlebedev/javascript-airbnb
es Испанский: paolocarrasco/javascript-style-guide
th Тайский: lvarayut/javascript-style-guide
tr Турецкий: eraycetinay/javascript
ua Украинский: ivanzusko/javascript
vn Вьетнамский: dangkyokhoang/javascript-style-guide
⬆ к оглавлению

Пообщаться с разработчиками Airbnb
Найдите их на gitter.
Участники перевода
Посмотреть всех
Лицензия
(The MIT License)

Copyright (c) 2012 Airbnb

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

The above copyright notice and this permission notice shall be included 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 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

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

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

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

leonidlebedev.github.io/javascript-airbnb/
Topics
react javascript styleguide translation eslint es6 style-linter linting style-guide es2015 naming-conventions arrow-functions es2017 es2016 es2018 es2019 es2020
Resources
 Readme
 Activity
Stars
 915 stars
Watchers
 44 watching
Forks
 270 forks
Report repository
Releases
No releases published
Packages
No packages published
Contributors
8
@leonidlebedev
@nialvi
@Eugeno
@mereskin-zz
@maslenkov
@VictoriaMaksimova
@skar404
@agladky
Deployments
10
 github-pages 3 years ago
+ 9 deployments
Footer
© 2024 GitHub, Inc.
Footer navigation
Terms
Privacy
Security
Status
Docs
Contact
Manage cookies
Do not share my personal information
Editing javascript-airbnb/README.md at master · leonidlebedev/javascript-airbnb