Бях четири седмици в тренировъчен лагер за кодиране чрез Flatiron, в който току-що бяхме завършили първата си фаза, интензивен модул по JavaScript. След като преди това се занимавах с Ruby, първоначално бях доста раздразнен от всички допълнителни битове (малка компютърна шега, хеп хеп хеп), които JavaScript изисква, за да прави неща, които преди съм правил в Ruby, използвайки по-малко знаци, по-малко стъпки и по-малко редове. Вземете тези два примера на код от проект, който реших в Ruby, след което по-късно реших с помощта на JavaScript. Тези фрагменти изпълняват същата задача:

Никакъв привидно ненужен елемент не предизвика гнева ми точно както точките и запетая, които непрекъснато виждах навсякъде в примерите за JavaScript код. Те изобщо не са необходими в Ruby и освен че не разбирах защо JavaScript се нуждае от тях, не разбрах точно къде трябваше да отидат. Беше разочароващо да имам нещо, което изглеждаше като съвършено добро решение на задание, след което трябваше да се върна и да се опитам да разбера къде трябваше да отидат взривените точки и запетая, преди да изпълня тестовете си. Те бяха навсякъде над всеки пример от Flatiron и всеки уважаван онлайн пример. Те очевидно изглеждаха много разпространени, важни и необходими, но какво изобщо правеха?

Първото ми впечатление беше, че изглеждаше аналогично на точка в писмения език - основно, посочвайки края на една мисъл или изявление и правейки ясно разграничение между края на това изявление и началото на следващото. Това предположение изглеждаше разумно и след това беше потвърдено от това, което намерих онлайн: точка и запетая в JavaScript завършва израз. Но как точно да разбера кога даден израз се счита за завършен и готов да бъде обвързан с точка и запетая? С ускореното темпо на програмата и присъщата природа на малките синтактични неща, които понякога правят големи разлики в кодирането, последното нещо, което исках, беше още едно малко синтактично нещо, за което да се тревожа.

Няколко лаборатории по-нататък осъзнах, че съм забравил точка и запетая в една от моите функции… Но вече бях пуснал тестовете… Всички тестове бяха издържани и кодът вървеше добре… 🤔 Опитах отново. Тестовете отново минаха. Дори премахнах всяка точка и запетая от кода и пуснах тестовете отново. Отново всички тестове преминаха успешно и кодът вървеше добре. Може би нещо не е наред в тези конкретни тестове...? Много объркан, попитах един от моите инструктори, който ми каза, че в повечето случаи използването на точка и запетая в края на израз не е задължително в JavaScript. Какво?? По избор?!? В моя кратък опит в кодирането бях прекарал часове в търсене на липсваща или допълнителна къдрава скоба, квадратна скоба, запетая, кавичка или скоба, които пречеха на кода ми да работи правилно. Предположих, че този бизнес с точка и запетая за завършване на израз в JavaScript ще бъде непоколебимо твърд, както всяка друга синтактична конвенция, която съм срещал. Вече бях посветил известно време и разочарование на темата. Научаването, че не е задължително, ме още повече обърка и разочарова.

В известен смисъл това беше добра новина за мен в краткосрочен план. Поддържането на темпото на усвояване на нова информация в тренировъчния лагер беше аналогично на пиенето от противопожарен маркуч и да имам една концепция по-малко, за да се тревожа за незабавното увиване на главата, беше много добре дошло. Въпреки това, освен че все още не разбирам как точно работят точките и запетая в JavaScript или къде да ги поставя, ако избера, сега имах много други въпроси:

  • Защо програмистите на JavaScript използват точка и запетая, ако не им се налага?
  • Защо те са във всички примери за JavaScript код в учебната програма на Flatiron, както и уважавани препратки като MDN и W3School, ако не са необходими?
  • Защо са били използвани на първо място??

И най-важното:

  • Какъв програмист съм?? Аз ли съм човек с точка и запетая или съм човек без точка и запетая?

Знаех, че трябва да се потопя дълбоко, за да разбера по-добре тази трудна част, точката и запетая, и да се надявам да разреша моята екзистенциална криза на кодираща идентичност… Ето какво научих:

Какво всъщност прави точката и запетая в JavaScript?

Точка и запетая се използва в JavaScript за обозначаване на края на израз, като отделя отделните изрази един от друг. Точката и запетая по същество казва на двигателя на JavaScript: Това е краят на този израз. Следващото в кода е отделно изявление. Какво се счита за израз в JavaScript, ще попитате?В общи линии изразът в JavaScript изпълнява действие, докато изразът произвежда стойност.

Даден този пример:

Един израз обаче винаги е част от израз, дори ако този израз се състои само от този израз. В горния пример поздрав е израз, състоящ се от едно твърдение. Неразделянето на изявления едно от друго може да доведе до някои неволни интерпретации на кода, точно както липсата на точка за край на изречението може да доведе до непреднамерени интерпретации в писмения език. Да предположим, че някой ме помоли да му разкажа нещо за мой приятел:

Първото изречение лесно може да се тълкува по начин, който определено не съм възнамерявал! Липсата на разделяне на изрази в JavaScript може да доведе до неволни интерпретации по същия начин. Поради тази причина, за да работи правилно, JavaScript трябва да прекрати всеки израз, преди да продължи напред в кода. Разработчикът обаче не е необходимо да вмъква точка и запетая, за да прекрати всеки израз, за ​​да може JavaScript да работи правилно... Последните две изречения може да изглеждат парадоксални, но и двете са верни! Повече за това по-долу, но първо: Защо някои програмисти избират винаги да използват точка и запетая, докато други ги избягват?

Защо някой използва точка и запетая в JavaScript, ако не е необходимо?

Като се консултирах с опитни JS инженери и преглеждах много онлайн статии, блогове и дискусионни табла, открих, че използването на точка и запетая за прекратяване на израз може да бъде доста поляризираща тема. Изглежда, че много успешни програмисти избират да не използват точка и запетая, освен в случаите, когато това е необходимо, защото смятат, че това прави кода им по-чист, по-елегантен и по-лесен за четене. По ирония на съдбата, една от основните причини, цитирани от тези в противоположния лагер за винаги завършване на изрази с точка и запетая, е, че това прави кода по-чист, по-елегантен и по-лесен за четене... 🤔

Мнозина, които използват точка и запетая само когато е необходимо, вярват, че това създава по-икономичен, по-чист и по-лек код, цитирайки общия принцип на кодиране за писане на най-простия, най-малък код, който евентуално ще работи. Тези, които винаги използват точка и запетая, ще заявят, че това е най-добрата практика, защото докато пропускането на точка и запетая в края на изразите ще работи през по-голямата част от времето, знанието къде завършват изразите и правилното им прекратяване с точка и запетая работи в 100% от времето.

Една определена тенденция, която забелязах, беше, че по-възрастните и по-опитни инженери преобладаващо подкрепяха навика винаги да завършват изрази с точка и запетая, без значение какво, и култивирането на този навик. Предполагам, че това е така, защото повечето други C-подобни езици, включително някои видни влиятели на JavaScript като C++ и Java, го изискват във всички случаи. Изричното поставяне на точка и запетая за прекратяване на изрази също изглежда е по-съответстващо на общите принципи на кодиране в най-популярните езици през 80-те и 90-те години.

Пропускане на точка и запетая за прекратяване на изрази, освен ако няма конкретна кодова конструкция, която изисква изглежда, че е станала по-популярна през последните 15 години. Предполагам, че това може да се дължи на кодирането, което все повече се учи в по-малко формални академични среди, както и на повишената популярност на други езици като Ruby и Python, които използват точка и запетая като разделители, а не терминатори.

Това е горещо обсъждана тема с привидно безкрайно количество дискусии и спорове онлайн и другаде (някои от тях са доста интензивни и забавни). Изводът изглежда е, че това е лично предпочитание. Ако знаете къде свършват вашите твърдения и научите нюансите и изключенията, няма голямо значение кой начин ще изберете да напишете своя JS код. Важното е последователността - решете да ги използвате винаги или да ги използвате само когато е необходимо, след което се придържайте към това, което решите.

Добре, но ЗАЩО точката и запетая не са задължителни в JavaScript? как??

Целият този предмет на дебат е възможен благодарение на нещо в JS, наречено автоматично вмъкване на точка и запетая (ASI). Можете да намерите официалната документация за ASI ТУК. По същество JS интерпретаторът автоматично ще „добави“ точка и запетая, за да прекрати изрази в определени ситуации, но не и в други. Следователно, често използваното опростяване, че точката и запетая не са задължителни за прекратяване на изрази в JS през повечето време.

В случаите, когато точка и запетая не се „добавя“ автоматично от ASI, анализаторът на JS понякога може да свърже множество изрази, които са предназначени да бъдат разделени. Това може да доведе до неочаквани резултати или грешки. В Ruby, например, новият ред прекратява операторите в следния пример.

Това работи:

В JS нов ред често ще прекрати израз, но не винаги. Ако опитам същия тип нещо в JS, ще попадна на грешка:

Това е така, защото JS машината чете това като такова:

Следователно горната грешка казва „Хей, z дори още не е дефиниран. Как можем да го използваме в самодефиниция, преди дори да е дефинирано, голяма глупост!“

Ето още един пример, който ще предизвика различна грешка поради същата липсваща точка и запетая:

Горният код ще изведе TypeError:

Защото, отново, без точка и запетая, JS машината вижда това:

...и смята, че y(x+y) е извикване на функция. напр. функция(параметър)

Нека опитаме тези два примера с точка и запетая, изрично завършваща всеки израз:

Но чакайте, защо редове 1 и 2 да не бъдат свързани и в предишния пример? Защо това?:

Да се ​​тълкува като това?:

а не това?:

Това е така, защото, както беше отбелязано по-рано, нов ред често ще прекрати израз в JS, но не винаги. Колко объркващо... Нека стигнем до дъното на това.

Официалната документация на ECMAScript изброява три правила на ASI. Въпреки че има само три правила, има безброй сценарии, които могат да доведат до неочаквани резултати или грешки, ако тези правила не се разбират или следват напълно. Нека да разгледаме правилата и някои примери за това как всяко от тях може да доведе до проблеми с нашия код.

(Настрана за наистина педантичния (аз): Автоматичното вмъкване на точка и запетая е просто термин. Той описва как се анализира кодът, но в действителност не се вмъкват точки и запетая в изходния код по време на анализирането. Всъщност мислене от гледна точка на точка и запетая вмъкването е начин да се разберат правилата, управляващи ASI и прекратяването на изрази в JS. Но за всички намерения и цели е напълно добре да се преструвате, че наистина се вмъкват точки и запетая, ако е по-лесно да го мислите по този начин.)

ASI Правило #1

„Когато, докато скрипт или модул се анализират от ляво на дясно, се срещне токен (наречен токен за нарушение), който не е разрешен от никаква продукция на граматиката, тогава автоматично се вмъква точка и запетая преди обиден токен, ако едно или повече от следните условия е вярно:

  • Неправилният токен е отделен от предишния токен с поне един LineTerminator.
  • Неправилното означение е }
  • Предишният токен е ) и вмъкнатата точка и запетая след това ще бъде анализирана като крайна точка и запетая на команда do-while“ — EMCA 262 6-то издание

Нека се опитаме да разберем тези подправила един по един.

Имайки предвид следния код, можем да видим, че първите две подправила на правило #1 влизат в действие:

Това ще регистрира истината, както е предвидено:

JS компилаторът ще започне да анализира горния код отляво надясно, започвайки от ред 1. Той продължава да анализира до първия знак на ред 2 (c), който е първият знак, който не е разрешен за JS граматика (известен като обиден токен). По същество ще го прочете като:

…и кажете „Хей, не мога да разбера никакъв смисъл от това c тук.“ Тук идва ASI! В този момент, тъй като неправилният токен (c) е непосредствено предшестван от нов ред, точка и запетая ще бъде „вмъкната автоматично“ между 8 в края на ред 1 и c в началото на ред 2, според първото подправило на ASI правило #1, и JS ще каже „А, виждам прекъсването на този ред там и знам, че възнамерявахте за ред 1 и ред 2 да бъдат отделни изявления.“

Ако искаме да бъдем изрични с нашите прекратявания на изявления, можем да го напишем така:

Можем обаче да изберем да пропуснем точката и запетая в края на ред 1, защото JS ще „вмъкне“ точка и запетая там за нас според първото подправило на правило №1 на ASI.

Нека да разгледаме останалата част от нашия пример:

Ред 5 е изявление, така че знаем, че JS ще изисква той да бъде прекратен с точка и запетая. Въпреки това, тъй като „нарушаващият токен“ е } в този случай, ASI ще се намеси и ще прекрати това изявление вместо нас съгласно второто подправило на ASI правило #1. Компилаторът ще го прочете като:

Предвид горния пример, разработчикът може да избере да го напише по следния начин:

Или това:

И двата горни примера са валидни JS и ще бъдат прочетени от компилатора по същия начин. За всички намерения и цели те са едно и също нещо.

О, както и тези по този въпрос:

Последните два примера са само за бъркане, но можете да видите колко голяма свобода може да има понякога с поставянето на точка и запетая в JS. И четирите примера по-горе са валидни JS и ще бъдат прочетени от компилатора по същия начин.

Ще разгледам това трето подправило по-долу, тъй като за мен е по-логично да го групирам с правило #3.

Правило №1 в просто изречение:

ASI ще „вмъкне“ точка и запетая преди неправилен токен, ако неправилният токен е затваряща фигурна скоба или ако има нов ред непосредствено преди него.

Правило №2 на ASI

„Когато, докато скриптът или модулът се анализират отляво надясно, се среща краят на входния поток от токени и анализаторът не може да анализира входния поток от токени като единичен пълен ECMAScript скрипт или модул , тогава в края на входния поток автоматично се вмъква точка и запетая.“EMCA 262 6-то издание

Това правило всъщност е освежаващо лесно за разбиране. По принцип това правило гласи, че ако JS компилаторът стигне до края на файл и няма точка и запетая като последен токен във файла, ASI ще „добави“ такъв. Това става много полезно, когато множество файлове (вероятно написани от множество разработчици) са свързани в програма.

Правило №2 в просто изречение:

Ако в края на файла няма точка и запетая, ASI ще „вмъкне“ такава.

Правило #3 на ASI

„Когато, докато скриптът или модулът се анализират отляво надясно, се среща токен, който е разрешен от някаква продукция на граматиката, но продукцията е ограничена продукция и токенът ще бъде първият токен за терминал или нетерминал непосредствено след анотацията „[тук няма LineTerminator]“ в рамките на ограниченото производство (и следователно такъв токен се нарича ограничен токен), а ограниченият токен е отделен от предишния токен с поне един LineTerminator, тогава точка и запетая се вмъква автоматично преди ограниченото означение.“ — EMCA 262 6-то издание

Господи… добре, че второто правило беше лесно за разбиране. Това е изключително тромаво за четене, но всъщност не е толкова лошо, колкото изглежда.

Нека се опитаме да разбием това. Първо, какво е „ограничено производство“? Документацията удобно ги изброява, следвайки третото правило:

По същество това правило казва, че ако имате нов ред непосредствено след едно от тези десет неща, изброени по-горе, ASI ще „вмъкне“ точка и запетая точно преди този нов ред. Ето един много достъпен пример с използване на израз за връщане:

Искам да върна този обект, но функцията ми връща недефиниран. Това е така, защото имаме граматически правилен ограничен оператор (изявление за връщане), в който ограниченият токен } (първият токен за терминал или нетерминал непосредствено след анотацията „[тук няма LineTerminator]“ )се отделя от предишния токен с нов ред.

Компилаторът вижда това:

Ако добавя още двойки ключ-стойност към този обект, ще получа грешка поради същото ASI правило:

ASI приема точка и запетая след оператора return поради прекъсването на реда. Ето как ще бъде анализиран:

Сега имаме този обект, който просто стои в средата на нашата функция и не прави нищо, или е блок от код, групиращ свързани изрази?? JS също не знае и след като стигне до : на ред 4, той казва, „Хей, в този момент вече няма жизнеспособен начин да разбера този код, не знам какво се опитвате да направите.“

Като поставим нашата отваряща къдрава скоба преди прекъсването на реда (на същия ред като израза за връщане), можем да избегнем тези проблеми:

Поради ASI горният код ще бъде анализиран като такъв:

Разбира се, разработчикът може също да избере изрично да добави точка и запетая, както е показано по-горе. Така или иначе, ще бъде анализиран по същия начин.

Връщането е най-често срещаното, но по същество същият принцип ще се прилага и за другите изброени „ограничени продукции“ (оператори за прекъсване, оператори за хвърляне, оператори за продължаване и т.н.).

Изрази за нарастване на PostFix:

Нека също да разгледаме набързо друг често срещан случай, в който се появява ASI правило #3, изрази за постфиксно увеличение. Първо, нека се уверим, че разбираме разликата между тези два случая:

Можем да видим в горния пример, че someNumber връща текущата си стойност и след товаувеличава стойността. Можем да видим, че ако извикаме променливата someNumber отново, тя наистина е била увеличена, след като е била върната преди това.

В горния пример беше използван оператор за нарастване на префикс, така че променливата someNumber беше увеличена before да бъде върната. Тогава можем да видим, извиквайки отново променливата someNumber, че нейната стойност все още е 6.

Прекъсването на редовете може да причини проблеми с тези оператори за нарастване поради ASI. Вземете този пример:

Както виждаме, someNumberне беше увеличено тук, но защо? Да видим какво се случи с друг номер:

Or:

Това, което се случва, е, че ASI „вмъква“ точка и запетая след someNumber вместо ++поради прекъсването на реда. Благодарение на ASI, компилаторът видя:

Изводът тук е доста прост: Поставете операторите за нарастване на същия ред като това, което искате да увеличават.

Правило #3 в просто изречение:

Когато използвате някое от тези:

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

Обърнете внимание на думата започнете. Поставянето на отварящата фигурна скоба, съдържаща израза, на същия ред като ключовата дума, се счита за на същия ред и е обичайна практика в JS.

Друг начин да го разгледате е да запомните тези две правила:

  1. ако въведете думите продължи, прекъсване, връщане, хвърляне или отстъпване, последвайте ги незабавно с отваряща фигурна скоба или каквото искате да се използва с нея на същия ред.
  2. Винаги поставяйте операторите за нарастване на същия ред като това, което искате да увеличават.

Разбира се, има безброй други примери, които определено си заслужава да бъдат проучени. Въпреки това, спазването на удебеленото изречение и две прости правила по-горе ще ви спести от усложненията с правило #3 на ASI през по-голямата част от времето.

Няколко случая „точка и запетая в JS са незадължителни, освен когато не са“.

ASI никога няма да вмъкне точка и запетая в началото на „for цикъл“:

Точка и запетая в заглавията на „for цикли“ се използват за разделяне на аргументи, а не за прекратяване на изрази. Това:

Ще регистрира:

Но ASI никога няма да вмъкне липсваща точка и запетая в началото на for цикъл във всеки случай, включително прекъсвания на редове:

Вземете тези примери с липсващи точки и запетая в началото на „for цикъл“:

И двете ще хвърлят тази грешка, защото ни липсват точки и запетая, от които компилаторът се нуждае, за да знае какво да прави, но ASI никога няма да ги „вмъкне“, защото те са вътре в главата на „for цикъл“:

ASI няма да се задейства, ако „вмъкнатата“ точка и запетая бъде анализирана като празен оператор:

Цикълът for е добър начин да илюстрирате и това правило. Предвид този пример, в който искам да превърна този масив от „не“ в „Да!“:

Това ще регистрира:

Това е така, защото компилаторът вижда:

По-горе console.log() е част от „изявлението for“ и ще се регистрира 4 пъти на втория аргумент на главата „for цикъл“. Масивът се променя всеки път, но не това исках. Исках да мине и да променя всяко "не" на "Да!" след това конзолата регистрира актуализирания масив. Трябва да разделя тези изявления с точка и запетая като такава:

Това прави това, което искам, защото последната точка и запетая на ред 3 е празен оператор. Ето защо ASI не задейства „вмъкване“ на точка и запетая в края на ред 3, когато първоначално го пропуснах.

ASI няма да разделя изразите, появяващи се на един ред:

Това е доста просто. Изявленията, които не са разделени с нов ред, трябва да бъдат разделени с точка и запетая.

Следните типове изрази не е необходимо да бъдат завършвани с точка и запетая и няма да задействат ASI:

  • Изявления „ако“.
  • Изявления „друго“.
  • Изявления "за".
  • Изявления „докато“.

  • Функционални отчети

Хубавото на горните примери е, че ако изрично добавите точка и запетая след }, това просто ще се чете като празен израз и няма да засегне кода ви. В тези случаи това е наистина незадължително.

ОБАЧЕ циклите „do…while“ и функционалните изрази НАПРАВО трябва да завършват с точка и запетая… Но ASI ще ги „вмъкне“, ако забравите:

Същият принцип се прилага при функционални изрази:

Общи съвети, ако изберете да пропуснете незадължителните точки и запетая

  • Никога не поставяйте две твърдения на един ред
  • Избягвайте да започвате нов ред с ( или [
  • Ако въведете думите return, continue, break, throw или yield, последвайте ги веднага с отваряща фигурна скоба на същия ред
  • Винаги поставяйте операторите за нарастване на същия ред като това, което искате да увеличават.
  • Точки и запетая винаги се изискват изрично в началото на „цикъл for“
  • Отделете време, за да разберете какво прави ASI

Защо трябва да се грижим за ASI

Независимо дали изберете да пропуснете незадължителните точки и запетая или да ги използвате изрично за прекратяване на изрази, важно е да знаете какво прави ASI по много причини:

  • ASI е невидим. Не можете да видите ASI да работи, нито някога ще има известие, което е задействано (освен от неочаквана грешка). Свързаните неща, които са визуално разделени пред вас, могат да бъдат много объркващи, ако не сте запознати с ASI.
  • ASI може да превърне невалидния JS код в синтактично валидна глупост, която може да бъде много трудна за отстраняване на грешки, особено ако няма грешка, хвърлена от кода.
  • Въпреки че този блог се съсредоточи върху проблеми, които могат да възникнат от ASI, като същевременно се пропускат незадължителните точки и запетая, има и случаи, в които изричната точка и запетая може да доведе до неочаквани резултати поради ASI (още един блог очаквайте скоро!).
  • Минимизирането и компресирането често разчитат на изрични точки и запетая, за да работят, така че ще трябва да знаете къде да ги вмъкнете правилно, ако използвате един от тези инструменти.
  • Компаниите и проектите с отворен код обикновено изискват да пишете със или без точка и запетая, ако искате да работите за тях или да допринесете.
  • Преди всичко придобиването на по-задълбочено разбиране за това какво прави нашият код и защо винаги ще ни прави по-добри разработчици!

Какъв програмист съм??

И така, относно моята екзистенциална криза на програмната идентичност, която ме доведе в тази заешка дупка: със сигурност съм в лагера на винаги завършващите изявления с точка и запетая. От моя гледна точка ASI създава проблеми там, където не е имало такива. Знам, че разбирам как работи много по-ясно, но не разбирам защо съществува на първо място. Ако JS просто изискваше изразите да бъдат завършвани с точка и запетая и всички го научиха по този начин, нито един от тези проблеми нямаше да съществува. От друга страна, ако не съществуваше, за какво щях да блогвам? В крайна сметка двете важни неща са, че JS разработчиците знаят как работи ASI и са последователни с решението си да включат или пропуснат незадължителни точки и запетая в своя код. Надявам се, че тази публикация в блога може да помогне на новите разработчици от двете убеждения!

Благодарим ви, че прочетохте! Моля, не се колебайте да коментирате и/или коригирате, тъй като искам този блог да бъде възможно най-фактологичен и полезен. Приятно кодиране!