Czy w jakimkolwiek innym języku niż JavaScript istnieje różnica między lokalizacjami początkowymi nawiasów klamrowych (ta sama i następna linia)?

91

Dzisiaj, kiedy losowo czytałem książkę O'Reilly o wzorach JavaScript, znalazłem jedną interesującą rzecz (strona 27 w celach informacyjnych).

W JavaScript w niektórych przypadkach istnieje różnica, jeśli lokalizacja początkowa nawiasów klamrowych jest inna.

function test_function1() {
    return
    {
        name: 'rajat'
    };
}

var obj = test_function1();
alert(obj);  //Shows "undefined"

Podczas

function test_function2() {
    return {
        name: 'rajat'
    };
}

var obj = test_function2();
alert(obj); //Shows object

JSfiddle Demo

Czy jakikolwiek inny język ma takie zachowanie? Jeśli tak, to na pewno musiałbym zmienić nawyk .. :)

Martwię się głównie o PHP, C, C ++, Javę i Ruby.

Rajat Singhal
źródło
1
Reprodukowano w Chrome i IE9, dobry chwyt: P
gideon,
4
Czułość na białe przestrzenie można zmusić do działania - spójrz na Pythona lub tryb liniowy fortran - ale subtelna wrażliwość na białe spacje jest dziełem diabła. Gah! To jest tak złe, jak marka!
dmckee --- ex-moderator kitten
To imponujące! Niezłe znalezisko!
CheckRaise,
Teraz chcę wiedzieć, dlaczego javascript zachowuje się w ten sposób.
CheckRaise,
4
@CheckRaise: Podsumowuję zasady tutaj: blogs.msdn.com/b/ericlippert/archive/2004/02/02/…
Eric Lippert

Odpowiedzi:

53

Każdy język, który nie opiera się na średnikach (ale zamiast na nowych wierszach) do oddzielania instrukcji, potencjalnie na to pozwala. Rozważmy Pythona :

>>> def foo():
...   return
...   { 1: 2 }
... 
>>> def bar():
...   return { 1: 2 }
... 
>>> foo()
>>> bar()
{1: 2}

Być może uda ci się skonstruować podobny przypadek w Visual Basic, ale nie wiem, jak to zrobić, ponieważ VB jest dość restrykcyjny w miejscach, w których można umieszczać wartości. Ale poniższe powinny zadziałać, chyba że analizator statyczny narzeka na nieosiągalny kod:

Try
    Throw New Exception()
Catch ex As Exception
    Throw ex.GetBaseException()
End Try

' versus

Try
    Throw New Exception()
Catch ex As Exception
    Throw
    ex.GetBaseException()
End Try

Z wymienionych języków Ruby ma tę samą właściwość. PHP, C, C ++ i Java nie tylko dlatego, że odrzucają znak nowej linii jako biały znak i wymagają średników do oddzielania instrukcji.

Oto odpowiednik kodu z przykładu Pythona w Rubim:

>> def foo
>>   return { 1 => 2 }
>> end
=> nil
>> def bar
>>   return
>>   { 1 => 2 }
>> end
=> nil
>> foo
=> {1=>2}
>> bar
=> nil
Konrad Rudolph
źródło
2
Twój przykład VB nie do końca mówi o tym, ponieważ VB nigdy nie pozwala, aby instrukcja obejmowała wiele wierszy, chyba że używasz sekwencji kontynuacji linii „_”.
phoog
2
Ok, wycofuję poprzedni komentarz, ponieważ właśnie spojrzałem na specyfikację, jest kilka kontekstów, w których VB.NET obsługuje niejawne kontynuacje linii. Wątpię, żeby jakikolwiek doświadczony programista VB uznał ten przykład za "gotcha", ponieważ jest całkiem oczywiste, że Throwi ex.GetBaseException()są to oddzielne linie logiczne. Mówiąc dokładniej, ponieważ Basic historycznie używa linii do rozgraniczenia swoich instrukcji, „gotcha” byłaby bardziej prawdopodobna w sytuacji, w której programista sądzi, że utworzył nową instrukcję w nowej linii logicznej, ale tego nie zrobił.
phoog
@phoog Prawda, to absolutnie nie problem.
Konrad Rudolph
40

Interpreter JavaScript automatycznie dodaje znak ;na końcu każdego wiersza, jeśli go nie znajdzie (z pewnymi wyjątkami, nie wchodząc w to tutaj :).

Zasadniczo więc problemem nie jest lokalizacja nawiasów klamrowych (które tutaj reprezentują literał obiektu, a nie blok kodu, jak w większości języków), ale ta mała „funkcja”, która wymusza na pierwszym przykładzie return ;=> undefined. Możesz sprawdzić zachowanie return w specyfikacji ES5 .

W przypadku innych języków, które mają podobne zachowanie, sprawdź odpowiedź Konrada .

Alex Ciminian
źródło
5
Bardzo pozytywna odpowiedź, ale w rzeczywistości jest zła, przepraszam. Wyjaśnienie jest miłe, ale proszę poprawić błąd.
Konrad Rudolph
Część dotycząca JavaScript nie jest zła, sposób, w jaki zachowuje się tak, jak robi, wynika z wstawienia średnika, który wymusza undefinedzwrócenie. Napisałem trochę o innych językach z prefiksem afaik , więc weź to z przymrużeniem oka :).
Alex Ciminian
5
Ale nie jest prawdą, że JS wstawia średnik „na końcu każdego wiersza” „z pewnymi wyjątkami”; raczej zwykle nie wstawia średnika i jest tylko kilka przypadków, w których tak jest . Dlatego powoduje tak wiele problemów.
ruakh
26

Z całą pewnością. Język programowania go Google wykazuje bardzo podobne zachowanie (aczkolwiek z różnymi efektami). Jak tam wyjaśniono:

W rzeczywistości dzieje się tak, że język formalny używa średników, podobnie jak w C czy Javie, ale są one wstawiane automatycznie na końcu każdego wiersza, który wygląda jak koniec instrukcji. Nie musisz ich wpisywać samodzielnie.

..fantastyczna okazja...

Takie podejście sprawia, że ​​kod wygląda czysto i nie zawiera średników. Jedyną niespodzianką jest to, że ważne jest, aby umieścić otwierający nawias klamrowy konstrukcji, takiej jak instrukcja if, w tym samym wierszu co if; jeśli tego nie zrobisz, są sytuacje, które mogą się nie skompilować lub mogą dać zły wynik. Język w pewnym stopniu wymusza styl klamrowy.

Potajemnie, myślę, że Rob Pike chciał tylko wymówki, by wymagać stylu One True Brace.

Dave
źródło
10
Fajnie, nie wiedziałem o tym :). Osobiście uważam, że automatyczne wstawianie średników nie jest dobrym pomysłem. Może wprowadzać subtelne błędy, które osoby niedoświadczone w języku będą miały trudności ze zrozumieniem. Jeśli chcesz napisać kod bez średników, wolę sposób w Pythonie.
Alex Ciminian
@Alex językach nawet bez żadnej średnikami (Vb) mają tę właściwość. Tak samo jak Python, który najwyraźniej wolisz, mimo że obsługuje to identycznie jak JavaScript.
Konrad Rudolph
Głosowałbym za, z wyjątkiem tego, że twoje drugie zdanie jest tak całkowicie błędne, że mam ochotę przegłosować. Myślę, że anulują. ;-)
ruakh
1
@ruakh, czy masz na myśli „idź robi dokładnie to”, czy odnosisz się do żartu o robpiku? W pierwszym przypadku mógłbym przeformułować „go zachowuje się tak samo”, w drugim przepraszam, jeśli moje kiepskie poczucie humoru obraża;)
Dave
1
Mam na myśli to, że „Go robi dokładnie to”. Oryginalna propozycja wstawiania średnika w Go wyraźnie kontrastuje z tą z JavaScript, wyjaśniając: „Ta propozycja może przypominać Ci opcjonalną regułę średnika JavaScript, która w efekcie dodaje średniki, aby naprawić błędy analizy. Propozycja Go jest zupełnie inna”, a to jest całkiem prawda, na każdym poziomie: działa inaczej, ma inne efekty i prawie nie ma żadnych pułapek. (Egzekwowanie OTBS, choć irytujące, nie jest problemem, ponieważ jest to spójne wymaganie w całym kodzie Go.)
ruakh
14

Odpowiedź na to pytanie jest dość prosta. Każdy język, w którym zastosowano „automatyczne wstawianie średnika”, może mieć problemy w tym wierszu. Problem z tym

return
{
     name: 'rajat'
};

.. polega na tym, że aparat js wstawi średnik po return;instrukcji (a tym samym zwróci undefined). Ten przykład jest dobrym powodem, aby otwierać nawiasy klamrowe zawsze po prawej stronie, a nigdy również po lewej stronie. Ponieważ już poprawnie zauważyłeś, jeśli w tym samym wierszu znajduje się nawias klamrowy, tłumacz zauważy to i nie może wstawić średnika.

jAndy
źródło
6

FWIW, JSLint zgłasza kilka ostrzeżeń o następującej składni:

$ jslint -stdin
function foo(){
  return
  { x: "y" };
}
^D
(3): lint warning: unexpected end of line; it is ambiguous whether these lines are part of the same statement
  return
........^

(3): lint warning: missing semicolon
  { x: "y" };
..^

(3): lint warning: unreachable code
  { x: "y" };
..^

(3): lint warning: meaningless block; curly braces have no impact
  { x: "y" };
..^

(3): lint warning: use of label
  { x: "y" };
.....^

(3): lint warning: missing semicolon
  { x: "y" };
...........^

(3): lint warning: empty statement or extra semicolon
  { x: "y" };
............^


0 error(s), 7 warning(s)
Brandan
źródło
1

Pierwszym językiem, w którym się z tym spotkałem, był awk (który również ma swój udział w "osobliwościach" składniowych; opcjonalne średniki, konkatenacja ciągów przy użyciu tylko białych znaków i tak dalej ...) Myślę, że projektanci DTrace, którzy opierali składnię D luźno w awk, miałem dość rozsądku, aby NIE kopiować tych funkcji, ale nie pamiętam od początku mojej głowy. Prosty przykład (liczenie liczby tagów ENTITY w DTD, z mojego Maca):

$ cat printEntities.awk 
# This prints all lines where the string ENTITY occurs
/ENTITY/ {
  print $0
}
$ awk -f printEntities.awk < /usr/share/texinfo/texinfo.dtd | wc -l
     119

Gdyby zamiast tego ten mały skrypt został napisany z nawiasem klamrowym w osobnym wierszu, tak by się stało:

$ cat printAll.awk 
# Because of the brace placement, the print statement will be executed
# for all lines in the input file
# Lines containing the string ENTITY will be printed twice,
# because print is the default action, if no other action is specified
/ENTITY/
{ 
   print $0 
}
$ awk -f printAll.awk < /usr/share/texinfo/texinfo.dtd | wc -l
     603
$ /bin/cat < /usr/share/texinfo/texinfo.dtd | wc -l
     484
$ 
Anders S.
źródło