Javascript heredoc

109

Мне нужно что-то вроде heredoc в JavaScript. У вас есть идеи по этому поводу? Мне нужна кросс-браузерная функциональность.

Я нашел это:

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

Думаю, у меня это сработает. :)

ВероЛом
источник
6
Дубликат stackoverflow.com/questions/805107/…, в котором есть более подробные ответы.
Чедвик
4
Необходимость добавлять "\" каждый раз заставляет меня хмуриться. Имхо, отсутствие нормального синтаксиса для многострочных строк совершенно неоправданно. И они могли добавить его в любой данной версии, но они этого не сделали.
Lex
1
Возможный дубликат создания многострочных строк в JavaScript
brasofilo
В настоящее время это делается с помощью шаблонных литералов, как показано в возможном дубликате (здесь нет обновленного ответа).
brasofilo 06

Ответы:

77

Попробуйте шаблон строки ES6 , вы можете сделать что-то вроде

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


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

=> true

Вы можете использовать эту замечательную функцию сегодня с 6to5 или TypeScript.

mko
источник
2
Это применимо, если вам просто нужны многострочные строки. Однако, поскольку вы не можете изменить символ, который окружает вашу строку, на самом деле это не heredoc.
Peeyush Kushwaha
3
Отметим, что исходный вопрос был задан 5 лет назад до того, как ES6 стал доступен. Это лучшая практика для продвижения вперед, поскольку производительность также лучше.
Генри Ценг
2
@HenryTseng, так вы предлагаете, чтобы ответы на этот вопрос были адаптированы к древним технологиям, которые существовали 5 лет назад? Если вопрос все еще открыт, то стоит воспользоваться преимуществами новых технологий в том виде, в каком они были созданы с течением времени. Таким образом, новые пользователи, столкнувшиеся с такой же проблемой, смогут найти здесь «неархеологическую» информацию.
asiby
1
Нет, я комментировал, почему раньше это решение не было более заметным. Похоже, что поддерживаемый способ продвигается вперед, если нет проблем с совместимостью.
Генри Ценг
63

Нет, к сожалению, JavaScript не поддерживает ничего подобного heredoc.

Эндрю Хэйр
источник
12
Я знаю, но надеюсь найти heredoc hack :)
VeroLom 07
Что-то вроде комментариев функции синтаксического анализа (но это не работает в ie / firefox) =
VeroLom 07
37

Как насчет этого:

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());

Просто замените "/ * ЗДЕСЬ" и "ЗДЕСЬ * /" выбранным словом.

Zv_oDD
источник
4
все ли браузеры / движки возвращают комментарии в Function.toString ()? это очень умно
gcb
Работает в консоли Chrome
Omn
4
Не сработает, если */в вашем heredoc есть заключительный комментарий .
Нариго
2
Я бы порекомендовал использовать ответ Нейта Ферреро, так как это более изысканный и оптимизированный пример. Мой использует 3 отдельных вызова regEx и является скорее подтверждением концепции.
Zv_oDD
1
Очень умно ... но вы не можете гарантировать, что он будет работать в будущем. Это слишком зависит от реализации, чтобы быть хорошей практикой.
Пьер-Оливье Варес,
33

Основываясь на ответе Zv_oDD, я создал аналогичную функцию для упрощения повторного использования.

Предупреждение: это нестандартная функция многих интерпретаторов JS, и, вероятно, в какой-то момент она будет удалена, но поскольку я создаю сценарий, который будет использоваться только в Chrome, я использую его! Не когда - либо полагаться на это для сайтов клиентов-облицовочных!

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

Использование:

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

Возврат:

"A test of horrible
Multi-line strings!"

Ноты:

  1. Текст обрезается с обоих концов, поэтому любые лишние пробелы с обеих сторон допустимы.

Редактирование:

02.02.2014 - изменено, чтобы вообще не связываться с прототипом функции и использовать вместо него имя heredoc.

26.05.2017 - обновлены пробелы, чтобы отразить современные стандарты кодирования.

Нейт Ферреро
источник
1
Я бы использовал hereDoc () в качестве имени моей функции, но этот код отлично работал, загружая мой 40k-строчный дамп журнала в переменную в консоли Chrome
Omn
Зачем вам создавать экземпляр функции и получать доступ к устаревшему свойству __ proto __? Почему бы просто не сделать Function.prototype.str = function () {...}?
Джон Курлак
@JohnKurlak, это даже лучше! Не думаю, что я осознавал, что это возможно, когда писал ответ.
Нейт Ферреро
2
@NateFerrero - Отличный ответ, спасибо! Добавил собственное расширение как отдельный ответ.
Эндрю Чеонг,
На моем Android, nexus 4, работающем под управлением 5.0.1, это больше не работает в Chrome. По какой-то причине он удаляет пробелы и комментарии. Я не могу понять, является ли это настройкой, но она определенно на стороне клиента. Есть идеи обходного пути?
MLU
19

В зависимости от того, какой вид движка JS / JS вы используете (SpiderMonkey, AS3), вы можете просто написать встроенный XML, в котором вы можете разместить текст на нескольких строках, например heredoc:

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

console.log(xml.toXMLString())
console.log(xml.toString()) // just gets the content
Дэйв Стюарт
источник
13

ES6 Template Strings имеет функцию heredoc.

Вы можете объявлять строки, заключенные в обратную галочку (``), и расширять их до нескольких строк.

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

Вы также можете включать выражения в строки шаблона. Они обозначены знаком доллара и фигурными скобками ( ${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"

На самом деле в нем больше функций, таких как Tagged Temple Strings и Raw Strings. Пожалуйста, найдите документацию по адресу

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

Чарли
источник
8

Мне плохо писать отдельный ответ для простого расширения ответа @NateFerrero , но я не считаю, что редактирование его ответа также уместно, поэтому, пожалуйста, проголосуйте за @NateFerrero, если этот ответ был для вас полезен.

tl; dr - для тех, кто хочет использовать блочные комментарии внутри своего heredoc ...

В основном мне нужны heredocs Javascript для хранения блока CSS, например

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;
    }
*/});

Однако, как вы можете видеть, я люблю комментировать свой CSS, и, к сожалению (на что указывает выделение синтаксиса), первый */завершает общий комментарий, нарушая heredoc.


Для этой конкретной цели (CSS) я решил добавить

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

в цепочку внутри @ NateFerrero's heredoc; в полной форме:

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

и используйте его, добавив пробел между *и /для «внутренних» комментариев блока, например:

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;
    }
*/});

replaceПросто находит /* ... * /и удаляет пространство , чтобы сделать /* ... */, тем самым сохраняя Heredoc до востребования.


Конечно, вы можете полностью удалить комментарии, используя

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

Вы также можете поддержать //комментарии, если добавите их в цепочку:

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

Кроме того, вы можете сделать что-то другое, кроме одного пробела между *и /, например -:

    /**
     * Nuke rounded corners.
     *-/

если вы просто обновите регулярное выражение соответствующим образом:

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

Или, может быть, вам нужно произвольное количество пробелов вместо одного?

.replace(/(\/\*[\s\S]*?\*)\s+\//g, '$1/')
                          ^^^
Эндрю Чеонг
источник
2
Прохладно! Это было известное ограничение моего метода, мне он нравится :)
Nate Ferrero
8

Вы можете использовать CoffeeScript , язык, который компилируется до JavaScript. Код компилируется один к одному в эквивалентный JS, и во время выполнения нет интерпретации.

И, конечно же, есть heredocs :)

Jakob
источник
24
Правильный ответ - нет. CoffeeScript и EJS можно использовать в качестве предложений.
alvincrespo 09
4
CoffeeScript - правильный ответ на большинство проблем с JS, с которыми я сталкивался. Если вы пишете более чем тривиальный объем JS (и цените свое время и энергию), вы обязаны использовать их перед собой.
Брэндон
5
Я думаю, что это хороший ответ, поскольку он предоставляет простой способ обойти отсутствие heredoc в javascript. Сэкономил мне много времени.
Stofke
3
Если вам просто нужен кусок js, но вы не хотите заниматься его написанием: coffeescript.org и используйте кнопку «Попробовать Coffeescript».
jcollum
1
Это скорее ответ, чем ответ с наивысшей оценкой, который в основном просто ... «нет». Ненавижу такие ответы.
Мэтт Флетчер
7

ES5 и более ранние версии

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

ES6 и более поздние версии

`some random
multi line
text here`

результат

some random
multi line
text here
Ga1der
источник
лучший простой ответ
Бенджамин
1
Старый стиль - это невероятно жестокий взлом: /
Шейн
1

Вы можете использовать макрос Sweet.js, чтобы добавить его так, как это создал Тим Дисней в этом посте.

Обратите внимание, что в этом подходе вместо разделителей строк используются обратные кавычки:

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`
Брэд Паркс
источник
1

Как уже говорили другие, строки шаблонов ES6 предоставляют вам большую часть того, что предоставляют традиционные heredocs.

Если вы хотите пойти дальше и использовать строку шаблона с тегами, theredocэто хорошая служебная функция, которая позволяет вам сделать это:

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
источник
0

Если у вас под рукой есть html и jQuery, а строка является допустимым HTML, это может быть полезно:

<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>

Если между текстом есть комментарии «<! - ->», это тоже работает, но часть текста может быть видна. Вот скрипка: https://jsfiddle.net/hr6ar152/1/

Макс Ориола
источник
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
}

пример

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));

с кешем: - сделать простую функцию шаблона и сохранить функцию: (второй раз работает быстро)

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)
Шимон Дудкин
источник
1
Совсем разные результаты в FF и Chrome.
Дэйв Ньютон,
что отличается? Только что протестировал в Chrome и FF, и я получаю точно такие же результаты. За исключением того, что в Chrome нет новых строк в консоли Chrome, если вы просто набираете имя переменной. но переменная такая же. Можно печатать с символами новой строки с помощью console.log ()
Шимон Дудкин
0

Я публикую эту версию, так как она избегает использования регулярных выражений для чего-то столь тривиального.

Регулярное выражение IMHO - это обфускация, созданная разработчиками Perl в качестве розыгрыша. остальная часть сообщества восприняла их серьезно, и теперь мы платим цену, спустя десятилетия. не используйте регулярное выражение, за исключением обратной совместимости с устаревшим кодом. В наши дни нет оправдания написанию кода, который не сразу становится читаемым и понятным человеком. регулярное выражение нарушает этот принцип на всех уровнях.

Я также добавил способ добавить результат на текущую страницу, хотя этого не просили.

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>

синхронизирован
источник