Có thể thay thế các ký tự mẫu của ES6 trong thời gian chạy (hoặc sử dụng lại) không?


129

tl; dr: Có thể tạo mẫu có thể tái sử dụng theo nghĩa đen không?

Tôi đã cố gắng sử dụng các ký tự mẫu nhưng tôi đoán là tôi không hiểu và bây giờ tôi đang thất vọng. Ý tôi là, tôi nghĩ rằng tôi hiểu được nó, nhưng "nó" không nên là cách nó hoạt động, hay nó sẽ nhận được như thế nào. Nó sẽ khác đi.

Tất cả các ví dụ tôi thấy (ngay cả các mẫu được gắn thẻ) yêu cầu "thay thế" được thực hiện tại thời điểm khai báo chứ không phải thời gian chạy, điều này dường như hoàn toàn vô dụng đối với tôi đối với một mẫu. Có lẽ tôi bị điên, nhưng "mẫu" đối với tôi là một tài liệu có chứa các mã thông báo được thay thế khi bạn sử dụng nó, không phải khi bạn tạo nó, nếu không nó chỉ là một tài liệu (tức là một chuỗi). Một mẫu được lưu trữ với các mã thông báo dưới dạng mã thông báo & các mã thông báo đó được đánh giá khi bạn ... đánh giá nó.

Mọi người đều trích dẫn một ví dụ khủng khiếp tương tự như:

var a = 'asd';
return `Worthless ${a}!`

Điều đó thật tuyệt, nhưng nếu tôi đã biết a, tôi sẽ chỉ return 'Worthless asd'hoặc return 'Worthless '+a. Vấn đề ở đây là gì? Nghiêm túc. Được rồi, vấn đề là sự lười biếng; ít điểm cộng hơn, dễ đọc hơn. Tuyệt quá. Nhưng đó không phải là một khuôn mẫu! Không phải IMHO. Và MHO là tất cả những gì quan trọng! Vấn đề, IMHO, là mẫu được đánh giá khi nó được khai báo, vì vậy, nếu bạn làm vậy, IMHO:

var tpl = `My ${expletive} template`;
function go() { return tpl; }
go(); // SPACE-TIME ENDS!

expletivekhông được khai báo, nó xuất ra một cái gì đó giống như My undefined template. Siêu. Trên thực tế, ít nhất trong Chrome, tôi thậm chí không thể khai báo mẫu; nó ném ra một lỗi vì expletivekhông được xác định. Những gì tôi cần là có thể thực hiện thay thế sau khi khai báo mẫu:

var tpl = `My ${expletive} template`;
function go() { return tpl; }
var expletive = 'great';
go(); // My great template

Tuy nhiên, tôi không thấy làm thế nào điều này có thể thực hiện được, vì đây không thực sự là các mẫu. Ngay cả khi bạn nói tôi nên sử dụng thẻ, nope, chúng không hoạt động:

> explete = function(a,b) { console.log(a); console.log(b); }
< function (a,b) { console.log(a); console.log(b); }
> var tpl = explete`My ${expletive} template`
< VM2323:2 Uncaught ReferenceError: expletive is not defined...

Tất cả những điều này đã khiến tôi tin rằng các nghĩa đen của khuôn mẫu được đặt tên sai một cách khủng khiếp và nên được gọi như chúng thực sự là: heredocs . Tôi đoán phần "nghĩa đen" lẽ ra phải cho tôi biết (như trong, bất biến)?

Tui bỏ lỡ điều gì vậy? Có cách nào (tốt) để tạo một mẫu có thể tái sử dụng theo nghĩa đen không?


Tôi cung cấp cho bạn, các ký tự mẫu có thể tái sử dụng :

> function out(t) { console.log(eval(t)); }
  var template = `\`This is
  my \${expletive} reusable
  template!\``;
  out(template);
  var expletive = 'curious';
  out(template);
  var expletive = 'AMAZING';
  out(template);
< This is
  my undefined reusable
  template!
  This is
  my curious reusable
  template!
  This is
  my AMAZING reusable
  template!

Và đây là một chức năng "helper" ngây thơ ...

function t(t) { return '`'+t.replace('{','${')+'`'; }
var template = t(`This is
my {expletive} reusable
template!`);

...Để làm cho nó tốt hơn".

Tôi có xu hướng gọi chúng là guteral mẫu vì khu vực mà chúng tạo ra cảm giác uốn lượn.


1
Nó hỗ trợ gạch ngang (nhưng không hỗ trợ trong các nhận xét như thế này). Đặt văn bản của bạn vào một <strike>thẻ.
Pointy

Các ký tự mẫu ES6 chủ yếu dành cho nội suy Chuỗi kiểu cũ. Nếu bạn muốn các mẫu động, hãy sử dụng Handlebars, v.v. hoặc giải pháp mẫu được gắn thẻ của Pointy.
joews

1
Chuỗi mẫu, mặc dù có tên, không có mẫu . Xem thêm Trì hoãn thực thi cho Chuỗi mẫu
ES6

8
Bạn có thể vui lòng làm cho bài viết của bạn bớt tồi tệ hơn một chút không? Ngoài ra, có vẻ như bạn dự định viết một hướng dẫn ở định dạng Hỏi & Đáp, nếu bạn đã làm như vậy, vui lòng xóa phần " Tôi cung cấp cho bạn… " khỏi câu hỏi của bạn và đăng nó dưới dạng câu trả lời .
Bergi

Tôi nhận thấy có nhiều câu trả lời hay ở đây. Có lẽ chấp nhận một.
abalter

Câu trả lời:


86

Để làm cho các ký tự này hoạt động giống như các công cụ mẫu khác, cần phải có một biểu mẫu trung gian.

Cách tốt nhất để làm điều này là sử dụng hàm Functiontạo.

const templateString = "Hello ${this.name}!";
const templateVars = {
    name: "world"    
}

const fillTemplate = function(templateString, templateVars){
    return new Function("return `"+templateString +"`;").call(templateVars);
}

console.log(fillTemplate(templateString, templateVars));

Như với các công cụ mẫu khác, bạn có thể lấy chuỗi đó từ những nơi khác như một tệp.

Có thể có một số vấn đề khi sử dụng phương pháp này như thẻ mẫu khó sử dụng, nhưng chúng có thể được thêm vào nếu bạn khéo léo. Bạn cũng không thể có logic JavaScript nội tuyến vì nội suy muộn. Điều này cũng có thể được khắc phục bằng một số suy nghĩ.


8
Đẹp! Bạn thậm chí có thể sử dụngnew Function(`return \`${template}\`;`)
Ruben Stolk

Và các mẫu này có thể được tạo hoặc được "bao gồm" thông qua các đối số bằng cách gọi một phương thức hoặc chuyển vào kết quả đã biên dịch của một mẫu khác.
Quentin Engles

Quentin 'không có thẻ mẫu' nghĩa là gì? Cảm ơn!
mikemaccana

10
Hãy cẩn thận rằng chuỗi mẫu này được 'ẩn' để chuyển đổi (tức là gói web) và do đó sẽ KHÔNG chuyển tải thành một thứ gì đó đủ tương thích (tức là IE11) ở phía máy khách ...!
Frank Nocke

9
Lỗ hổng XSS ? Thông tin chi tiết trong JSFIDDLE NÀY
Kamil Kiełczewski

65

Bạn có thể đặt một chuỗi mẫu trong một hàm:

function reusable(a, b) {
  return `a is ${a} and b is ${b}`;
}

Bạn có thể làm điều tương tự với một mẫu được gắn thẻ:

function reusable(strings) {
  return function(... vals) {
    return strings.map(function(s, i) {
      return `${s}${vals[i] || ""}`;
    }).join("");
  };
}

var tagged = reusable`a is ${0} and b is ${1}`; // dummy "parameters"
console.log(tagged("hello", "world"));
// prints "a is hello b is world"
console.log(tagged("mars", "jupiter"));
// prints "a is mars b is jupiter"

Ý tưởng là để trình phân tích cú pháp mẫu tách ra các chuỗi không đổi từ biến "vị trí", sau đó trả về một hàm vá tất cả lại với nhau dựa trên một bộ giá trị mới mỗi lần.


3
@FelixKling đó có thể là; Tôi sẽ kiểm tra và sửa chữa nó nếu có. chỉnh sửa có nó trông giống như tôi rời ra một phần quan trọng trong ví dụ này, đó là "tái sử dụng" chức năng :)
chóp

@FelixKling Tôi không biết phải làm gì vì tôi hoàn toàn không thể nhớ lại những gì mình đã nghĩ lúc đó!
Pointy

1
Vâng, nó không có ý nghĩa gì cả tbh;) Bạn luôn có thể loại bỏ nó .... nhưng reusablecó thể được triển khai để nó trả về một hàm và bạn sẽ sử dụng ${0}${1}bên trong chữ thay vì ${a}${b}. Sau đó, bạn có thể sử dụng các giá trị đó để tham chiếu đến các đối số của hàm, tương tự như những gì Bergi làm trong ví dụ cuối cùng của anh ấy: stackoverflow.com/a/22619256/218196 (hoặc tôi đoán về cơ bản nó giống nhau).
Felix Kling

1
@FelixKling OK Tôi nghĩ rằng tôi đã nghĩ ra một cái gì đó ít nhất là mơ hồ dọc theo dòng của OP.
Pointy

3
Các mẫu được gắn thẻ có thể thực sự mạnh mẽ nếu kết quả thực sự không phải là một chuỗi. Ví dụ: trong một dự án của tôi, tôi sử dụng nó để thực hiện nội suy nút AST. Ví dụ: người ta có thể làm expression`a + ${node}`để xây dựng một nút BinaryExpression với một nút AST hiện có node. Bên trong, chúng tôi chèn trình giữ chỗ để tạo mã hợp lệ, phân tích cú pháp nó thành AST và thay thế trình giữ chỗ bằng giá trị được chuyển vào.
Felix Kling

45

Có lẽ cách tốt nhất để làm điều này là với các hàm mũi tên (vì tại thời điểm này, chúng tôi đang sử dụng ES6)

var reusable = () => `This ${object} was created by ${creator}`;

var object = "template string", creator = "a function";
console.log (reusable()); // "This template string was created by a function"

object = "example", creator = "me";
console.log (reusable()); // "This example was created by me"

... Và đối với các chữ mẫu được gắn thẻ:

reusable = () => myTag`The ${noun} go ${verb} and `;

var noun = "wheels on the bus", verb = "round";
var myTag = function (strings, noun, verb) {
    return strings[0] + noun + strings[1] + verb + strings[2] + verb;
};
console.log (reusable()); // "The wheels on the bus go round and round"

noun = "racecars", verb = "fast";
myTag = function (strings, noun, verb) {
    return strings[0] + noun + strings[1] + verb;
};
console.log (reusable()); // "The racecars go fast"

Điều này cũng tránh việc sử dụng eval()hoặc Function()có thể gây ra sự cố với trình biên dịch và gây chậm nhiều.


Tôi nghĩ đây là một trong những cách tốt nhất vì bạn có thể đưa một số mã vào bên trong hàm myTagđể thực hiện một số công việc. Ví dụ: sử dụng các tham số đầu vào làm khóa để lưu đầu ra vào bộ nhớ cache.
WOW

Tôi nghĩ đây là câu trả lời tốt nhất. Bạn cũng có thể thêm các thông số để vào mũi tên chức năng mà tôi nghĩ làm cho nó thậm chí còn sạch hơn: var reusable = (value: string) => `Value is ${value}`.
haggisandchips

13

Câu trả lời năm 2019 :

Lưu ý : Thư viện ban đầu mong đợi người dùng làm sạch các chuỗi để tránh XSS. Phiên bản 2 của thư viện không còn yêu cầu các chuỗi người dùng phải được làm sạch (điều mà các nhà phát triển web nên làm) vì nó tránheval hoàn toàn.

Các es6-dynamic-templatemô-đun trên NPM thực hiện điều này.

const fillTemplate = require('es6-dynamic-template');

Không giống như các câu trả lời hiện tại:

  • Nó sử dụng chuỗi mẫu ES6, không phải là một định dạng tương tự. Cập nhật phiên bản 2 sử dụng định dạng tương tự, thay vì chuỗi mẫu ES6, để ngăn người dùng sử dụng Chuỗi đầu vào không được bảo vệ.
  • Nó không cần this trong chuỗi mẫu
  • Bạn có thể chỉ định chuỗi mẫu và các biến trong một hàm duy nhất
  • Đó là một mô-đun được duy trì, có thể cập nhật, thay vì copypasta từ StackOverflow

Cách sử dụng rất đơn giản. Sử dụng dấu nháy đơn vì chuỗi mẫu sẽ được giải quyết sau!

const greeting = fillTemplate('Hi ${firstName}', {firstName: 'Joe'});

Nếu bạn đang sử dụng tính năng này với React Native, nó sẽ bị phá vỡ đặc biệt trên Android. Runtime nút Android không hỗ trợ các mẫu năng động, những người duy nhất điền sẵn
Oliver Dixon

1
Đây là một giải pháp tôi sử dụng trong các dự án cá nhân của mình và nó hoạt động hoàn hảo. Tôi thực sự nghĩ rằng đó là một ý tưởng tồi nếu sử dụng quá nhiều thư viện, đặc biệt là cho các tiện ích nhỏ như thế này.
Oliver Dixon

1
Lỗ hổng XSS ? Chi tiết trong FIDDLE NÀY
Kamil Kiełczewski

1
@kamil Chỉ XSS nếu bạn a) cung cấp cho người dùng khả năng tạo b) không khử trùng các chuỗi đầu vào. Tôi sẽ thêm một cảnh báo rằng mọi người nên khử trùng đầu vào của người dùng.
mikemaccana

1
Điều này không sử dụng từ xa các ký tự mẫu es6. Hãy thử 10 * 20 = ${10 * 20}để nó có thể là một định dạng tương tự nhưng nó không phải là thậm chí từ xa es6 mẫu literals
GMAN

12

Có, bạn có thể làm điều đó bằng cách phân tích chuỗi của bạn với mẫu dưới dạng JS bởi Function(hoặc eval) - nhưng điều này không được khuyến nghị và cho phép tấn công XSS

Thay vào đó, bạn có thể chèn các trường đối tượng một cách an toàn vào objmẫu strtheo cách động như sau

let inject = (str, obj) => str.replace(/\${(.*?)}/g, (x,g)=> obj[g]);


Đây là phương pháp tôi sử dụng và nó hoạt động tốt. Ví dụ tốt! Không? sau khi * trong RegEx trợ giúp mặc dù? Tôi không phải là chuyên gia RegEx, nhưng tôi đoán vì * có nghĩa là 0 hoặc nhiều hơn (và bạn muốn "nhiều hơn" trong trường hợp này), nên không cần hạn chế tham lam?
Sáng 1-1,

@ Gen1-1 .*?có nghĩa là không tham lam - nếu bạn xóa "?"thì đoạn mã sẽ cho kết quả sai
Kamil Kiełczewski

Bạn đúng, sai lầm của tôi. Tôi không sử dụng $ trong các mẫu của mình và sử dụng RegEx: / {(\ w *)} / g vì tôi không bao giờ có khoảng trắng trong thẻ, nhưng. *? cũng hoạt động. Tôi sử dụng:function taggedTemplate(template, data, matcher) { if (!template || !data) { return template; } matcher = matcher || /{(\w*)}/g; // {one or more alphanumeric characters with no spaces} return template.replace(matcher, function (match, key) { var value; try { value = data[key] } catch (e) { // } return value || ""; }); }
Gen 1-1

@ Gen1-1 cũng có dữ liệu lồng nhau được không? thích data = { a: 1, b: { c:2, d:3 } }-> b.c?
muescha

1
@muescha Bạn sẽ thay đổi dòng: value = data [key], để sử dụng đệ quy và tìm kiếm toàn bộ đối tượng dữ liệu cũng như các đối tượng lồng nhau cho đến khi bạn tìm thấy thuộc tính. Ví dụ: codereview.stackexchange.com/questions/73714/…mikedoesweb.com/2016/es6-depth-first-object-tree-search
Gen1-1

9

Đơn giản hóa câu trả lời do @metamorphasi cung cấp;

const fillTemplate = function(templateString, templateVars){
  var func = new Function(...Object.keys(templateVars),  "return `"+templateString +"`;")
  return func(...Object.values(templateVars));
}

// Sample
var hosting = "overview/id/d:${Id}";
var domain = {Id:1234, User:22};
var result = fillTemplate(hosting, domain);

console.log(result);


Mã này tự giải thích nhiều hơn là câu trả lời hàng đầu. Đã nhận được phiếu bầu của tôi :)
ymz

Điều này sẽ cho phép bạn sử dụng các biến hoặc tệp bên ngoài (trong NodeJS) làm mẫu hoặc tự động xây dựng chúng tại thời điểm chạy. Nếu không sử dụng eval.
b01

Lỗ hổng XSS ? Làm khó với mã độc (biến var hosting) TẠI ĐÂY .
Kamil Kiełczewski

7

Nếu bạn không muốn sử dụng các thông số yêu cầu hoặc bối cảnh / namespace để tham khảo các biến trong mẫu của bạn, ví dụ ${0}, ${this.something}hoặc ${data.something}, bạn có thể có một hàm mẫu chăm sóc của Phạm vi cho bạn.

Ví dụ về cách bạn có thể gọi một mẫu như vậy:

const tempGreet = Template(() => `
  <span>Hello, ${name}!</span>
`);
tempGreet({name: 'Brian'}); // returns "<span>Hello, Brian!</span>"

Chức năng Mẫu:

function Template(cb) {
  return function(data) {
    const dataKeys = [];
    const dataVals = [];
    for (let key in data) {
      dataKeys.push(key);
      dataVals.push(data[key]);
    }
    let func = new Function(...dataKeys, 'return (' + cb + ')();');
    return func(...dataVals);
  }
}

Điều khó hiểu trong trường hợp này là bạn chỉ cần truyền một hàm (trong ví dụ tôi đã sử dụng hàm mũi tên) trả về mẫu ES6 theo nghĩa đen. Tôi nghĩ rằng đó là một sự đánh đổi nhỏ để có được loại nội suy có thể tái sử dụng mà chúng ta đang theo đuổi.

Đây là trên GitHub: https://github.com/Adelphos/ES6-Reusable-Template


3
Đây là tốt, nhưng việc rút gọn (Vals, func, vv) là unnecessaery, 'cb' không phải là một callback (đây là mã hoàn toàn đồng bộ), và bạn chỉ có thể sử dụng Object.values()Object.keys()
mikemaccana

3

Câu trả lời ngắn gọn là chỉ sử dụng _.template trong lodash

// Use the ES template literal delimiter as an "interpolate" delimiter.
// Disable support by replacing the "interpolate" delimiter.
var compiled = _.template('hello ${ user }!');
compiled({ 'user': 'pebbles' });
// => 'hello pebbles!'

3

Tui bỏ lỡ điều gì vậy? Có cách nào [tốt] để tạo một mẫu có thể tái sử dụng theo nghĩa đen không?

Có lẽ tôi là thiếu một cái gì đó, bởi vì giải pháp của tôi cho vấn đề này dường như quá rõ ràng đối với tôi đến mức tôi rất ngạc nhiên là không ai viết điều đó trong một câu hỏi cũ như vậy.

Tôi có gần như một lớp lót cho nó:

function defer([first, ...rest]) {
  return (...values) => rest.reduce((acc, str, i) => acc + values[i] + str, first);
}

Đó là tất cả. Khi tôi muốn sử dụng lại một mẫu và trì hoãn việc giải quyết các thay thế, tôi chỉ cần làm:

> t = defer`My template is: ${null} and ${null}`;
> t('simple', 'reusable');          // 'My template is: simple and reusable'
> t('obvious', 'late to the party'; // 'My template is: obvious and late to the party'
> t(null);                          // 'My template is: null and undefined'
>
> defer`Choose: ${'ignore'} / ${undefined}`(true, false); // 'Choose: true / false'

Việc áp dụng thẻ này sẽ trả về một 'function'(thay vì a 'string') bỏ qua bất kỳ tham số nào được truyền cho nghĩa đen. Sau đó, nó có thể được gọi với các tham số mới sau này. Nếu một tham số không có thay thế tương ứng, nó sẽ trở thành 'undefined'.


Câu trả lời mở rộng

Mã đơn giản này có chức năng, nhưng nếu bạn cần hành vi phức tạp hơn, logic tương tự có thể được áp dụng và có vô số khả năng. Bạn có thể:

  1. Sử dụng các thông số gốc:

Bạn có thể lưu trữ các giá trị ban đầu được chuyển đến chữ trong cấu trúc và sử dụng chúng theo những cách sáng tạo khi áp dụng mẫu. Chúng có thể trở thành cờ, trình xác thực loại, hàm, v.v. Đây là một ví dụ sử dụng chúng làm giá trị mặc định:

    function deferWithDefaults([first, ...rest], ...defaults) {
      return (...values) => rest.reduce((acc, curr, i) => {
        return acc + (i < values.length ? values[i] : defaults[i]) + curr;
      }, first);
    }

Sau đó:

    > t = deferWithDefaults`My template is: ${'extendable'} and ${'versatile'}`;
    > t('awesome');                 // 'My template is: awesome and versatile' 
  1. Viết một nhà máy mẫu:

Làm điều đó bằng cách gói logic này trong một hàm mong đợi, như là đối số, một hàm tùy chỉnh có thể được áp dụng trong việc rút gọn (khi nối các phần của mẫu theo nghĩa đen) và trả về một mẫu mới với hành vi tùy chỉnh.

    const createTemplate = fn => function (strings, ...defaults) {
      const [first, ...rest] = strings;
      return (...values) => rest.reduce((acc, curr, i) => {
        return acc + fn(values[i], defaults[i]) + curr;
      }, first);
    };

Sau đó, bạn có thể, ví dụ: viết các mẫu tự động thoát hoặc khử trùng các tham số khi viết html, css, sql, bash ... được nhúng

    function sqlSanitize(token, tag) {
      // this is a gross simplification, don't use in production.
      const quoteName = name => (!/^[a-z_][a-z0-9_$]*$/.test(name) ? `"${name.replace(/"/g, '""')}"` : name);
      const quoteValue = value => (typeof value == 'string' ? `'${value.replace(/'/g, "''")}'` : value);
      switch (tag) {
        case 'table':
          return quoteName(token);
        case 'columns':
          return token.map(quoteName);
        case 'row':
          return token.map(quoteValue);
        default:
          return token;
      }
    }

    const sql = createTemplate(sqlSanitize);

Với mẫu sql ngây thơ này (tôi nhắc lại, ngây thơ! ), Chúng ta có thể tạo các truy vấn như sau:

    > q  = sql`INSERT INTO ${'table'} (${'columns'})
    ... VALUES (${'row'});`
    > q('user', ['id', 'user name', 'is"Staff"?'], [1, "O'neil", true])
    // `INSERT INTO user (id,"user name","is""Staff""?")
    // VALUES (1,'O''neil',true);`
  1. Chấp nhận các tham số đã đặt tên để thay thế: Một bài tập không quá khó, dựa trên những gì đã có. Có một cách triển khai trong câu trả lời khác này .

  2. Làm cho đối tượng trả về hoạt động như 'string'sau: Chà, điều này gây tranh cãi, nhưng có thể dẫn đến kết quả thú vị. Thể hiện trong câu trả lời khác này .

  3. Giải quyết các tham số trong không gian tên chung tại trang web cuộc gọi:

Tôi cung cấp cho bạn, các ký tự mẫu có thể tái sử dụng:

Vâng, đây là những gì OP cho thấy là phụ lục của anh ấy, sử dụng lệnh evil, ý tôi là eval,. Điều này có thể được thực hiện mà không cần eval, chỉ bằng cách tìm kiếm tên biến được truyền vào đối tượng toàn cục (hoặc cửa sổ). Tôi sẽ không chỉ cách làm điều đó vì tôi không thích nó. Đóng cửa là sự lựa chọn phù hợp.


2

Đây là nỗ lực tốt nhất của tôi:

var s = (item, price) => {return `item: ${item}, price: $${price}`}
s('pants', 10) // 'item: pants, price: $10'
s('shirts', 15) // 'item: shirts, price: $15'

Để khái quát:

var s = (<variable names you want>) => {return `<template with those variables>`}

Nếu bạn không chạy E6, bạn cũng có thể làm:

var s = function(<variable names you want>){return `<template with those variables>`}

Điều này có vẻ ngắn gọn hơn một chút so với các câu trả lời trước.

https://repl.it/@abalter/reusable-JS-template-literal


2

Nói chung, tôi chống lại việc sử dụng cái ác eval(), nhưng trong trường hợp này, nó có lý:

var template = "`${a}.${b}`";
var a = 1, b = 2;
var populated = eval(template);

console.log(populated);         // shows 1.2

Sau đó, nếu bạn thay đổi các giá trị và gọi lại eval (), bạn sẽ nhận được kết quả mới:

a = 3; b = 4;
populated = eval(template);

console.log(populated);         // shows 3.4

Nếu bạn muốn nó trong một hàm, thì nó có thể được viết như vậy:

function populate(a, b){
  return `${a}.${b}`;
}

Nếu bạn đang viết một hàm bao gồm mẫu, bạn chắc chắn không nên sử dụng eval.
Bergi

@Bergi Tại sao? Nó khác với cách triển khai của bạn như thế nào?
isapir

2
Những lý do tôi "dường như biết" áp dụng cho bất kỳ mã được xây dựng động nào. Viết hàm để tạo kết quả mà không gọi eval()rõ ràng là hoàn toàn giống với eval(), do đó không có lợi ích gì vì nó chỉ làm cho mã khó đọc hơn.
isapir

1
Chính xác. Và vì populatehàm của bạn không tự động xây dựng mã, nó không nên sử dụng evalvới tất cả các nhược điểm của nó.
Bergi

6
chức năng của bạn có thể chỉ là function populate(a,b) { return `${a}.${b}`; }giá trị không thêm gì
Vitim.us

1

CẬP NHẬT: Câu trả lời sau được giới hạn cho các tên biến đơn, do đó, các mẫu như: 'Result ${a+b}'không hợp lệ cho trường hợp này. Tuy nhiên, bạn luôn có thể chơi với các giá trị mẫu:

format("This is a test: ${a_b}", {a_b: a+b});

CÂU TRẢ LỜI GỐC:

Dựa trên các câu trả lời trước nhưng tạo ra một chức năng tiện ích "thân thiện" hơn:

var format = (template, params) => {
    let tpl = template.replace(/\${(?!this\.)/g, "${this.");
    let tpl_func = new Function(`return \`${tpl}\``);

    return tpl_func.call(params);
}

Bạn có thể lập hóa đơn giống như:

format("This is a test: ${hola}, second param: ${hello}", {hola: 'Hola', hello: 'Hi'});

Và chuỗi kết quả phải là:

'This is a test: Hola, second param: Hi'

Còn về một mẫu như thế này? `Result: ${a+b}`
Atiris

1
Xin chào @Atiris, bạn nói đúng, Đó là một hạn chế, tôi đã cập nhật câu trả lời của mình.
Roberto

1

Nếu bạn đang tìm kiếm thứ gì đó khá đơn giản (chỉ là các trường biến cố định, không cần tính toán, điều kiện…) nhưng nó cũng hoạt động ở phía máy khách trên các trình duyệt không hỗ trợ chuỗi mẫu như IE 8,9,10,11

ở đây chúng ta bắt đầu:

fillTemplate = function (templateString, templateVars) {
    var parsed = templateString;
    Object.keys(templateVars).forEach(
        (key) => {
            const value = templateVars[key]
            parsed = parsed.replace('${'+key+'}',value)
        }
    )
    return parsed
}

Thao tác này sẽ thực hiện tra cứu mọi biến. Có một cách khác thay thế tất cả các lần xuất hiện cùng một lúc mà tôi thực hiện trong mô-đun này: an toàn-es6-template
Aalex Gabi

1

Tôi đã bực mình tại dự phòng cần thêm gõ this.mọi thời gian, vì vậy tôi cũng nói thêm regex để mở rộng biến như .ađểthis.a .

Giải pháp:

const interp = template => _thisObj =>
function() {
    return template.replace(/\${([^}]*)}/g, (_, k) =>
        eval(
            k.replace(/([.a-zA-Z0-9$_]*)([a-zA-Z0-9$_]+)/, (r, ...args) =>
                args[0].charAt(0) == '.' ? 'this' + args[0] + args[1] : r
            )
        )
    );
}.call(_thisObj);

Sử dụng như vậy:

console.log(interp('Hello ${.a}${.b}')({ a: 'World', b: '!' }));
// outputs: Hello World!

1

Tôi chỉ xuất bản một gói npm có thể thực hiện công việc này một cách đơn giản. Cảm hứng sâu sắc từ câu trả lời này .

const Template = require('dynamic-template-string');

var tpl = new Template('hello ${name}');

tpl.fill({name: 'world'}); // ==> 'hello world';
tpl.fill({name: 'china'}); // ==> 'hello china';

Thực hiện của nó là đơn giản chết người. Chúc bạn sẽ thích nó.


module.exports = class Template {
  constructor(str) {
    this._func = new Function(`with(this) { return \`${str}\`; }`);
  }

  fill(data) {
    return this._func.call(data);
  }
}

1

bạn có thể sử dụng hàm mũi tên nội tuyến như thế này, định nghĩa:

const template = (substitute: string) => `[^.?!]*(?<=[.?\s!])${substitute}(?=[\s.?!])[^.?!]*[.?!]`;

sử dụng:

console.log(template('my replaced string'));

1

Chuỗi mẫu thời gian chạy

var templateString = (template, values) => {
    let output = template;
    Object.keys(values)
        .forEach(key => {
        output = output.replace(new RegExp('\\$' + `{${key}}`, 'g'), values[key]);
    });
    return output;
};

Kiểm tra

console.debug(templateString('hello ${word} world', {word: 'wonderful'}));

0

const fillTemplate = (template, values) => {
  template = template.replace(/(?<=\${)\w+(?=})/g, v=>"this."+v);
  return Function.apply(this, ["", "return `"+template+"`;"]).call(values);
};

console.log(fillTemplate("The man ${man} is brother of ${brother}", {man: "John", brother:"Peter"}));
//The man John is brother of Peter

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.