Чому слід уникати операторів збільшення ("++") та скорочення ("-") в JavaScript?


357

Однією з порад щодо інструменту jslint є:

++ і -
Оператори ++ (приріст) та - (декремент), як відомо, сприяють поганому коду, заохочуючи надмірну хитрість. Вони посідають лише недосконалу архітектуру в наданні вірусів та інших загроз безпеці. Існує опція plusplus, яка забороняє використання цих операторів.

Я знаю, що подібні конструкції PHP $foo[$bar++]можуть легко спричинити помилки за одним, але я не міг придумати кращий спосіб керувати циклом, ніж a while( a < 10 ) do { /* foo */ a++; }або for (var i=0; i<10; i++) { /* foo */ }.

Чи підкреслює їх jslint через те, що є деякі подібні мови, яким не вистачає синтаксису " ++" і " --", або обробляти його по-різному, чи є інші обґрунтування для уникнення " ++" і " --", які мені можуть бути відсутні?


59
Тому слід робити масив [index = index + 1] замість масиву [++ index] (якщо перший навіть дозволений!). Яке навантаження копати.
Лоуренс Дол,

34
Я не бачив Крокфорда робити показник = індекс + 1. Я бачив, як він робив індекс + = 1. Я думаю, що це розумна альтернатива. І це приємно, коли ти хочеш змінити крок на щось, окрім 1.
Nosredna

124
Особисто я не великий фанат Крокфорда. Він, здається, вважає все, що коли-небудь спричиняло помилку в його коді, як зло.
Паоло Бергантіно

38
У JavaScript ви повинні вважати кожну помилку якоюсь злою, оскільки немає офіційної документації, не існує постачальника сертифікатів, а також ви не навчаєтесь JS належним чином в університеті. Crockford та Firebug заповнили ці діри в освіті JavaScript.
stevek

40
++не викликає помилок. Використання ++"хитромудрих" способів може призвести до помилок, особливо якщо кодова база підтримує більше однієї людини, але це не проблема з оператором, це проблема з програмістом. Я не навчався JS в університеті (тому що він ще не існував), але що? Я робив C, що, звичайно, мав ++спочатку, але це також отримує "так що?" Я не ходив до університету, щоб вивчити певну мову, я пішов вивчати хороші практики програмування, які я можу застосувати до будь-якої мови.
nnnnnn

Відповіді:


408

На мій погляд, завжди використовувати ++ і - самостійно в одному рядку, як у:

i++;
array[i] = foo;

замість

array[++i] = foo;

Що-небудь поза цим може бентежити деяких програмістів і, на мою думку, просто не варто. Для циклів це виняток, оскільки використання оператора приросту є ідіоматичним і, таким чином, завжди зрозумілим.


3
Так, це я теж роблю. Важко помилитися, а іншим легше зрозуміти, що ти робиш.
Носредна

65
Це, здається, стає в основі проблеми і моєї розгубленості. Використовується поодинці, i ++; є кристально чистим. Щоразу, коли ви додасте інший код навколо цього базового вираження, читабельність та чіткість починають страждати.
artlung

2
Погодьтеся, але це стосується не тільки JavaScript
Tobias

5
Ви не говорите, чи пишете ви C або C ++. Можливо, ви повинні використовувати оператор збільшення префікса в C ++, якщо iзмінна пізніше стане складовим класом, і в цьому випадку вам буде непотрібно створювати тимчасовий об'єкт. Мені здається, що оператор Postfix є більш естетично приємним.
mc0e

2
Я віддаю перевагу 1-рядковій версії. Це нагадує операцію стека. push є масив [++ i] = foo, pop is bar = масив [i--]. (Але з масивами на основі 0, масив [i ++] = foo та bar = array [- i] виглядає все ще краще). Інша справа, я б ненавиджу це, якби хтось додав додатковий код між i ++; і масив [i] = foo ;.
Флоріан Ф

257

Мене відверто бентежить ця порада. Частина мене замислюється, чи не пов’язано це з відсутністю досвіду (сприйманого чи фактичного) з кодерами JavaScript.

Я можу бачити, як хтось, що просто "зломує" якийсь зразок коду, може зробити невинну помилку з ++ та -, але я не бачу, чому досвідчений професіонал уникне їх.


7
Хороший момент Джон Б. Тому це мене так турбує. Крокфорд - це кар'єрний програміст (варті десятиліть), який розмовляє різними мовами протягом десятиліть і працює з JavaScript в основному з моменту його створення. Я не думаю, що його порада походить від "Я лінивий і неуважний недосвідчений хакер" - тому я намагаюся зрозуміти, звідки вона береться.
artlung

14
@artlung Можливо, це більше стосується "ледачого та неуважного недосвідченого хакера", ймовірно, зіпсується з цим кодом, і я не хочу його плутати.
Кріс Кадмор

9
Я повністю не згоден з вашою відповіддю. На мою думку, справа не в тому, "я достатньо досвідчений, щоб використовувати його?" - але "Ясніше чи ні?" Якщо всередині циклу a для циклу або поодинці це суто ясно - всі зрозуміють, що це не шкодить. Проблема виникає тоді, коли викладений ++більш згорнутий вираз, у якому ++ x відрізняється від x ++, внаслідок чого щось не легко прочитати. Крокфордська ідея не про те, "чи можу я це зробити?" йдеться про "як я можу уникнути помилок?"
ArtoAle

1
Це особиста перевага, в якій точності ясність для вас важливіша, ніж компактність. Це виходить за межі достатнього досвіду, щоб зрозуміти код. Дивіться інші відповіді на прикладах, коли це більше клопоту, ніж варто.
Frug

1
Я цілком погоджуюся. Навіщо ганьбити себе тим, що не використовуєте частину мови? Я знаходжу вагомі причини для використання як пост-, так і попереднього збільшення та зменшення. Для мене такий рядок зрозумілий і стислий ... if (--imagesLoading === 0) start (); Якщо ви вважаєте, що ваш код незрозумілий, використовуйте коментарі!
xtempore

69

У C є історія таких дій, як:

while (*a++ = *b++);

скопіювати рядок, можливо, це джерело надмірної хитрості, про яку йдеться.

І завжди виникає питання про що

++i = i++;

або

i = i++ + ++i;

насправді так. Він визначений на деяких мовах, а в інших немає гарантії того, що станеться.

Ці приклади осторонь, я не думаю, що є щось ідіоматичне, ніж цикл, який використовується ++для збільшення. У деяких випадках ви можете втекти за допомогою петлі передбачення або певного циклу, який перевіряв інші умови. Але перекручувати код, щоб спробувати уникнути використання приросту, смішно.


104
i = i ++ + ++ i; змусив мене трохи боліти очі - :)
Hugoware

3
Мій також. Навіть якщо вони не були однаковою змінною, скажіть, у мене було x = a ++ + ++ b; - це поєднання миттєво ускладнює х, а і б важче утримувати в моїй голові. тепер, якби кожен був окремими висловлюваннями в окремих рядках, як у - a ++; ++ b; x = a + b; це було б легше зрозуміти з першого погляду.
artlung

10
На жаль, (a ++; b ++; x = a = b) не є тим самим значенням, що (a ++ + ++ b).
Thomas L Holaday

8
@Marius: x = a+++b-> x = (a++)+b-> x = a + b; a++. Токенізер жадібний.
Callum Rogers

14
Для додаткової безпеки роботи видаліть пробіли в останньому прикладі.
mc0e

48

Якщо ви прочитаєте JavaScript The Good Parts, ви побачите, що заміна Крокфорда на i ++ в циклі a for є i + = 1 (не i = i + 1). Це досить чисто і читабельно, і з меншою ймовірністю перетворюється на щось "складне".

Крокфорд зробив заборону автоматичного посилення та автокредитування опцією в jsLint. Ви вибираєте, чи слід дотримуватися поради

Моє особисте правило - не робити нічого в поєднанні з автоматичним посиленням або автоматичним посиленням.

Я дізнався з багаторічного досвіду роботи в C, що я не отримую перевищення буфера (або індекс масиву поза межами), якщо я продовжую використовувати це просто. Але я виявив, що я отримую перевищення буфера, якщо потрапляю в "надмірно складну" практику робити інші речі в тому ж самому заяві.

Тож, для моїх власних правил, використання i ++ як приросту в циклі for є прекрасним.


Відмінний пункт про те, що варіант "плюс" є саме цим, варіантом. Виходячи з ваших та інших відповідей, я думаю, я отримую відчуття, що ++ сама по собі або for-петля самі по собі не є заплутаними. Друге, що ви поєднуєте цей ++ в інший вислів, який ви твердите, стає складніше читати та розуміти в контексті.
artlung

6
кожен отримує переповнення буфера. Навіть OpenSSH з їх відкритим кодом та їх кількома комітетами перегляду
Ерік

11
@Eric Переглянувши свій коментар п'ятирічного віку: ой, як ви мали рацію!
wchargin

27

У циклі це нешкідливо, але в заяві про призначення це може призвести до несподіваних результатів:

var x = 5;
var y = x++; // y is now 5 and x is 6
var z = ++x; // z is now 7 and x is 7

Пробіл між змінною та оператором також може призвести до несподіваних результатів:

a = b = c = 1; a ++ ; b -- ; c; console.log('a:', a, 'b:', b, 'c:', c)

На завершення, несподівані результати також можуть бути проблемою:

var foobar = function(i){var count = count || i; return function(){return count++;}}

baz = foobar(1);
baz(); //1
baz(); //2


var alphabeta = function(i){var count = count || i; return function(){return ++count;}}

omega = alphabeta(1);
omega(); //2
omega(); //3

І це запускає автоматичне вставлення крапки з комою після нового рядка:

var foo = 1, bar = 2, baz = 3, alpha = 4, beta = 5, delta = alpha
++beta; //delta is 4, alpha is 4, beta is 6

попереднє посилення / післязростання плутанини може призвести до поодиноких помилок, які вкрай важко діагностувати. На щастя, вони теж цілком непотрібні. Є кращі способи додати 1 до змінної.

Список літератури


13
Але вони не "несподівані", якщо ви розумієте оператора. Це як ніколи не використовувати, ==тому що ви не розумієте різниці між ==і ===.
greg84

1
Саме так. Є питання C #, яке розкриває помилкові припущення.
Пол Sweatte

Я думаю, що Крокфорд полягає в тому, що більшість програмістів не повністю «розуміють оператора», навіть якщо вони думають, що це роблять; отже, існує небезпека їх використання, оскільки потенціал непорозуміння великий. Дивіться коментар @ Павла про помилкові припущення, що підсилює аргумент, що люди взагалі неправильно розуміють, як насправді працюють оператори префікса та постфікса. З цього приводу я визнаю, що мені подобається їх використовувати, але заради інших намагаюся обмежити їх використання ідіоматичними випадками (див . Відповідь cdmckay ).
gfullam

Здається, ваше посилання Whitespace порушено.
Руслан

Що складного у вашому прикладі "пробілів"? Я отримую прямий вихід a: 2 b: 0 c: 1. У першому прикладі я також не бачу нічого дивного чи несподіваного ("заява про призначення").
Кен Вільямс

17

Розглянемо наступний код

    int a[10];
    a[0] = 0;
    a[1] = 0;
    a[2] = 0;
    a[3] = 0;
    int i = 0;
    a[i++] = i++;
    a[i++] = i++;
    a[i++] = i++;

оскільки я ++ оцінюється вдвічі, то вихід (від налагоджувача vs2005)

    [0] 0   int
    [1] 0   int
    [2] 2   int
    [3] 0   int
    [4] 4   int

Тепер розглянемо наступний код:

    int a[10];
    a[0] = 0;
    a[1] = 0;
    a[2] = 0;
    a[3] = 0;
    int i = 0;
    a[++i] = ++i;
    a[++i] = ++i;
    a[++i] = ++i;

Зауважте, що вихід однаковий. Тепер ви можете подумати, що ++ i та i ++ однакові. Вони не

    [0] 0   int
    [1] 0   int
    [2] 2   int
    [3] 0   int
    [4] 4   int

Нарешті розглянемо цей код

    int a[10];
    a[0] = 0;
    a[1] = 0;
    a[2] = 0;
    a[3] = 0;
    int i = 0;
    a[++i] = i++;
    a[++i] = i++;
    a[++i] = i++;

Вихід зараз:

    [0] 0   int
    [1] 1   int
    [2] 0   int
    [3] 3   int
    [4] 0   int
    [5] 5   int

Тому вони не однакові, змішування обох призводить до не настільки інтуїтивної поведінки. Я думаю, що для циклів нормально ++, але слідкуйте за тим, щоб у вас було декілька символів ++ в одному рядку чи однакові інструкції


3
Дякуємо, що зробили цей експеримент. Особливо "просто виглядає" код, але моєму мозку стає важко втриматись дуже швидко. Однозначно потрапляє в категорію "хитрі".
artlung

28
Я зупинився після першого прикладу. Ви сказали "Розгляньте наступний код". Ні, ніхто не пише цього коду. Це ідіот, який пише, що хибний, а не мовна конструкція.
Сем Харвелл

4
Ви показали, що два різних фрагмента коду дають різний вихід, і це доводить, що це погано?
nickf

7
висновок - "стежте, коли у вас є кілька символів ++ в одному рядку чи однаковій інструкції". Я не думаю, що ти це читав
Ерік

1
"оскільки я ++ оцінюється двічі" - неправильно! Змінення змінної двічі між точками послідовності, а також її використання недійсне в C ++ і призводить до не визначеної поведінки. Тож компілятор може робити все, що завгодно. Результати, представлені тут, випадкові від реалізації компілятора.
tbleher

16

Характери операторів приросту та скорочення "до" та "після" можуть бути заплутаними для тих, хто не знайомий з ними; це один із способів, яким вони можуть бути хитрі.


29
Домовились; Однак, досвідчений професіонал повинен не слід плутати.
Нейт

1
Я думаю, це схоже на вказівки, щоб уникнути статичного статичного чи глобального. З цим, як з програмуванням програмування, немає нічого по суті, але необізнане використання може призвести до поганого коду.
Джоель Мартінес

6
Питання не в тому, чи зможе хороший програміст "пропрацювати свій шлях" за логікою - хороший код в ідеалі не вимагає жодної роботи для розуміння. Я думаю, що McWafflestix полягає в тому, що ви не повинні писати код, який при короткому огляді може призвести до додавання помилок через два тижні або два роки. Я знаю, що я був винен у додаванні коду до звичайного, якого я не повністю зрозумів :)
Майк,

1
@Mike: Так, зауважте, як я не вказав, чи можуть оператори бути складними для оригінального автора коду чи для більш пізнього обслуговування. Як правило, ми намагаємось припустити, що оригінальні кодери не використовують конструкції, з якими вони не знайомі / не зручні (на жаль, це часто неправильне припущення); однак, ця ознайомленість, безумовно, не поширюється на керівників (і, як зазначалося вище, може не поширюватися навіть на оригінального автора).
Пол Соньє

Я намагаюся написати свій код для читання іншими програмістами, але я не намагаюся писати його для читання початківцям.
Лука H

16

На мій погляд, "явне завжди краще, ніж неявне". Тому що в якийсь момент ви можете заплутатися в цьому твердженні про збільшення y+ = x++ + ++y. Хороший програміст завжди робить свій код більш читабельним.


1
У x ++ або ++ y немає нічого неявного.
Флоріан F

1
Ця читабельна річ з кодом хитра ... Я думаю, що існує базовий рядок того, наскільки простим повинен бути ваш код, і ми - як громада - повинні процвітати і вміти читати те, що зараз не читається, наприклад, однолінійні з вкладеними тернеріями, щоб дозволити більше запускати речі.
Хамза Уагад

14

Найважливіше обґрунтування уникнення ++ або - полягає в тому, що оператори повертають значення і викликають побічні ефекти одночасно, що ускладнює міркування про код.

З метою ефективності я віддаю перевагу:

  • ++ i, коли не використовується повернене значення (без тимчасового)
  • i ++ при використанні зворотного значення (без зупинки трубопроводу)

Я фанат містера Крокфорда, але в цьому випадку мені доводиться не погоджуватися. ++iце на 25% менше тексту для розбору, i+=1 і, певно, ясніше.


13

Я дивився відео Дугласа Крокфорда про це, і його пояснення щодо використання приросту та зменшення - це те

  1. У минулому він використовувався в інших мовах, щоб порушити межі масивів і викликати всі способи поганості і
  2. Що це більш заплутано, і недосвідчені розробники JS не знають, що саме це робить.

По-перше, масиви в JavaScript мають динамічний розмір, і, вибачте, якщо я помиляюсь, неможливо порушити межі масиву та отримати доступ до даних, до яких не можна отримати доступ за допомогою цього методу в JavaScript.

По-друге, чи слід уникати складних речей, безумовно, проблема полягає не в тому, що у нас є цей інструмент, але проблема полягає в тому, що там є розробники, які претендують на JavaScript, але не знають, як працюють ці оператори ?? Це досить просто. значення ++, дайте мені поточне значення і після виразу додайте до нього значення ++, збільште значення перед тим, як надати мені.

Вирази, такі як ++ + ++ b, легко розробити, якщо ви просто пам'ятаєте вище.

var a = 1, b = 1, c;
c = a ++ + ++ b;
// c = 1 + 2 = 3; 
// a = 2 (equals two after the expression is finished);
// b = 2;

Я думаю, ви щойно повинні запам'ятати, хто повинен читати код, якщо у вас є команда, яка знає JS зсередини, то вам не потрібно турбуватися. Якщо ні, то прокоментуйте це, напишіть інакше і т. Д. Робіть те, що вам потрібно зробити. Я не думаю, що приріст і зменшення по суті є поганими, або генеруються помилки, або створюється вразливість, можливо, менше читається залежно від вашої аудиторії.

До речі, я думаю, що Дуглас Крокфорд так чи інакше є легендою, але я думаю, що він викликав багато лякань за оператора, який цього не заслужив.

Я живу, щоб бути неправдивим, хоча ...


10

Інший приклад, більш простий, ніж деякі інші, з простим поверненням збільшення вартості:

function testIncrement1(x) {
    return x++;
}

function testIncrement2(x) {
    return ++x;
}

function testIncrement3(x) {
    return x += 1;
}

console.log(testIncrement1(0)); // 0
console.log(testIncrement2(0)); // 1
console.log(testIncrement3(0)); // 1

Як бачимо, жоден пост-приріст / декремент не повинен використовуватися при операторі return, якщо ви хочете, щоб цей оператор впливав на результат. Але повернення не "вловлює" операторів після збільшення / зменшення:

function closureIncrementTest() {
    var x = 0;

    function postIncrementX() {
        return x++;
    }

    var y = postIncrementX();

    console.log(x); // 1
}

Лише одна з ваших функцій отримує x як параметр
Calimero100582

8

Я думаю, що програмісти повинні бути компетентними мовою, якою вони користуються; використовувати його чітко; і добре використовувати його. Я не думаю, що вони повинні штучно калічити мову, якою вони користуються. Я кажу з досвіду. Я колись працював буквально по сусідству з магазином Cobol, де вони не використовували ELSE "тому що це було занадто складно". Reductio ad absurdam.


@downvoter Захоплююче. Ви не вважаєте, що програмісти повинні володіти мовою? Ви думаєте, що вони повинні штучно калічити мову? Що це таке? Тут немає жодного третього вибору.
Маркіз Лорн

1
і підсумок цього прикладу COBOL змушує мене ще більше радіти, що COBOL здебільшого вимер, перш ніж я потрапив у кодування
Марк К Коуан

1
@MarkCowan Я не розумію, чому. Моя думка про свавільне обмеження, а не про мову. Анекдот не вказує на щось погане з Коболом. Що потрібно було вимерти, це магазин програмування, і це було. Відвідайте нутри будь-якого великого банку, і ви побачите, що Кобол живий і здоровий.
Маркіз Лорн

2

Як вже згадувалося в деяких існуючих відповідях (що дратує я не можу коментувати), проблема полягає в тому, що x ++ ++ x оцінюють за різними значеннями (до vs після збільшення), що не очевидно і може бути дуже заплутано - якщо це значення використовується. cdmckay пропонує досить розумно дозволити використовувати оператор приросту, але лише таким чином, що повернене значення не використовується, наприклад, у його власному рядку. Я також включав би стандартне використання в циклі for (але тільки в третьому операторі, повернене значення якого не використовується). Я не можу придумати іншого прикладу. Будучи «спалений», я би рекомендував те саме керівництво і для інших мов.

Я не погоджуюся з твердженням, що ця надмірна суворість пояснюється тим, що багато програмістів JS є недосвідченими. Це саме той тип письма, який типовий для «надмірно розумних» програмістів, і я впевнений, що це набагато частіше зустрічається у більш традиційних мовах та у розробників JS, які мають досвід роботи з такими мовами.


1
Дякую за відповідь. Для того, щоб коментувати, вам знадобиться значення репутації 50 або більше. Див: stackoverflow.com/faq
artlung

1
@artlung: Я знаю це і обґрунтування цього. Я просто говорив, що це дратує :)
Близько Привмана

2

З мого досвіду, ++ i або i ++ ніколи не викликав плутанини, окрім як уперше дізнавшись про те, як працює оператор. Це важливо для найосновніших циклів і циклів, які викладаються будь-яким курсом середньої школи або коледжу, що викладається мовами, де можна використовувати оператора. Я особисто вважаю, що роблю щось на зразок того, що нижче, щоб шукати та читати краще, ніж щось із знаком ++, який знаходиться на окремому рядку.

while ( a < 10 ){
    array[a++] = val
}

Зрештою, це налаштування стилю, а не більше того, що важливіше - коли ви робите це у своєму коді, ви залишаєтесь послідовними, щоб інші, що працюють над тим самим кодом, могли слідувати, і не потрібно обробляти однакові функції по-різному .

Крім того, Крокфорд, здається, використовує i- = 1, який мені здається важче прочитати, ніж --i або i--


2

Мої 2 центи - це те, що їх слід уникати у двох випадках:

1) Коли у вас є змінна, яка використовується в багатьох рядках, і ви збільшуєте / зменшуєте її на першому операторі, який використовує її (або останньому, або, що ще гірше, в середині):

// It's Java, but applies to Js too
vi = list.get ( ++i );
vi1 = list.get ( i + 1 )
out.println ( "Processing values: " + vi + ", " + vi1 )
if ( i < list.size () - 1 ) ...

У подібних прикладах ви можете легко пропустити, що змінна автоматично збільшується / зменшується або навіть видаляється перше твердження. Іншими словами, використовуйте його лише в дуже коротких блоках або там, де змінна з’являється в блоці лише на кількох близьких висловлюваннях.

2) У випадку множини ++ і - приблизно однакова змінна в тому самому операторі. Дуже важко згадати, що відбувається у таких випадках:

result = ( ++x - --x ) * x++;

Екзамени та професійні тести задають подібні приклади, і справді я натрапив на це питання, шукаючи документацію про один з них, але в реальному житті не варто змушувати так багато думати про один рядок коду.


1

Фортран - мова, схожа на С? Він не має ані ++, ані -. Ось як ви пишете цикл :

     integer i, n, sum

      sum = 0
      do 10 i = 1, n
         sum = sum + i
         write(*,*) 'i =', i
         write(*,*) 'sum =', sum
  10  continue

Елемент індексу i збільшується правилами мови кожен раз через цикл. Якщо ви хочете збільшити щось інше, ніж 1, порахуйте, наприклад, два, наприклад, синтаксис ...

      integer i

      do 20 i = 10, 1, -2
         write(*,*) 'i =', i
  20  continue

Чи схожий на Python C? Він використовує розуміння діапазону та списку та інші синтаксиси, щоб обійти необхідність збільшення індексу:

print range(10,1,-2) # prints [10,8.6.4.2]
[x*x for x in range(1,10)] # returns [1,4,9,16 ... ]

Отже, грунтуючись на цьому рудиментарному дослідженні рівно двох альтернатив, мовні дизайнери можуть уникати ++ і - передбачуючи випадки використання та надаючи альтернативний синтаксис.

Чи відрізняються Fortran і Python від магніту-помилки, ніж процедурні мови, які мають ++ та -? Я не маю доказів.

Я стверджую, що Fortran і Python схожі на C, тому що я ніколи не зустрічав когось, хто вільно володів C, хто не міг би з 90% точністю відгадати правильність намірів непритомного Fortran чи Python.


1
Так, IFortran - з 1950-х, тоді як C - з 1970-х, тому Фортран може бути ні, як C. Цікавий приклад Python, якого не вистачає на зразок оператора plusplus. Ітерація над структурами даних простіше обробляється в Python, Python - це "інакше" об'єктно-орієнтований JavaScript. Можливо, порада Крокфорда стосується використання більше синтаксису, подібного до ОО, для ітерації.
artlung
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.