Javascript heredoc

109

Potrzebuję czegoś takiego jak heredoc w JavaScript. Masz na to jakieś pomysły? Potrzebuję funkcji wielu przeglądarek.

Znalazłem to:

heredoc = '\
<div>\
    <ul>\
        <li><a href="#zzz">zzz</a></li>\
    </ul>\
</div>';

Myślę, że to zadziała. :)

VeroLom
źródło
6
Duplikat stackoverflow.com/questions/805107/…, który zawiera bardziej szczegółowe odpowiedzi.
Chadwick
4
Konieczność dodania „\” sprawia, że ​​za każdym razem marszczę brwi. Imho, brak normalnej składni dla ciągów wielowierszowych jest całkowicie nieuzasadniony. Mogli dodać to w dowolnej wersji, ale tego nie zrobili.
Lex,
1
Możliwy duplikat tworzenia ciągów wielowierszowych w JavaScript
brasofilo
Obecnie odbywa się to za pomocą literałów szablonu, jak pokazano w możliwym duplikacie (tutaj nie ma zaktualizowanej odpowiedzi).
brasofilo

Odpowiedzi:

77

Wypróbuj szablon ciągów ES6 , możesz zrobić coś takiego

var hereDoc = `
This
is
a
Multiple
Line
String
`.trim()


hereDoc == 'This\nis\na\nMultiple\nLine\nString'

=> true

Możesz korzystać z tej wspaniałej funkcji już dziś, korzystając z 6to5 lub TypeScript

mko
źródło
2
Ma to zastosowanie, jeśli potrzebujesz tylko ciągów wieloliniowych. Jednak ponieważ nie możesz tak naprawdę zmienić symbolu, który otacza twój ciąg, tak naprawdę nie jest to heredoc.
Peeyush Kushwaha
3
Uwaga: oryginalne pytanie zostało zadane 5 lat temu, zanim ES6 był dostępny. To najlepsza praktyka posuwająca się naprzód, ponieważ poprawia się również wydajność.
Henry Tseng
2
@HenryTseng, więc czy sugerujesz, że odpowiedzi na to pytanie powinny być dostosowane do starożytnych technologii, które istniały 5 lat temu? Jeśli pytanie jest nadal otwarte, to zasługuje na wykorzystanie nowych technologii w miarę ich powstawania. W ten sposób nowi użytkownicy mający ten sam problem mogą znaleźć tutaj informacje „niearcheologiczne”.
asiby
1
Nie, komentowałem, dlaczego wcześniej to rozwiązanie nie było bardziej widoczne. Wygląda na to, że obsługiwany sposób posuwa się naprzód, jeśli nie ma żadnych problemów ze zgodnością.
Henry Tseng
63

Nie, niestety JavaScript nie obsługuje niczego takiego jak heredoc.

Andrew Hare
źródło
12
Wiem, ale mam nadzieję, że znajdę hack heredoc :)
VeroLom
Coś w rodzaju komentarzy funkcji parsowania (ale nie działa w ie / firefox) =
VeroLom
37

Co powiesz na to:

function MyHereDoc(){
/*HERE
<div>
   <p>
      This is written in the HEREDOC, notice the multilines :D.
   </p>
   <p>
      HERE
   </p>
   <p>
      And Here
   </p>
</div>
HERE*/
    var here = "HERE";
    var reobj = new RegExp("/\\*"+here+"\\n[\\s\\S]*?\\n"+here+"\\*/", "m");
    str = reobj.exec(MyHereDoc).toString();
    str = str.replace(new RegExp("/\\*"+here+"\\n",'m'),'').toString();
    return str.replace(new RegExp("\\n"+here+"\\*/",'m'),'').toString();
}

//Usage 
document.write(MyHereDoc());

Po prostu zastąp „/ * TUTAJ” i „TUTAJ * /” wybranym słowem.

Zv_oDD
źródło
4
czy wszystkie przeglądarki / silniki zwracają komentarze w Function.toString ()? to bardzo sprytne
gcb
Działa na konsoli Chrome
Omn
4
Nie zadziała, jeśli masz komentarz zamykający */w swoim heredoc.
Narigo
2
Poleciłbym skorzystać z odpowiedzi Nate Ferrero, ponieważ jest to bardziej wyrafinowany i zoptymalizowany przykład. Mój używa 3 oddzielnych wywołań regEx i jest bardziej dowodem słuszności koncepcji.
Zv_oDD
1
Bardzo sprytne ... ale nie możesz zagwarantować, że zadziała w przyszłości. Jest to zbyt zależne od implementacji, aby było dobrą praktyką.
Pierre-Olivier Vares
33

Opierając się na odpowiedzi Zv_oDD, stworzyłem podobną funkcję dla łatwiejszego ponownego użycia.

Ostrzeżenie: jest to niestandardowa funkcja wielu interpreterów JS i prawdopodobnie zostanie w pewnym momencie usunięta, ale ponieważ buduję skrypt do użytku tylko w Chrome, używam go! Nie zawsze polegać na tym na stronach internetowych klientów od strony!

// Multiline Function String - Nate Ferrero - Public Domain
function heredoc(fn) {
  return fn.toString().match(/\/\*\s*([\s\S]*?)\s*\*\//m)[1];
};

Posługiwać się:

var txt = heredoc(function () {/*
A test of horrible
Multi-line strings!
*/});

Zwroty:

"A test of horrible
Multi-line strings!"

Uwagi:

  1. Tekst jest przycinany na obu końcach, więc wszelkie dodatkowe spacje na obu końcach są OK.

Edycje:

2/2/2014 - zmieniono, aby w ogóle nie zadzierać z prototypem funkcji i zamiast tego używać nazwy heredoc.

26.05.2017 - zaktualizowano białe znaki w celu odzwierciedlenia nowoczesnych standardów kodowania.

Nate Ferrero
źródło
1
Użyłbym hereDoc () jako nazwy mojej funkcji, ale ten kod działał dobrze, ładując mój zrzut dziennika 40
tys. Wierszy
Dlaczego miałbyś utworzyć wystąpienie funkcji i uzyskać dostęp do przestarzałej właściwości __ proto __? Dlaczego nie po prostu zrobić Function.prototype.str = function () {...}?
John Kurlak
@JohnKurlak, to jest jeszcze lepsze! Nie sądzę, żebym wiedział, że było to możliwe, kiedy pisałem odpowiedź.
Nate Ferrero
2
@NateFerrero - Świetna odpowiedź, dzięki! Dodałem własne rozszerzenie jako oddzielną odpowiedź.
Andrew Cheong,
Na moim Androidzie Nexus 4 w wersji 5.0.1 nie działa to już w Chrome. Z jakiegoś powodu usuwa spacje i komentarze. Nie mogę dowiedzieć się, czy to jest ustawienie, ale zdecydowanie jest to po stronie klienta. Jakieś pomysły na obejście?
MLU
19

W zależności od rodzaju używanego silnika JS / JS (SpiderMonkey, AS3) możesz po prostu napisać wbudowany XML, w którym możesz umieścić tekst w wielu wierszach, jak heredoc:

var xml = <xml>
    Here 
    is 
    some 
    multiline 
    text!
</xml>

console.log(xml.toXMLString())
console.log(xml.toString()) // just gets the content
Dave Stewart
źródło
13

ES6 Template Strings posiada funkcję heredoc.

Możesz zadeklarować łańcuchy ujęte w back-tick (``) i można je rozwinąć przez wiele linii.

var str = `This is my template string...
and is working across lines`;

Możesz również dołączyć wyrażenia do ciągów szablonów. Są one oznaczone znakiem dolara i nawiasami klamrowymi ( ${expression}).

var js = "Java Script";
var des = `Template strings can now be used in ${js} with lot of additional features`;

console.log(des); //"Template strings can now be used in Java Script with lot of additional features"

W rzeczywistości jest w nim więcej funkcji, takich jak Tagged Temple Strings i Raw Strings. Dokumentację można znaleźć pod adresem

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/template_strings

Charlie
źródło
8

Źle się czuję, pisząc osobną odpowiedź jako rozszerzenie odpowiedzi @ NateFerrero , ale nie uważam, że edycja jego odpowiedzi jest odpowiednia, więc proszę, zagłosuj na @NateFerrero, jeśli ta odpowiedź była dla Ciebie przydatna.

tl; dr - Dla tych, którzy chcą używać komentarzy blokowych w swoim heredoc ...

Potrzebowałem głównie heredoców Javascript do przechowywania bloku CSS, np

var css = heredoc(function() {/*
    /**
     * Nuke rounded corners.
     */
    body div {
        border-top-left-radius: 0 !important;
        border-top-right-radius: 0 !important;
        border-bottom-right-radius: 0 !important;
        border-bottom-left-radius: 0 !important;
    }
*/});

Jak widać jednak, lubię komentować mój CSS i niestety (jak sugeruje podświetlanie składni) pierwszy */kończy ogólny komentarz, łamiąc heredoc.


W tym konkretnym celu (CSS) moim obejściem było dodanie

.replace(/(\/\*[\s\S]*?\*) \//g, '$1/')

do łańcucha wewnątrz @ NateFerrero's heredoc; w pełnej formie:

function heredoc (f) {
    return f.toString().match(/\/\*\s*([\s\S]*?)\s*\*\//m)[1].replace(/(\/\*[\s\S]*?\*) \//g, '$1/');
};

i użyj go, dodając spację między *i /dla komentarzy do bloku „wewnętrznego”, na przykład:

var css = heredoc(function() {/*
    /**
     * Nuke rounded corners.
     * /
    body div {
        border-top-left-radius: 0 !important;
        border-top-right-radius: 0 !important;
        border-bottom-right-radius: 0 !important;
        border-bottom-left-radius: 0 !important;
    }
*/});

Po replaceprostu znajduje /* ... * /i usuwa przestrzeń do zrobienia /* ... */, zachowując w ten sposób heredoc do czasu wezwania.


Możesz oczywiście całkowicie usunąć komentarze za pomocą

.replace(/\/\*[\s\S]*?\* \//g, '')

Możesz również wspierać //komentarze, jeśli dodasz je do łańcucha:

.replace(/^\s*\/\/.*$/mg, '')

Możesz także zrobić coś innego niż pojedyncza spacja między *i /, na przykład -:

    /**
     * Nuke rounded corners.
     *-/

jeśli tylko odpowiednio zaktualizujesz wyrażenie regularne:

.replace(/(\/\*[\s\S]*?\*)-\//g, '$1/')
                          ^

A może wolisz dowolną ilość białych znaków zamiast pojedynczej spacji?

.replace(/(\/\*[\s\S]*?\*)\s+\//g, '$1/')
                          ^^^
Andrew Cheong
źródło
2
Chłodny! To było znane ograniczenie mojej metody, podoba mi się :)
Nate Ferrero
8

Możesz użyć CoffeeScript , języka, który kompiluje się do JavaScript. Kod kompiluje jeden do jednego do równoważnego kodu JS i nie ma interpretacji w czasie wykonywania.

I oczywiście ma heredoki :)

Jakob
źródło
24
Prawidłowa odpowiedź to nie. Jako sugestie można użyć CoffeeScript i EJS.
alvincrespo
4
CoffeeScript to poprawna odpowiedź na większość napotkanych problemów z JS. Jeśli napiszesz więcej niż trywialną ilość JS (i cenisz swój czas i energię), jesteś winien to sobie wykorzystać.
Brandon
5
Myślę, że to dobra odpowiedź, ponieważ zapewnia łatwy sposób obejścia braku heredoc w javascript. Zaoszczędził mi dużo czasu.
Stofke,
3
Jeśli chcesz tylko kawałek js, ale nie chcesz zajmować się pisaniem go: coffeescript.org i użyj przycisku „Wypróbuj Coffeescript”.
jcollum
1
To bardziej odpowiedź niż najwyżej oceniana, czyli po prostu… „nie”. Nienawidzę takich odpowiedzi.
Matt Fletcher,
7

ES5 i wcześniejsze wersje

(function(){/**
some random
multi line
text here
**/}).toString().slice(15,-5);

ES6 i nowsze wersje

`some random
multi line
text here`

wynik

some random
multi line
text here
Ga1der
źródło
najlepiej po prostu odpowiedz
Benjamin
1
Stary styl to niesamowicie brutalny hack: /
Shayne
1

Możesz użyć makr Sweet.js, aby dodać to w ten sposób, jak stworzył Tim Disney w tym poście

Zauważ, że to podejście zamiast tego używa odwrotnych apostrofów jako separatorów ciągów:

let str = macro {
    case {_ $template } => {
        var temp = #{$template}[0];
        var tempString = temp.token.value.raw;
        letstx $newTemp = [makeValue(tempString, #{here})];
        return #{$newTemp}
    }
}

str `foo bar baz`
Brad Parks
źródło
1

Jak powiedzieli inni, łańcuchy szablonów ES6 zapewniają większość tego, co zapewniają tradycyjne heredoki.

Jeśli chcesz pójść o krok dalej i użyć oznakowanego ciągu szablonu, theredocto fajna funkcja narzędzia, która pozwala ci to zrobić:

if (yourCodeIsIndented) {
  console.log(theredoc`
    Theredoc will strip the
    same amount of indentation
    from each line.

      You can still indent
      further if you want.

    It will also chop off the
    whitespace-only first and
    last lines.
  `)
}
Neall
źródło
0

Jeśli masz pod ręką trochę html i jQuery, a ciąg znaków jest prawidłowym kodem HTML, może to być przydatne:

<div id="heredoc"><!--heredoc content
with multiple lines, even 'quotes' or "double quotes",
beware not to leave any tag open--></div>
<script>
var str = (function() {
   var div = jQuery('#heredoc');
   var str = div.html();
   str = str.replace(/^<\!--/, "").toString();
   str = str.replace(/-->$/, "").toString();
   return str;
})();
</script>

Jeśli pomiędzy tekstem znajdują się komentarze „<! - ->”, to również działa, ale część tekstu może być widoczna. Oto skrzypce: https://jsfiddle.net/hr6ar152/1/

Max Oriola
źródło
0
// js heredoc - http://stackoverflow.com/a/32915549/466363
// a function with comment with eval-able string, use it just like regular string

function extractFuncCommentString(func,comments) {
  var matches = func.toString().match(/function\s*\(\)\s*\{\s*\/\*\!?\s*([\s\S]+?)\s*\*\/\s*\}/);
  if (!matches) return undefined;
  var str=matches[1];

   // i have made few flavors of comment removal add yours if you need something special, copy replacement lines from examples below, mix them
  if(comments===1 )
  {
   // keep comments, in order to keep comments  you need to convert /**/ to / * * / to be able to put them inside /**/ like /*    / * * /    */
   return (
    str
   .replace(/\/\s\*([\s\S]*?)\*\s\//g,"/*$1*/") //       change   / * text * /  to   /* text */ 
   )
  }
  else if(comments===2)
  {
   // keep comments and replace singleline comment to multiline comment
   return (
    str
   .replace(/\/\s\*([\s\S]*?)\*\s\//g,"/*$1*/") //       change   / * text * /  to   /* text */ 
   .replace(/\/\/(.*)/g,"/*$1*/")          //           change   //abc to  /*abc*/
   )
  }
  else if(comments===3)
  {
   // remove comments
   return (
      str
      .replace(/\/\s\*([\s\S]*?)\*\s\//g,"") //       match / * abc * /
      .replace(/\/\/(.*)/g,"")             // match //abc
     )
  }
  else if(comments===4)
  {
   // remove comments and trim and replace new lines with escape codes
   return (
      str
      .replace(/\/\s\*([\s\S]*?)\*\s\//g,"") //       match / * abc * /
      .replace(/\/\/(.*)/g,"")             // match //abc
      .trim() // after removing comments trim and:
      .replace(/\n/g,'\\n').replace(/\r/g,'\\r') // replace new lines with escape codes. allows further eval() of the string, you put in the comment function: a quoted text but with new lines
     )
  }
  else if(comments===5)
  {
   // keep comments comments and replace strings, might not suit when there are spaces or comments before and after quotes 
   // no comments allowed before quotes of the string
   return (
      str
      .replace(/\/\s\*([\s\S]*?)\*\s\//g,"/*$1*/") //       change   / * text * /  to   /* text */
      .replace(/\/\/(.*)/g,"/*$1*/")          //           change   //abc to  /*abc*/
      .trim() // trim space around quotes to not escape it and:
      .replace(/\n/g,'\\n').replace(/\r/g,'\\r') // replace new lines with escape codes. allows further eval() of the string, you put in the comment function: a quoted text but with new lines
     )
  }
  else 
  return str
}

przykład

var week=true,b=123;
var q = eval(extractFuncCommentString(function(){/*!

// this is a comment     


'select 

/ * this
is a multiline 
comment * /

 a
,b  // this is a comment  
,c
from `table`
where b='+b+' and monthweek="'+(week?'w':'m')+'" 
//+' where  a=124
order by a asc
'
*/},4));

z pamięcią podręczną: - stwórz prostą funkcję szablonu i zapisz funkcję: (drugi raz działa szybko)

var myfunction_sql1;
function myfunction(week,a){


    if(!myfunction_sql1) eval('myfunction_sql1=function(week,a){return ('+extractFuncCommentString(function(){/*!
'select 

/ * this
is a multiline 
comment * /

 a
,b  // this is a comment  
,c
from `table`
where b='+b+' and monthweek="'+(week?'w':'m')+'" 
//+' where  a=124
order by a asc

'*/},4)+')}');
    q=myfunction_sql1(week,a);
    console.log(q)
}
myfunction(true,1234)
Shimon Doodkin
źródło
1
Zupełnie inne wyniki w FF i Chrome.
Dave Newton
co za różnica? Właśnie przetestowałem w Chrome i FF i otrzymałem dokładnie takie same wyniki. Tyle że w Chrome nie ma nowych linii w konsoli Chrome, jeśli wpiszesz tylko nazwę var. ale zmienna jest taka sama. Możliwe jest drukowanie z nowymi wierszami za pomocą console.log ()
Shimon Doodkin
0

Publikuję tę wersję, ponieważ unika ona użycia wyrażenia regularnego do czegoś tak trywialnego.

IMHO regex to zaciemnienie, które zostało stworzone jako praktyczny żart wśród programistów perla. reszta społeczności potraktowała je poważnie i teraz zapłacimy za to cenę, dekady później. nie używaj wyrażenia regularnego, z wyjątkiem zgodności wstecznej ze starszym kodem. w dzisiejszych czasach nie ma wymówki, by pisać kod, który nie byłby od razu czytelny i zrozumiały dla człowieka. regex narusza tę zasadę na każdym poziomie.

Dodałem również sposób dodania wyniku do bieżącej strony, a nie to, o co proszono.

function pretty_css () {
/*
    pre { color: blue; }

*/
}
function css_src (css_fn) {
   var css = css_fn.toString();
   css = css.substr(css.indexOf("/*")+2);
   return css.substr(0,css.lastIndexOf("*/")).trim();
}

function addCss(rule) {
  let css = document.createElement('style');
  css.type = 'text/css';
  if (css.styleSheet) css.styleSheet.cssText = rule; // Support for IE
  else css.appendChild(document.createTextNode(rule)); // Support for the rest
  document.getElementsByTagName("head")[0].appendChild(css);
}

addCss(css_src(pretty_css));

document.querySelector("pre").innerHTML=css_src(pretty_css);
<pre></pre>

niezsynchronizowane
źródło