Konversi string ke string template

147

Apakah mungkin membuat string template seperti string biasa

let a="b:${b}";

kemudian mengubahnya menjadi string template

let b=10;
console.log(a.template());//b:10

tanpa eval, new Functiondan cara lain untuk menghasilkan kode dinamis?

KOLANICH
sumber
5
Apakah Anda menemukan cara untuk mencapai ini? Saya mungkin perlu melakukannya suatu hari dan saya ingin tahu apa yang Anda tiba.
Bryan Rayner
@BryanRayner katakanlah program js Anda mencoba mengambil data dari API lainnya, yang urlnya ada dalam file config.js sebagai string "/ resources / <resource_id> / update /" dan Anda meletakkan "resource_id" secara dinamis dari program Anda . Kecuali jika Anda ingin membagi url itu menjadi beberapa bagian dan menyimpannya di area yang berbeda, Anda memerlukan semacam pemrosesan templat string.
Ryu_hayabusa
Alih-alih menggunakan eval lebih baik digunakan untuk regex Eval itu tidak dianjurkan & sangat berkecil hati, jadi jangan gunakan itu developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…! biarkan b = 10; biarkan a = "b: $ {b}"; biarkan response = a.replace (/ \ $ {\ w +} /, b); conssole.log (respons);
Vijay Palaskar

Jawaban:

79

Karena string template Anda harus mendapatkan referensi ke bvariabel secara dinamis (dalam runtime), maka jawabannya adalah: TIDAK, itu tidak mungkin dilakukan tanpa pembuatan kode dinamis.

Tetapi dengan evalitu cukup sederhana:

let tpl = eval('`'+a+'`');
alexpods
sumber
7
eval tidak aman, demikian juga cara lain pembuatan kode dinamis
KOLANICH
8
@KOLANICH Untuk tertentu bahwa kasus - kutipan melarikan diri kembali di atali dan itu akan jauh lebih sedikit tidak aman: let tpl = eval('`'+a.replace(/`/g,'\\`')+'`');. Saya pikir yang lebih penting adalah evalmencegah kompiler untuk mengoptimalkan kode Anda. Tapi saya pikir itu tidak relevan dengan pertanyaan ini.
alexpods
3
Bahkan, Anda juga dapat menjalankan fungsi di dalam string template.
KOLANICH
9
@ KOLANICH Maaf Anda tidak suka eval. Namun, ingat bahwa templat literal itu sendiri adalah bentuk eval. Dua contoh: var test = Result: ${alert('hello')}; var test = Result: ${b=4}; Keduanya akan menjalankan kode arbitrer dalam konteks skrip. Jika Anda ingin mengizinkan string yang sewenang-wenang, Anda dapat juga mengizinkannya eval.
Manngo
6
Hati-hati. Karena sesuatu seperti babel tidak akan mengubah ini, kode ini TIDAK akan berfungsi di IE
cgsd
79

Dalam proyek saya, saya telah membuat sesuatu seperti ini dengan ES6:

String.prototype.interpolate = function(params) {
  const names = Object.keys(params);
  const vals = Object.values(params);
  return new Function(...names, `return \`${this}\`;`)(...vals);
}

const template = 'Example text: ${text}';
const result = template.interpolate({
  text: 'Foo Boo'
});
console.log(result);

PEMBARUAN Saya telah menghapus ketergantungan lodash, ES6 memiliki metode yang setara untuk mendapatkan kunci dan nilai.

Mateusz Moska
sumber
1
Hai, Solusi Anda berfungsi dengan baik, tetapi ketika saya menggunakannya dalam React Native (mode build), ia melempar kesalahan: Karakter '' 'tidak valid , meskipun itu berfungsi ketika saya menjalankan dalam mode debug. Sepertinya, masalah babel, ada bantuan?
Mohit Pandey
@ MohitPandey Saya mendapatkan kesalahan yang sama ketika saya menjalankan tes kode ini di bawah PhantomJS dan melewati di bawah chrome. Jika itu masalahnya, saya pikir ada PhantomJS versi beta baru dalam perjalanan dengan dukungan yang lebih baik untuk ES6, Anda dapat mencoba menginstalnya.
Mateusz Moska
1
Sayangnya, itu tidak berfungsi dan saya menuliskan regex untuk hal yang sama. Ditambahkan sebagai jawaban juga.
Mohit Pandey
solusi ini hanya berfungsi jika karakter centang-belakang "` "tidak ada dalam string template
SliverNinja - MSFT
Ketika saya mencobanya saya mendapat ReferenceError: _ is not defined. Apakah kode itu bukan ES6 tetapi lodashspesifik, atau ...?
xpt
29

Apa yang Anda minta di sini:

//non working code quoted from the question
let b=10;
console.log(a.template());//b:10

persis sama (dalam hal kekuatan dan, eh, keamanan) untuk eval: kemampuan untuk mengambil string yang berisi kode dan menjalankan kode itu; dan juga kemampuan kode yang dieksekusi untuk melihat variabel lokal di lingkungan pemanggil.

Tidak ada cara di JS untuk fungsi untuk melihat variabel lokal di pemanggilnya, kecuali fungsi itu eval(). Bahkan Function()tidak bisa melakukannya.


Ketika Anda mendengar ada sesuatu yang disebut "string template" yang masuk ke JavaScript, wajar untuk menganggapnya sebagai pustaka template bawaan, seperti Moustache. Bukan itu. Ini terutama hanya interpolasi string dan string multiline untuk JS. Saya pikir ini akan menjadi kesalahpahaman umum untuk sementara waktu. :(

Jason Orendorff
sumber
2
TBH itulah yang saya pikir itu. Pasti sangat, sangat berguna.
Bryan Rayner
Apakah ini (masih) berfungsi? Saya mengerti template is not a function.
Ionică Bizău
2
Blok kode di bagian atas jawaban ini adalah kutipan dari pertanyaan. Tidak bekerja.
Jason Orendorff
27

Tidak, tidak ada cara untuk melakukan ini tanpa pembuatan kode dinamis.

Namun, saya telah membuat fungsi yang akan mengubah string biasa menjadi fungsi yang dapat disediakan dengan peta nilai, menggunakan string template secara internal.

Hasilkan Template String Gist

/**
 * Produces a function which uses template strings to do simple interpolation from objects.
 * 
 * Usage:
 *    var makeMeKing = generateTemplateString('${name} is now the king of ${country}!');
 * 
 *    console.log(makeMeKing({ name: 'Bryan', country: 'Scotland'}));
 *    // Logs 'Bryan is now the king of Scotland!'
 */
var generateTemplateString = (function(){
    var cache = {};

    function generateTemplate(template){
        var fn = cache[template];

        if (!fn){
            // Replace ${expressions} (etc) with ${map.expressions}.

            var sanitized = template
                .replace(/\$\{([\s]*[^;\s\{]+[\s]*)\}/g, function(_, match){
                    return `\$\{map.${match.trim()}\}`;
                    })
                // Afterwards, replace anything that's not ${map.expressions}' (etc) with a blank string.
                .replace(/(\$\{(?!map\.)[^}]+\})/g, '');

            fn = Function('map', `return \`${sanitized}\``);
        }

        return fn;
    }

    return generateTemplate;
})();

Pemakaian:

var kingMaker = generateTemplateString('${name} is king!');

console.log(kingMaker({name: 'Bryan'}));
// Logs 'Bryan is king!' to the console.

Semoga ini bisa membantu seseorang. Jika Anda menemukan masalah dengan kode, harap berbaik hati memperbarui Gist.

Bryan Rayner
sumber
Terima kasih! Saya menggunakan ini alih-alih solusi javascript sprintf.
seangwright
1
tidak berfungsi untuk setiap templat yang var test = generateTemplateString('/api/${param1}/${param2}/') console.log(test({param1: 'bar', param2: 'foo'}))dikembalikan/api/bar//
Guillaume Vincent
Terima kasih sudah diperbaiki. Regex termasuk satu kecocokan $ {param1} / $ {param2} padahal seharusnya dua kecocokan.
Bryan Rayner
Perhatikan bahwa yang ini tidak berfungsi di IE11, karena tidak ada dukungan untuk kutu belakang.
s.meijer
1
Tentu saja, jika string template tidak didukung oleh browser, metode ini tidak akan berfungsi. Jika Anda ingin menggunakan string template di browser yang tidak didukung, saya akan merekomendasikan menggunakan bahasa seperti TypeScript, atau transpiler seperti Babel; Itulah satu-satunya cara untuk memasukkan ES6 ke browser lama.
Bryan Rayner
9

TLDR: https://jsfiddle.net/w3jx07vt/

Semua orang tampaknya khawatir tentang mengakses variabel, mengapa tidak lewat saja? Saya yakin itu tidak akan terlalu sulit untuk mendapatkan konteks variabel dalam penelepon dan meneruskannya. Gunakan ini https://stackoverflow.com/a/6394168/6563504 untuk mendapatkan alat peraga dari obj. Saya tidak dapat menguji untuk Anda sekarang, tetapi ini harus bekerja.

function renderString(str,obj){
    return str.replace(/\$\{(.+?)\}/g,(match,p1)=>{return index(obj,p1)})
}

Diuji. Ini kode lengkapnya.

function index(obj,is,value) {
    if (typeof is == 'string')
        is=is.split('.');
    if (is.length==1 && value!==undefined)
        return obj[is[0]] = value;
    else if (is.length==0)
        return obj;
    else
        return index(obj[is[0]],is.slice(1), value);
}

function renderString(str,obj){
    return str.replace(/\$\{.+?\}/g,(match)=>{return index(obj,match)})
}

renderString('abc${a}asdas',{a:23,b:44}) //abc23asdas
renderString('abc${a.c}asdas',{a:{c:22,d:55},b:44}) //abc22asdas
M3D
sumber
@ s.meijer dapatkah Anda menguraikan? Saya berhasil menggunakan kode ini. jsfiddle.net/w3jx07vt
M3D
1
Regex yang lebih baik akan memungkinkan Anda untuk tidak memilih ${}karakter. Coba:/(?!\${)([^{}]*)(?=})/g
Eric Hodonsky
@Relic jsfiddle.net/w3jx07vt/2 Saya tidak bisa bekerja, peduli untuk mengulurkan tangan dan saya akan memperbarui posting saya? :)
M3D
Jadi cara Anda mencoba meraih ini sebenarnya tidak banyak membantu, saya malah melakukan ganti string. Alih-alih menambahkan langkah interpolasi, jadi saya bisa menggunakan string sebagai interp atau string. Tidak mewah, tetapi berhasil.
Eric Hodonsky
8

Masalahnya di sini adalah memiliki fungsi yang memiliki akses ke variabel pemanggilnya. Inilah mengapa kami melihat langsung evaldigunakan untuk pemrosesan template. Solusi yang mungkin adalah menghasilkan fungsi yang mengambil parameter formal yang dinamai oleh properti kamus, dan memanggilnya dengan nilai yang sesuai dalam urutan yang sama. Cara alternatif adalah memiliki sesuatu yang sederhana seperti ini:

var name = "John Smith";
var message = "Hello, my name is ${name}";
console.log(new Function('return `' + message + '`;')());

Dan bagi siapa pun yang menggunakan kompiler Babel kita perlu membuat closure yang mengingat lingkungan di mana ia dibuat:

console.log(new Function('name', 'return `' + message + '`;')(name));
didinko
sumber
Cuplikan pertama Anda sebenarnya lebih buruk daripada evalkarena hanya berfungsi dengan namevariabel global
Bergi
@Bergi Pernyataan Anda valid - ruang lingkup fungsi akan hilang. Saya ingin menghadirkan solusi mudah untuk masalah ini dan memberikan contoh sederhana tentang apa yang bisa dilakukan. Orang dapat dengan mudah menemukan yang berikut untuk mengatasi masalah: var template = function() { var name = "John Smith"; var message = "Hello, my name is ${name}"; this.local = new Function('return '+ message +';')();}
didinko
Tidak, itulah yang tidak akan berhasil - new Functiontidak memiliki akses ke var namedalam templatefungsi.
Bergi
Snip kedua memperbaiki masalah saya ... Pilih suara dari saya! Terima kasih, ini membantu menyelesaikan masalah sementara yang kami alami dengan perutean dinamis ke iframe :)
Kris Boyd
7

Ada banyak solusi bagus yang diposting di sini, tetapi belum ada yang menggunakan metode String.raw ES6 . Ini contriubution saya. Ini memiliki batasan penting karena hanya akan menerima properti dari objek yang diteruskan, artinya tidak ada eksekusi kode dalam templat yang akan berfungsi.

function parseStringTemplate(str, obj) {
    let parts = str.split(/\$\{(?!\d)[\wæøåÆØÅ]*\}/);
    let args = str.match(/[^{\}]+(?=})/g) || [];
    let parameters = args.map(argument => obj[argument] || (obj[argument] === undefined ? "" : obj[argument]));
    return String.raw({ raw: parts }, ...parameters);
}
let template = "Hello, ${name}! Are you ${age} years old?";
let values = { name: "John Doe", age: 18 };

parseStringTemplate(template, values);
// output: Hello, John Doe! Are you 18 years old?
  1. Pisahkan string menjadi bagian teks non-argumen. Lihat regex .
    parts: ["Hello, ", "! Are you ", " years old?"]
  2. Pisahkan string menjadi nama properti. Kosongkan array jika pertandingan gagal.
    args: ["name", "age"]
  3. Parameter peta dari objberdasarkan nama properti. Solusi dibatasi oleh pemetaan satu tingkat yang dangkal. Nilai yang tidak terdefinisi diganti dengan string kosong, tetapi nilai-nilai palsu lainnya diterima.
    parameters: ["John Doe", 18]
  4. Gunakan String.raw(...)dan kembalikan hasil.
pekaaw
sumber
Karena penasaran, apa nilai yang diberikan String.raw di sini? Tampaknya Anda melakukan semua pekerjaan untuk mengurai string dan melacak apa yang dimaksud dengan subtitle. Apakah ini jauh berbeda dari sekadar menelepon .replace()berulang kali?
Steve Bennett
Poin wajar, @SteveBennett. Saya punya beberapa masalah mengubah string normal menjadi string template, dan menemukan solusi dengan membangun sendiri objek mentah. Saya kira itu mengurangi String.raw ke metode penggabungan, tapi saya pikir itu bekerja dengan cukup baik. Saya ingin melihat solusi yang bagus dengan .replace(), :) Saya pikir keterbacaan penting, jadi saat menggunakan ekspresi reguler sendiri, saya mencoba memberi nama mereka untuk membantu memahami semuanya ...
pekaaw
6

Mirip dengan jawaban Daniel (dan inti s.meijer ) tetapi lebih mudah dibaca:

const regex = /\${[^{]+}/g;

export default function interpolate(template, variables, fallback) {
    return template.replace(regex, (match) => {
        const path = match.slice(2, -1).trim();
        return getObjPath(path, variables, fallback);
    });
}

//get the specified property or nested property of an object
function getObjPath(path, obj, fallback = '') {
    return path.split('.').reduce((res, key) => res[key] || fallback, obj);
}

Catatan: Ini sedikit meningkatkan asli s.meijer, karena tidak akan cocok dengan hal-hal seperti ${foo{bar}(regex hanya memungkinkan karakter penjepit non-keriting di dalam ${dan }).


UPDATE: Saya diminta untuk contoh menggunakan ini, jadi di sini Anda pergi:

const replacements = {
    name: 'Bob',
    age: 37
}

interpolate('My name is ${name}, and I am ${age}.', replacements)
Matt Browne
sumber
Bisakah Anda memposting contoh yang benar-benar menggunakan ini? Javascript ini sedikit di luar saya. Saya akan menyarankan regex /\$\{(.*?)(?!\$\{)\}/g(untuk menangani kurung kurawal). Saya punya solusi yang berfungsi, tetapi saya tidak yakin ini se portable milik Anda, jadi saya ingin melihat bagaimana ini harus diterapkan dalam satu halaman. Milik saya juga menggunakan eval().
Reguler Joe
Saya teruskan dan mengirim jawaban juga, dan saya ingin umpan balik Anda tentang cara menjadikannya lebih aman dan berpusat pada kinerja: stackoverflow.com/a/48294208 .
Reguler Joe
@ RegulerJoe saya menambahkan contoh. Tujuan saya adalah untuk membuatnya tetap sederhana, tetapi Anda benar bahwa jika Anda ingin menangani kurung kurawal bersarang, Anda perlu mengubah regex. Namun, saya tidak bisa memikirkan use case untuk itu ketika mengevaluasi string biasa seolah-olah itu adalah templat literal (seluruh tujuan fungsi ini). Apa yang ada dalam pikiranmu?
Matt Browne
Juga, saya bukan ahli kinerja atau ahli keamanan; jawaban saya sebenarnya hanya menggabungkan dua jawaban sebelumnya. Tetapi saya akan mengatakan bahwa menggunakan evalmembuat Anda jauh lebih terbuka terhadap kemungkinan kesalahan yang akan menyebabkan masalah keamanan, sedangkan semua versi saya lakukan adalah mencari properti pada objek dari jalur yang dipisahkan titik, yang seharusnya aman.
Matt Browne
5

Anda dapat menggunakan prototipe string, misalnya

String.prototype.toTemplate=function(){
    return eval('`'+this+'`');
}
//...
var a="b:${b}";
var b=10;
console.log(a.toTemplate());//b:10

Tetapi jawaban dari pertanyaan awal tidak mungkin.

sarkiroka
sumber
5

Saya menyukai jawaban s.meijer dan menulis versi saya sendiri berdasarkan pada:

function parseTemplate(template, map, fallback) {
    return template.replace(/\$\{[^}]+\}/g, (match) => 
        match
            .slice(2, -1)
            .trim()
            .split(".")
            .reduce(
                (searchObject, key) => searchObject[key] || fallback || match,
                map
            )
    );
}
Daniel
sumber
1
Rapi! Sangat Rapi!
xpt
4

Saya memerlukan metode ini dengan dukungan untuk Internet Explorer. Ternyata kutu belakang tidak didukung bahkan oleh IE11. Juga; menggunakan evalatau itu setara Functionrasanya tidak benar.

Untuk yang memperhatikan; Saya juga menggunakan backticks, tetapi ini dihapus oleh kompiler seperti babel. Metode yang disarankan oleh yang lain, bergantung padanya pada run-time. Seperti dikatakan di atas; ini merupakan masalah di IE11 dan lebih rendah.

Jadi inilah yang saya pikirkan:

function get(path, obj, fb = `$\{${path}}`) {
  return path.split('.').reduce((res, key) => res[key] || fb, obj);
}

function parseTpl(template, map, fallback) {
  return template.replace(/\$\{.+?}/g, (match) => {
    const path = match.substr(2, match.length - 3).trim();
    return get(path, map, fallback);
  });
}

Contoh output:

const data = { person: { name: 'John', age: 18 } };

parseTpl('Hi ${person.name} (${person.age})', data);
// output: Hi John (18)

parseTpl('Hello ${person.name} from ${person.city}', data);
// output: Hello John from ${person.city}

parseTpl('Hello ${person.name} from ${person.city}', data, '-');
// output: Hello John from -
s.meijer
sumber
"Menggunakan eval atau itu setara Fungsi tidak terasa benar." ... Ya ... Saya setuju, tapi saya pikir ini adalah salah satu dari sedikit kasus penggunaan di mana orang bisa mengatakan, "mmhkay, mari kita gunakan". Silakan periksa jsperf.com/es6-string-tmpl - ini adalah kasus penggunaan praktis saya. Menggunakan fungsi Anda (dengan regexp yang sama dengan saya) dan milik saya (eval + string literal). Terima kasih! :)
Andrea Puddu
@ AndreaPuddu, kinerja Anda memang lebih baik. Tapi sekali lagi; string template tidak didukung di IE. Jadi eval('`' + taggedURL + '`')tidak berhasil.
s.meijer
"Sepertinya" lebih baik saya katakan, karena itu diuji secara terpisah ... Satu-satunya tujuan dari tes itu adalah untuk melihat masalah kinerja potensial menggunakan eval. Mengenai literal templat: terima kasih telah menunjukkannya lagi. Saya menggunakan Babel untuk mengubah kode saya tetapi fungsi saya tetap tidak berfungsi apparently
Andrea Puddu
3

Saat ini saya tidak dapat mengomentari jawaban yang ada sehingga saya tidak dapat langsung mengomentari tanggapan luar biasa Bryan Raynor. Dengan demikian, respons ini akan memperbarui jawabannya dengan sedikit koreksi.

Singkatnya, fungsinya gagal untuk benar-benar men-cache fungsi yang dibuat, jadi itu akan selalu dibuat ulang, terlepas dari apakah itu melihat template sebelumnya. Berikut adalah kode yang diperbaiki:

    /**
     * Produces a function which uses template strings to do simple interpolation from objects.
     * 
     * Usage:
     *    var makeMeKing = generateTemplateString('${name} is now the king of ${country}!');
     * 
     *    console.log(makeMeKing({ name: 'Bryan', country: 'Scotland'}));
     *    // Logs 'Bryan is now the king of Scotland!'
     */
    var generateTemplateString = (function(){
        var cache = {};

        function generateTemplate(template){
            var fn = cache[template];

            if (!fn){
                // Replace ${expressions} (etc) with ${map.expressions}.

                var sanitized = template
                    .replace(/\$\{([\s]*[^;\s\{]+[\s]*)\}/g, function(_, match){
                        return `\$\{map.${match.trim()}\}`;
                    })
                    // Afterwards, replace anything that's not ${map.expressions}' (etc) with a blank string.
                    .replace(/(\$\{(?!map\.)[^}]+\})/g, '');

                fn = cache[template] = Function('map', `return \`${sanitized}\``);
            }

            return fn;
        };

        return generateTemplate;
    })();
pengguna2501097
sumber
3

@ Mosus Moska, solusi berfungsi dengan baik, tetapi ketika saya menggunakannya dalam React Native (build mode), ia melempar kesalahan: Karakter '' 'tidak valid , meskipun berfungsi ketika saya menjalankannya dalam mode debug.

Jadi saya menuliskan solusi saya sendiri menggunakan regex.

String.prototype.interpolate = function(params) {
  let template = this
  for (let key in params) {
    template = template.replace(new RegExp('\\$\\{' + key + '\\}', 'g'), params[key])
  }
  return template
}

const template = 'Example text: ${text}',
  result = template.interpolate({
    text: 'Foo Boo'
  })

console.log(result)

Demo: https://es6console.com/j31pqx1p/

CATATAN: Karena saya tidak tahu akar penyebab masalah, saya mengangkat tiket dalam repo asli-reaksi, https://github.com/facebook/react-native/issues/14107 , sehingga setelah itu mereka dapat memperbaiki / membimbing saya tentang hal yang sama :)

Mohit Pandey
sumber
ini mendukung template yang berisi karakter back-tick. Namun, daripada mencoba menciptakan pola templating, Anda mungkin lebih baik hanya menggunakan kumis atau sejenisnya . tergantung pada seberapa kompleks template Anda, ini adalah pendekatan brute force yang tidak mempertimbangkan kasus tepi - kuncinya dapat berisi pola regex khusus.
SliverNinja - MSFT
2

Masih dinamis tetapi tampaknya lebih terkontrol daripada hanya menggunakan eval telanjang:

const vm = require('vm')
const moment = require('moment')


let template = '### ${context.hours_worked[0].value} \n Hours worked \n #### ${Math.abs(context.hours_worked_avg_diff[0].value)}% ${fns.gt0(context.hours_worked_avg_diff[0].value, "more", "less")} than usual on ${fns.getDOW(new Date())}'
let context = {
  hours_worked:[{value:10}],
  hours_worked_avg_diff:[{value:10}],

}


function getDOW(now) {
  return moment(now).locale('es').format('dddd')
}

function gt0(_in, tVal, fVal) {
  return _in >0 ? tVal: fVal
}



function templateIt(context, template) {
  const script = new vm.Script('`'+template+'`')
  return script.runInNewContext({context, fns:{getDOW, gt0 }})
}

console.log(templateIt(context, template))

https://repl.it/IdVt/3

Robert Moskal
sumber
1

Solusi ini bekerja tanpa ES6:

function render(template, opts) {
  return new Function(
    'return new Function (' + Object.keys(opts).reduce((args, arg) => args += '\'' + arg + '\',', '') + '\'return `' + template.replace(/(^|[^\\])'/g, '$1\\\'') + '`;\'' +
    ').apply(null, ' + JSON.stringify(Object.keys(opts).reduce((vals, key) => vals.push(opts[key]) && vals, [])) + ');'
  )();
}

render("hello ${ name }", {name:'mo'}); // "hello mo"

Catatan: Functionkonstruktor selalu dibuat dalam lingkup global, yang berpotensi menyebabkan variabel global ditimpa oleh templat, misalnyarender("hello ${ someGlobalVar = 'some new value' }", {name:'mo'});

cruzanmo
sumber
0

Karena kita menciptakan kembali roda pada sesuatu yang akan menjadi fitur yang indah di javascript.

Saya menggunakan eval(), yang tidak aman, tetapi javascript tidak aman. Saya siap mengakui bahwa saya tidak hebat dengan javascript, tapi saya punya kebutuhan, dan saya butuh jawaban jadi saya membuatnya.

Saya memilih untuk menyesuaikan dgn mode variabel saya dengan @, daripada karena $, terutama karena saya ingin menggunakan fitur multiline dari literal tanpa mengevaluasi sampai siap. Jadi sintaks variabel adalah@{OptionalObject.OptionalObjectN.VARIABLE_NAME}

Saya bukan ahli javascript, jadi saya dengan senang hati menerima saran tentang peningkatan tetapi ...

var prsLiteral, prsRegex = /\@\{(.*?)(?!\@\{)\}/g
for(i = 0; i < myResultSet.length; i++) {
    prsLiteral = rt.replace(prsRegex,function (match,varname) {
        return eval(varname + "[" + i + "]");
        // you could instead use return eval(varname) if you're not looping.
    })
    console.log(prsLiteral);
}

Implementasi yang sangat sederhana berikut

myResultSet = {totalrecords: 2,
Name: ["Bob", "Stephanie"],
Age: [37,22]};

rt = `My name is @{myResultSet.Name}, and I am @{myResultSet.Age}.`

var prsLiteral, prsRegex = /\@\{(.*?)(?!\@\{)\}/g
for(i = 0; i < myResultSet.totalrecords; i++) {
    prsLiteral = rt.replace(prsRegex,function (match,varname) {
        return eval(varname + "[" + i + "]");
        // you could instead use return eval(varname) if you're not looping.
    })
    console.log(prsLiteral);
}

Dalam implementasi saya yang sebenarnya, saya memilih untuk menggunakan @{{variable}}. Satu set kawat gigi lagi. Sangat tidak mungkin untuk menemukan itu secara tak terduga. Regex untuk itu akan terlihat seperti/\@\{\{(.*?)(?!\@\{\{)\}\}/g

Untuk membuatnya lebih mudah dibaca

\@\{\{    # opening sequence, @{{ literally.
(.*?)     # capturing the variable name
          # ^ captures only until it reaches the closing sequence
(?!       # negative lookahead, making sure the following
          # ^ pattern is not found ahead of the current character
  \@\{\{  # same as opening sequence, if you change that, change this
)
\}\}      # closing sequence.

Jika Anda tidak berpengalaman dengan regex, aturan yang cukup aman adalah melarikan diri dari setiap karakter non-alfanumerik, dan jangan pernah melarikan diri huruf secara sia-sia karena banyak huruf yang lolos memiliki makna khusus untuk hampir semua rasa regex.

Joe biasa
sumber
0

Anda harus mencoba modul JS kecil ini, oleh Andrea Giammarchi, dari github: https://github.com/WebReflection/backtick-template

/*! (C) 2017 Andrea Giammarchi - MIT Style License */
function template(fn, $str, $object) {'use strict';
  var
    stringify = JSON.stringify,
    hasTransformer = typeof fn === 'function',
    str = hasTransformer ? $str : fn,
    object = hasTransformer ? $object : $str,
    i = 0, length = str.length,
    strings = i < length ? [] : ['""'],
    values = hasTransformer ? [] : strings,
    open, close, counter
  ;
  while (i < length) {
    open = str.indexOf('${', i);
    if (-1 < open) {
      strings.push(stringify(str.slice(i, open)));
      open += 2;
      close = open;
      counter = 1;
      while (close < length) {
        switch (str.charAt(close++)) {
          case '}': counter -= 1; break;
          case '{': counter += 1; break;
        }
        if (counter < 1) {
          values.push('(' + str.slice(open, close - 1) + ')');
          break;
        }
      }
      i = close;
    } else {
      strings.push(stringify(str.slice(i)));
      i = length;
    }
  }
  if (hasTransformer) {
    str = 'function' + (Math.random() * 1e5 | 0);
    if (strings.length === values.length) strings.push('""');
    strings = [
      str,
      'with(this)return ' + str + '([' + strings + ']' + (
        values.length ? (',' + values.join(',')) : ''
      ) + ')'
    ];
  } else {
    strings = ['with(this)return ' + strings.join('+')];
  }
  return Function.apply(null, strings).apply(
    object,
    hasTransformer ? [fn] : []
  );
}

template.asMethod = function (fn, object) {'use strict';
  return typeof fn === 'function' ?
    template(fn, this, object) :
    template(this, fn);
};

Demo (semua tes berikut menghasilkan true):

const info = 'template';
// just string
`some ${info}` === template('some ${info}', {info});

// passing through a transformer
transform `some ${info}` === template(transform, 'some ${info}', {info});

// using it as String method
String.prototype.template = template.asMethod;

`some ${info}` === 'some ${info}'.template({info});

transform `some ${info}` === 'some ${info}'.template(transform, {info});
colxi
sumber
0

Saya membuat solusi saya sendiri melakukan suatu jenis dengan deskripsi sebagai fungsi

export class Foo {
...
description?: Object;
...
}

let myFoo:Foo = {
...
  description: (a,b) => `Welcome ${a}, glad to see you like the ${b} section`.
...
}

dan melakukan:

let myDescription = myFoo.description('Bar', 'bar');
cesarmarinhorj
sumber
0

Alih-alih menggunakan eval lebih baik gunakan regex

Eval itu tidak direkomendasikan & sangat tidak disarankan, jadi tolong jangan menggunakannya ( mdn eval ).

 let b = 10;
 let a="b:${b}";

let response = a.replace(/\${\w+}/ ,b);
conssole.log(response);
Vijay Palaskar
sumber
berfungsi untuk satu, bagaimana kalau saya memiliki "a is $ {a}, b is {b} ..."?
leachim