Always getting "[object Object]" as output from custom string format function - javascript

I'm using a string format function copied direct from the StackOverflow source code; you can test it right now in the developer console with the following code:
"Logged in as {tag}, on {guilds} guild{s}".formatUnicorn({ tag: "TAG_HERE", guilds: "GUILD_COUNT", s: "s" });
I'm trying to use this exact same function, yet am always getting [object Object] as the output.
logger.info("bot", "Logged in as {tag}, on {guilds} guild{s}".format({ tag: client.user.tag, guilds: client.guilds.size, s: client.guilds.size === 1 ? "" : "s" }));
I've tried require("util").inspect(...)ing the Object that's returned, but that just outputs '[object Object]', essentially the exact same thing but with single quotes surrounding it.
Here's the function, if that helps. I've renamed some of the variables for this question, though in testing the code it was a direct copy from the SO source:
String.prototype.format = () => {
let string = this.toString();
if(!arguments.length)
return string;
let type = typeof arguments[0],
replacements = "string" == type || "number" == type ? Array.prototype.slice.call(arguments) : arguments[0];
for(const index in replacements)
string = string.replace(new RegExp("\\{" + index + "\\}", "gi"), replacements[index]);
return string;
}
This is likely a simple mistake with a simple solution but I've worked quite a while to try to diagnose the issue/get it working but finding nothing to fix it myself.

Arrow functions does not bind its own arguments so your function will not work.
Changing from String.prototype.format = () => {} to String.prototype.format = function () {} will fix it for you.

it hapens because you are using this.toString(); and this is a json Object.
try it :
String.prototype.format = function() { ...
instead:
String.prototype.format = () => { ...

Related

Is it possible to convert a double quoted string with template literal to tilde string to assign values? [duplicate]

Is it possible to create a template string as a usual string,
let a = "b:${b}";
and then convert it into a template string,
let b = 10;
console.log(a.template()); // b:10
without eval, new Function and other means of dynamic code generation?
In my project I've created something like this with 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);
As your template string must get reference to the b variable dynamically (in runtime), so the answer is: NO, it's impossible to do it without dynamic code generation.
But, with eval it's pretty simple:
let tpl = eval('`'+a+'`');
No, there is not a way to do this without dynamic code generation.
However, I have created a function which will turn a regular string into a function which can be provided with a map of values, using template strings internally.
Generate 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;
})();
Usage:
var kingMaker = generateTemplateString('${name} is king!');
console.log(kingMaker({name: 'Bryan'}));
// Logs 'Bryan is king!' to the console.
Hope this helps somebody. If you find a problem with the code, please be so kind as to update the Gist.
What you're asking for here:
//non working code quoted from the question
let b=10;
console.log(a.template());//b:10
is exactly equivalent (in terms of power and, er, safety) to eval: the ability to take a string containing code and execute that code; and also the ability for the executed code to see local variables in the caller's environment.
There is no way in JS for a function to see local variables in its caller, unless that function is eval(). Even Function() can't do it.
When you hear there's something called "template strings" coming to JavaScript, it's natural to assume it's a built-in template library, like Mustache. It isn't. It's mainly just string interpolation and multiline strings for JS. I think this is going to be a common misconception for a while, though. :(
There are many good solutions posted here, but none yet which utilizes the ES6 String.raw method. Here is my contriubution. It has an important limitation in that it will only accept properties from a passed in object, meaning no code execution in the template will work.
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?
Split string into non-argument textual parts. See regex.
parts: ["Hello, ", "! Are you ", " years old?"]
Split string into property names. Empty array if match fails.
args: ["name", "age"]
Map parameters from obj by property name. Solution is limited by shallow one level mapping. Undefined values are substituted with an empty string, but other falsy values are accepted.
parameters: ["John Doe", 18]
Utilize String.raw(...) and return result.
TLDR:
https://jsfiddle.net/bj89zntu/1/
Everyone seems to be worried about accessing variables. Why not just pass them? I'm sure it won't be too hard to get the variable context in the caller and pass it down. Use
ninjagecko's answer to get the props from obj.
function renderString(str,obj){
return str.replace(/\$\{(.+?)\}/g,(match,p1)=>{return index(obj,p1)})
}
Here is the full code:
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
The issue here is to have a function that has access to the variables of its caller. This is why we see direct eval being used for template processing. A possible solution would be to generate a function taking formal parameters named by a dictionary's properties, and calling it with the corresponding values in the same order. An alternative way would be to have something simple as this:
var name = "John Smith";
var message = "Hello, my name is ${name}";
console.log(new Function('return `' + message + '`;')());
And for anyone using Babel compiler we need to create closure which remembers the environment in which it was created:
console.log(new Function('name', 'return `' + message + '`;')(name));
I liked s.meijer's answer and wrote my own version based on his:
function parseTemplate(template, map, fallback) {
return template.replace(/\$\{[^}]+\}/g, (match) =>
match
.slice(2, -1)
.trim()
.split(".")
.reduce(
(searchObject, key) => searchObject[key] || fallback || match,
map
)
);
}
Similar to Daniel's answer (and s.meijer's gist) but more readable:
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);
}
Note: This slightly improves s.meijer's original, since it won't match things like ${foo{bar} (the regex only allows non-curly brace characters inside ${ and }).
UPDATE: I was asked for an example using this, so here you go:
const replacements = {
name: 'Bob',
age: 37
}
interpolate('My name is ${name}, and I am ${age}.', replacements)
#Mateusz Moska, solution works great, but when i used it in React Native(build mode), it throws an error: Invalid character '`', though it works when i run it in debug mode.
So i wrote down my own solution using 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/
NOTE: Since I don't know the root cause of an issue, i raised a ticket in react-native repo, https://github.com/facebook/react-native/issues/14107, so that once they can able to fix/guide me about the same :)
You can use the string prototype, for example
String.prototype.toTemplate=function(){
return eval('`'+this+'`');
}
//...
var a="b:${b}";
var b=10;
console.log(a.toTemplate());//b:10
But the answer of the original question is no way.
I required this method with support for Internet Explorer. It turned out the back ticks aren't supported by even IE11. Also; using eval or it's equivalent Function doesn't feel right.
For the one that notice; I also use backticks, but these ones are removed by compilers like babel. The methods suggested by other ones, depend on them on run-time. As said before; this is an issue in IE11 and lower.
So this is what I came up with:
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);
});
}
Example 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 -
I currently can't comment on existing answers so I am unable to directly comment on Bryan Raynor's excellent response. Thus, this response is going to update his answer with a slight correction.
In short, his function fails to actually cache the created function, so it will always recreate, regardless of whether it's seen the template before. Here is the corrected code:
/**
* 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;
})();
Still dynamic but seems more controlled than just using a naked eval:
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
I made my own solution doing a type with a description as a function
export class Foo {
...
description?: Object;
...
}
let myFoo:Foo = {
...
description: (a,b) => `Welcome ${a}, glad to see you like the ${b} section`.
...
}
and so doing:
let myDescription = myFoo.description('Bar', 'bar');
I came up with this implementation and it works like a charm.
function interpolateTemplate(template: string, args: any): string {
return Object.entries(args).reduce(
(result, [arg, val]) => result.replace(`$\{${arg}}`, `${val}`),
template,
)
}
const template = 'This is an example: ${name}, ${age} ${email}'
console.log(interpolateTemplate(template,{name:'Med', age:'20', email:'example#abc.com'}))
You could raise an error if arg is not found in template
This solution works without 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"
Note: the Function constructor is always created in the global scope, which could potentially cause global variables to be overwritten by the template, e.g. render("hello ${ someGlobalVar = 'some new value' }", {name:'mo'});
You should try this tiny JS module, by Andrea Giammarchi, from 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 (all the following tests return 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});
Faz assim (This way):
let a = 'b:${this.b}'
let b = 10
function template(templateString, templateVars) {
return new Function('return `' + templateString + '`').call(templateVars)
}
result.textContent = template(a, {b})
<b id=result></b>
Since we're reinventing the wheel on something that would be a lovely feature in javascript.
I use eval(), which is not secure, but javascript is not secure. I readily admit that I'm not excellent with javascript, but I had a need, and I needed an answer so I made one.
I chose to stylize my variables with an # rather than an $, particularly because I want to use the multiline feature of literals without evaluating til it's ready. So variable syntax is #{OptionalObject.OptionalObjectN.VARIABLE_NAME}
I am no javascript expert, so I'd gladly take advice on improvement but...
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);
}
A very simple implementation follows
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);
}
In my actual implementation, I choose to use #{{variable}}. One more set of braces. Absurdly unlikely to encounter that unexpectedly. The regex for that would look like /\#\{\{(.*?)(?!\#\{\{)\}\}/g
To make that easier to read
\#\{\{ # 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.
If you're not experienced with regex, a pretty safe rule is to escape every non-alphanumeric character, and don't ever needlessly escape letters as many escaped letters have special meaning to virtually all flavors of regex.
You can refer to this solution
const interpolate = (str) =>
new Function(`return \`${new String(str)}\`;`)();
const foo = 'My';
const obj = {
text: 'Hanibal Lector',
firstNum: 1,
secondNum: 2
}
const str = "${foo} name is : ${obj.text}. sum = ${obj.firstNum} + ${obj.secondNum} = ${obj.firstNum + obj.secondNum}";
console.log(interpolate(str));
I realize I am late to the game, but you could:
const a = (b) => `b:${b}`;
let b = 10;
console.log(a(b)); // b:10

Javascript string interpolation not working from text input [duplicate]

Is it possible to create a template string as a usual string,
let a = "b:${b}";
and then convert it into a template string,
let b = 10;
console.log(a.template()); // b:10
without eval, new Function and other means of dynamic code generation?
In my project I've created something like this with 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);
As your template string must get reference to the b variable dynamically (in runtime), so the answer is: NO, it's impossible to do it without dynamic code generation.
But, with eval it's pretty simple:
let tpl = eval('`'+a+'`');
No, there is not a way to do this without dynamic code generation.
However, I have created a function which will turn a regular string into a function which can be provided with a map of values, using template strings internally.
Generate 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;
})();
Usage:
var kingMaker = generateTemplateString('${name} is king!');
console.log(kingMaker({name: 'Bryan'}));
// Logs 'Bryan is king!' to the console.
Hope this helps somebody. If you find a problem with the code, please be so kind as to update the Gist.
What you're asking for here:
//non working code quoted from the question
let b=10;
console.log(a.template());//b:10
is exactly equivalent (in terms of power and, er, safety) to eval: the ability to take a string containing code and execute that code; and also the ability for the executed code to see local variables in the caller's environment.
There is no way in JS for a function to see local variables in its caller, unless that function is eval(). Even Function() can't do it.
When you hear there's something called "template strings" coming to JavaScript, it's natural to assume it's a built-in template library, like Mustache. It isn't. It's mainly just string interpolation and multiline strings for JS. I think this is going to be a common misconception for a while, though. :(
There are many good solutions posted here, but none yet which utilizes the ES6 String.raw method. Here is my contriubution. It has an important limitation in that it will only accept properties from a passed in object, meaning no code execution in the template will work.
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?
Split string into non-argument textual parts. See regex.
parts: ["Hello, ", "! Are you ", " years old?"]
Split string into property names. Empty array if match fails.
args: ["name", "age"]
Map parameters from obj by property name. Solution is limited by shallow one level mapping. Undefined values are substituted with an empty string, but other falsy values are accepted.
parameters: ["John Doe", 18]
Utilize String.raw(...) and return result.
TLDR:
https://jsfiddle.net/bj89zntu/1/
Everyone seems to be worried about accessing variables. Why not just pass them? I'm sure it won't be too hard to get the variable context in the caller and pass it down. Use
ninjagecko's answer to get the props from obj.
function renderString(str,obj){
return str.replace(/\$\{(.+?)\}/g,(match,p1)=>{return index(obj,p1)})
}
Here is the full code:
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
The issue here is to have a function that has access to the variables of its caller. This is why we see direct eval being used for template processing. A possible solution would be to generate a function taking formal parameters named by a dictionary's properties, and calling it with the corresponding values in the same order. An alternative way would be to have something simple as this:
var name = "John Smith";
var message = "Hello, my name is ${name}";
console.log(new Function('return `' + message + '`;')());
And for anyone using Babel compiler we need to create closure which remembers the environment in which it was created:
console.log(new Function('name', 'return `' + message + '`;')(name));
I liked s.meijer's answer and wrote my own version based on his:
function parseTemplate(template, map, fallback) {
return template.replace(/\$\{[^}]+\}/g, (match) =>
match
.slice(2, -1)
.trim()
.split(".")
.reduce(
(searchObject, key) => searchObject[key] || fallback || match,
map
)
);
}
Similar to Daniel's answer (and s.meijer's gist) but more readable:
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);
}
Note: This slightly improves s.meijer's original, since it won't match things like ${foo{bar} (the regex only allows non-curly brace characters inside ${ and }).
UPDATE: I was asked for an example using this, so here you go:
const replacements = {
name: 'Bob',
age: 37
}
interpolate('My name is ${name}, and I am ${age}.', replacements)
#Mateusz Moska, solution works great, but when i used it in React Native(build mode), it throws an error: Invalid character '`', though it works when i run it in debug mode.
So i wrote down my own solution using 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/
NOTE: Since I don't know the root cause of an issue, i raised a ticket in react-native repo, https://github.com/facebook/react-native/issues/14107, so that once they can able to fix/guide me about the same :)
You can use the string prototype, for example
String.prototype.toTemplate=function(){
return eval('`'+this+'`');
}
//...
var a="b:${b}";
var b=10;
console.log(a.toTemplate());//b:10
But the answer of the original question is no way.
I required this method with support for Internet Explorer. It turned out the back ticks aren't supported by even IE11. Also; using eval or it's equivalent Function doesn't feel right.
For the one that notice; I also use backticks, but these ones are removed by compilers like babel. The methods suggested by other ones, depend on them on run-time. As said before; this is an issue in IE11 and lower.
So this is what I came up with:
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);
});
}
Example 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 -
I currently can't comment on existing answers so I am unable to directly comment on Bryan Raynor's excellent response. Thus, this response is going to update his answer with a slight correction.
In short, his function fails to actually cache the created function, so it will always recreate, regardless of whether it's seen the template before. Here is the corrected code:
/**
* 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;
})();
Still dynamic but seems more controlled than just using a naked eval:
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
I made my own solution doing a type with a description as a function
export class Foo {
...
description?: Object;
...
}
let myFoo:Foo = {
...
description: (a,b) => `Welcome ${a}, glad to see you like the ${b} section`.
...
}
and so doing:
let myDescription = myFoo.description('Bar', 'bar');
I came up with this implementation and it works like a charm.
function interpolateTemplate(template: string, args: any): string {
return Object.entries(args).reduce(
(result, [arg, val]) => result.replace(`$\{${arg}}`, `${val}`),
template,
)
}
const template = 'This is an example: ${name}, ${age} ${email}'
console.log(interpolateTemplate(template,{name:'Med', age:'20', email:'example#abc.com'}))
You could raise an error if arg is not found in template
This solution works without 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"
Note: the Function constructor is always created in the global scope, which could potentially cause global variables to be overwritten by the template, e.g. render("hello ${ someGlobalVar = 'some new value' }", {name:'mo'});
You should try this tiny JS module, by Andrea Giammarchi, from 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 (all the following tests return 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});
Faz assim (This way):
let a = 'b:${this.b}'
let b = 10
function template(templateString, templateVars) {
return new Function('return `' + templateString + '`').call(templateVars)
}
result.textContent = template(a, {b})
<b id=result></b>
Since we're reinventing the wheel on something that would be a lovely feature in javascript.
I use eval(), which is not secure, but javascript is not secure. I readily admit that I'm not excellent with javascript, but I had a need, and I needed an answer so I made one.
I chose to stylize my variables with an # rather than an $, particularly because I want to use the multiline feature of literals without evaluating til it's ready. So variable syntax is #{OptionalObject.OptionalObjectN.VARIABLE_NAME}
I am no javascript expert, so I'd gladly take advice on improvement but...
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);
}
A very simple implementation follows
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);
}
In my actual implementation, I choose to use #{{variable}}. One more set of braces. Absurdly unlikely to encounter that unexpectedly. The regex for that would look like /\#\{\{(.*?)(?!\#\{\{)\}\}/g
To make that easier to read
\#\{\{ # 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.
If you're not experienced with regex, a pretty safe rule is to escape every non-alphanumeric character, and don't ever needlessly escape letters as many escaped letters have special meaning to virtually all flavors of regex.
You can refer to this solution
const interpolate = (str) =>
new Function(`return \`${new String(str)}\`;`)();
const foo = 'My';
const obj = {
text: 'Hanibal Lector',
firstNum: 1,
secondNum: 2
}
const str = "${foo} name is : ${obj.text}. sum = ${obj.firstNum} + ${obj.secondNum} = ${obj.firstNum + obj.secondNum}";
console.log(interpolate(str));
I realize I am late to the game, but you could:
const a = (b) => `b:${b}`;
let b = 10;
console.log(a(b)); // b:10

Using template literals with JSON [duplicate]

Is it possible to create a template string as a usual string,
let a = "b:${b}";
and then convert it into a template string,
let b = 10;
console.log(a.template()); // b:10
without eval, new Function and other means of dynamic code generation?
In my project I've created something like this with 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);
As your template string must get reference to the b variable dynamically (in runtime), so the answer is: NO, it's impossible to do it without dynamic code generation.
But, with eval it's pretty simple:
let tpl = eval('`'+a+'`');
No, there is not a way to do this without dynamic code generation.
However, I have created a function which will turn a regular string into a function which can be provided with a map of values, using template strings internally.
Generate 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;
})();
Usage:
var kingMaker = generateTemplateString('${name} is king!');
console.log(kingMaker({name: 'Bryan'}));
// Logs 'Bryan is king!' to the console.
Hope this helps somebody. If you find a problem with the code, please be so kind as to update the Gist.
What you're asking for here:
//non working code quoted from the question
let b=10;
console.log(a.template());//b:10
is exactly equivalent (in terms of power and, er, safety) to eval: the ability to take a string containing code and execute that code; and also the ability for the executed code to see local variables in the caller's environment.
There is no way in JS for a function to see local variables in its caller, unless that function is eval(). Even Function() can't do it.
When you hear there's something called "template strings" coming to JavaScript, it's natural to assume it's a built-in template library, like Mustache. It isn't. It's mainly just string interpolation and multiline strings for JS. I think this is going to be a common misconception for a while, though. :(
There are many good solutions posted here, but none yet which utilizes the ES6 String.raw method. Here is my contriubution. It has an important limitation in that it will only accept properties from a passed in object, meaning no code execution in the template will work.
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?
Split string into non-argument textual parts. See regex.
parts: ["Hello, ", "! Are you ", " years old?"]
Split string into property names. Empty array if match fails.
args: ["name", "age"]
Map parameters from obj by property name. Solution is limited by shallow one level mapping. Undefined values are substituted with an empty string, but other falsy values are accepted.
parameters: ["John Doe", 18]
Utilize String.raw(...) and return result.
TLDR:
https://jsfiddle.net/bj89zntu/1/
Everyone seems to be worried about accessing variables. Why not just pass them? I'm sure it won't be too hard to get the variable context in the caller and pass it down. Use
ninjagecko's answer to get the props from obj.
function renderString(str,obj){
return str.replace(/\$\{(.+?)\}/g,(match,p1)=>{return index(obj,p1)})
}
Here is the full code:
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
The issue here is to have a function that has access to the variables of its caller. This is why we see direct eval being used for template processing. A possible solution would be to generate a function taking formal parameters named by a dictionary's properties, and calling it with the corresponding values in the same order. An alternative way would be to have something simple as this:
var name = "John Smith";
var message = "Hello, my name is ${name}";
console.log(new Function('return `' + message + '`;')());
And for anyone using Babel compiler we need to create closure which remembers the environment in which it was created:
console.log(new Function('name', 'return `' + message + '`;')(name));
I liked s.meijer's answer and wrote my own version based on his:
function parseTemplate(template, map, fallback) {
return template.replace(/\$\{[^}]+\}/g, (match) =>
match
.slice(2, -1)
.trim()
.split(".")
.reduce(
(searchObject, key) => searchObject[key] || fallback || match,
map
)
);
}
Similar to Daniel's answer (and s.meijer's gist) but more readable:
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);
}
Note: This slightly improves s.meijer's original, since it won't match things like ${foo{bar} (the regex only allows non-curly brace characters inside ${ and }).
UPDATE: I was asked for an example using this, so here you go:
const replacements = {
name: 'Bob',
age: 37
}
interpolate('My name is ${name}, and I am ${age}.', replacements)
#Mateusz Moska, solution works great, but when i used it in React Native(build mode), it throws an error: Invalid character '`', though it works when i run it in debug mode.
So i wrote down my own solution using 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/
NOTE: Since I don't know the root cause of an issue, i raised a ticket in react-native repo, https://github.com/facebook/react-native/issues/14107, so that once they can able to fix/guide me about the same :)
You can use the string prototype, for example
String.prototype.toTemplate=function(){
return eval('`'+this+'`');
}
//...
var a="b:${b}";
var b=10;
console.log(a.toTemplate());//b:10
But the answer of the original question is no way.
I required this method with support for Internet Explorer. It turned out the back ticks aren't supported by even IE11. Also; using eval or it's equivalent Function doesn't feel right.
For the one that notice; I also use backticks, but these ones are removed by compilers like babel. The methods suggested by other ones, depend on them on run-time. As said before; this is an issue in IE11 and lower.
So this is what I came up with:
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);
});
}
Example 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 -
I currently can't comment on existing answers so I am unable to directly comment on Bryan Raynor's excellent response. Thus, this response is going to update his answer with a slight correction.
In short, his function fails to actually cache the created function, so it will always recreate, regardless of whether it's seen the template before. Here is the corrected code:
/**
* 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;
})();
Still dynamic but seems more controlled than just using a naked eval:
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
I made my own solution doing a type with a description as a function
export class Foo {
...
description?: Object;
...
}
let myFoo:Foo = {
...
description: (a,b) => `Welcome ${a}, glad to see you like the ${b} section`.
...
}
and so doing:
let myDescription = myFoo.description('Bar', 'bar');
I came up with this implementation and it works like a charm.
function interpolateTemplate(template: string, args: any): string {
return Object.entries(args).reduce(
(result, [arg, val]) => result.replace(`$\{${arg}}`, `${val}`),
template,
)
}
const template = 'This is an example: ${name}, ${age} ${email}'
console.log(interpolateTemplate(template,{name:'Med', age:'20', email:'example#abc.com'}))
You could raise an error if arg is not found in template
This solution works without 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"
Note: the Function constructor is always created in the global scope, which could potentially cause global variables to be overwritten by the template, e.g. render("hello ${ someGlobalVar = 'some new value' }", {name:'mo'});
You should try this tiny JS module, by Andrea Giammarchi, from 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 (all the following tests return 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});
Faz assim (This way):
let a = 'b:${this.b}'
let b = 10
function template(templateString, templateVars) {
return new Function('return `' + templateString + '`').call(templateVars)
}
result.textContent = template(a, {b})
<b id=result></b>
Since we're reinventing the wheel on something that would be a lovely feature in javascript.
I use eval(), which is not secure, but javascript is not secure. I readily admit that I'm not excellent with javascript, but I had a need, and I needed an answer so I made one.
I chose to stylize my variables with an # rather than an $, particularly because I want to use the multiline feature of literals without evaluating til it's ready. So variable syntax is #{OptionalObject.OptionalObjectN.VARIABLE_NAME}
I am no javascript expert, so I'd gladly take advice on improvement but...
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);
}
A very simple implementation follows
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);
}
In my actual implementation, I choose to use #{{variable}}. One more set of braces. Absurdly unlikely to encounter that unexpectedly. The regex for that would look like /\#\{\{(.*?)(?!\#\{\{)\}\}/g
To make that easier to read
\#\{\{ # 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.
If you're not experienced with regex, a pretty safe rule is to escape every non-alphanumeric character, and don't ever needlessly escape letters as many escaped letters have special meaning to virtually all flavors of regex.
You can refer to this solution
const interpolate = (str) =>
new Function(`return \`${new String(str)}\`;`)();
const foo = 'My';
const obj = {
text: 'Hanibal Lector',
firstNum: 1,
secondNum: 2
}
const str = "${foo} name is : ${obj.text}. sum = ${obj.firstNum} + ${obj.secondNum} = ${obj.firstNum + obj.secondNum}";
console.log(interpolate(str));
I realize I am late to the game, but you could:
const a = (b) => `b:${b}`;
let b = 10;
console.log(a(b)); // b:10

String to Object conversion?

I have a string variable that is a string representation of an object. Like this:
{
"column": "'asdf'",
"sort": "true",
"search": "{\"asdf\":\"searchval\"}"
}
I would like to transform the above string into an object that looks like this:
{
column: 'asdf',
sort: 'true',
search: {
asdf: 'searchval'
}
}
I am planning on doing a search and replace regex expression for quotes and back slashes, and then using eval(), but that seems unsafe and innefficient. Is there an alternative method that would work better?
I am planning on assigning the object value of the string to an object variable.
Have you tried JSON.parse(); yet?
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse
var myOriginalData = JSON.stringify({"column": "'asdf'","sort": "true","search": "{\"asdf\":\"searchval\"}"});
myNewData = JSON.parse(myOriginalData, function (key, value) {
var type;
if (value && typeof value === 'object') {
type = value.type;
if (typeof type === 'string' && typeof window[type] === 'function') {
return new (window[type])(value);
}
}
return value;
});
console.log('myNewData -----------------------------------------------------------');
console.log(myNewData);
Working Example: http://plnkr.co/edit/2jLGIxx3AVEceVQsOpxL?p=preview
Immediately after posting this, I found out there is a JSON.parse(); function. The only reason I don't delete the question, is it might save someone time later.
All you need to do is pass the string variable as a parameter and it will output an object.
If anyone sees any problems with this solution, or knows a better solution, please make an answer!

Converting objects to text that can be reproduced by eval(); anomolies with function object

My objective is to translate an object into text form that can be stored using, say, localStorage, and subsequently retrieved and converted back to its original object form using eval(). I have written a function called uneval() to do the conversion to text as follows:
function uneval(obj){
// Convert object to a string that can be evaluated with eval() to its original
if(obj===null)return 'null';
if(typeof obj === 'number' || typeof obj === 'boolean')return ''+obj;
if(typeof obj === 'string')return '"'+obj+'"';
if(Object.prototype.toString.call(obj) === '[object Array]')
{ var str = '[', i=0, l=obj.length;
for(i;i<l;i++)str+= (i==0?'':',') + uneval(obj[i]);
return str+']';
}
if(typeof obj === 'object')
{ var str='({', i;
for (i in obj)str+= (2==str.length?'':',') + i + ':' + uneval(obj[i]);
return str+='})';
}
if(typeof obj === 'function')return '(' + obj.toString() + ')';
return '';}
[Edit 1: An object in {} needs parenthese around to be successfully converted using eval()]
[Edit 2: Function code amended to place parentheses around the function code string]
[Edit 3: Changed to a named function for recursion avoiding arguments.callee]
[Edit 4: Handle null]
Note that this function retains nested structure. It works fine (at least in Google Chrome and MS IE8) except when the object, or an item of the object, is a function. I have found a workaround by making an assignment of the function in string form to an unlikely variable, as "_=". That does the job. Without that, I get a syntax error from eval(). Could anyone explain why that is the case, please?
By the way, I can avoid the small chance of a conflict using _ as a variable by calling eval() within a cover function in which _ is localised.
In this code:
var func = eval('function () {}');
eval parses the string as a function declaration, not a function expression, and since function declarations cannot be anonymous, a syntax error is thrown. In order to ensure that your string is parsed as a function expression, instead of a declaration, just add parens at the beginning, and end of the string. These parents will be parsed as the grouping operator. The contents of the grouping operator are then parsed as an expression; in this case, a function expression.
var func = eval('(function () {})'); // doesn't throw
So, in your uneval function, do this:
if ( typeof obj === 'function' ) return '(' + obj.toString() + ')';

Categories