How to compile object inside a string? - javascript

I have this string:
const fullname = "my name is ${param.name}"
and this object:
const param = {
name: 'foo'
};
Is there a way to "compile" the string with the object in typescript/javascript?
Why? I want to separate the data from the schema (string).
Both are in separate files so I can't just do: my name is ${param.name}.
I have a function that takes the string and the object and make the output. but there is something like this exist?
The result I expect is:
`my name is foo`

Short answer (one line of code) - yes - it's possible
console.log(new Function('obj', 'return "hello " + obj.name')({ name: 'jackob' }))
Let's break it down to better understand what's happaned:
new Function('obj', 'return "hello " + obj.name')
This will create a new function that accepts a single parameter (as the name implies we are expecting an object). The function body is handling the object and concat it into a string (very similar to what you tried to do)
In order to run it we can use it as an anonymous function or create a reference (e.g. function delegation)
const func = new Function('obj', 'return "hello " + obj.name')
func({ name: 'jackob' }) // here we call the function with a static object as parameter
Because the function return a value (a compiled string) we can get the returned value
const value = func({ name: 'jackob' })
console.log(value)
And one last technical note: if you feel that you are going to compile expressions - I recommend JSONata (here's a specific link to your use-case). It's a great project that unifies your operations over json objects and will help you to get more support during development and in future releases.
Good luck 🍀

You are looking for a template string:
You can use the following syntax:
`my name is ${param.name}`

Related

Why can functions be called using `` operator in JavaScript? [duplicate]

I'm not sure how to explain this, but when I run
console.log`1`
In google chrome, I get output like
console.log`1`
VM12380:2 ["1", raw: Array[1]]
Why is the backtick calling the log function, and why is it making a index of raw: Array[1]?
Question brought up in the JS room by Catgocat, but no answers made sense besides something about templating strings that didn't really fit why this is happening.
It is called Tagged Template in ES-6 more could be read about them Here, funny I found the link in the starred section of the very chat.
But the relevant part of the code is below (you can basically create a filtered sort).
function tag(strings, ...values) {
assert(strings[0] === 'a');
assert(strings[1] === 'b');
assert(values[0] === 42);
return 'whatever';
}
tag `a${ 42 }b` // "whatever"
Basically, its merely tagging the "1" with console.log function, as it would do with any other function. The tagging functions accept parsed values of template strings and the values separately upon which further tasks can be performed.
Babel transpiles the above code to
var _taggedTemplateLiteralLoose = function (strings, raw) { strings.raw = raw; return strings; };
console.log(_taggedTemplateLiteralLoose(["1"], ["1"]));
As you can see it in the example above, after being transpiled by babel, the tagging function (console.log) is being passed the return value of the following es6->5 transpiled code.
_taggedTemplateLiteralLoose( ["1"], ["1"] );
The return value of this function is passed to console.log which will then print the array.
Tagged template literal:
The following syntax:
function`your template ${foo}`;
Is called the tagged template literal.
The function which is called as a tagged template literal receives the its arguments in the following manner:
function taggedTemplate(strings, arg1, arg2, arg3, arg4) {
console.log(strings);
console.log(arg1, arg2, arg3, arg4);
}
taggedTemplate`a${1}b${2}c${3}`;
The first argument is an array of all the individual string characters
The remaining argument correspond with the values of the variables which we receive via string interpolation. Notice in the example that there is no value for arg4 (because there are only 3 times string interpolation) and thus undefined is logged when we try to log arg4
Using the rest parameter syntax:
If we don't know beforehand how many times string interpolation will take place in the template string it is often useful to use the rest parameter syntax. This syntax stores the remaining arguments which the function receives into an array. For example:
function taggedTemplate(strings, ...rest) {
console.log(rest);
}
taggedTemplate `a${1}b${2}c${3}`;
taggedTemplate `a${1}b${2}c${3}d${4}`;
Late to the party but, TBH, none of the answers give an explanation to 50% of the original question ("why the raw: Array[1]")
1. Why is it possible to call the function without parenthesis, using backticks?
console.log`1`
As others have pointed out, this is called Tagged Template (more details also here).
Using this syntax, the function will receive the following arguments:
First argument: an array containing the different parts of the string that are not expressions.
Rest of arguments: each of the values that are being interpolated (ie. those which are expressions).
Basically, the following are 'almost' equivalent:
// Tagged Template
fn`My uncle ${uncleName} is ${uncleAge} years old!`
// function call
fn(["My uncle ", " is ", " years old!"], uncleName, uncleAge);
(see point 2. to understand why they're not exactly the same)
2. Why the ["1", raw: Array[1]] ???
The array being passed as the first argument contains a property raw, wich allows accessing the raw strings as they were entered (without processing escape sequences).
Example use case:
let fileName = "asdf";
fn`In the folder C:\Documents\Foo, create a new file ${fileName}`
function fn(a, ...rest) {
console.log(a); //In the folder C:DocumentsFoo, create a new file
console.log(a.raw); //In the folder C:\Documents\Foo, create a new file
}
What, an array with a property ??? ???
Yes, since JavaScript arrays are actually objects, they can store properties.
Example:
const arr = [1, 2, 3];
arr.property = "value";
console.log(arr); //[1, 2, 3, property: "value"]

How to parse functions bodies from a string using javascript?

I'm looking for a way to get a function declaration body by name from a string of js code. I'm in Nodejs environment.
Let's say I have some spaghetti.js file. I can read it into a string
const allJs = fs.readFileSync('spaghetti.js');
Now I need a function that receives that string and function name and returns a string with everything between { }.
Something like this
allJs = 'let a=1; const b=[2, 3]; function cook(items){return items}; cook(b)';
parseFunction(allJs, 'cook');//'return items'
The complexity of input js is not limited.
I tried to find an npm module for that, but no luck.
You should have a look at an AST parser for Javascript:
http://esprima.org/
https://github.com/ternjs/acorn
That should be more safe than using RegExp or something.
A String can be evaluated locally with the native eval() method. But remember, eval is a form of evil!
If the parseFunction() above is relying on something like this then the global Function constructor is being used and the 'new' function is bound to the return value of that operation (and thus that return value itself needs to be called).
A simple way to achieve this might be to do something like this...
var funcString = 'var a = 1, b = 3;';
funcString += 'function summit(){return a + b;}';
funcString += 'return summit();';
function makeNewFunc(str) {
return new Function(str);
}
var newFunc = makeNewFunc( funcString );
console.log('newFunc:',newFunc);
//-> newFunc: function anonymous()
console.log('newFunc():',newFunc());
//-> newFunc(): 4
This demonstrates how functions can be created and invoked from String snippets. (EDIT: Turning something like that into a Node module is a simple matter);
Hope that helped. :)

accessing user input within the value of an object literal

These forums are amazing and the contributors I've been helped by are really talented. So I keep coming back everytime I can't solve my own problems or am not understanding a programming concept. This is certainly one of the latter times!
With the help I've received so far, I've managed to develop a complicated form in which the end user will mostly click a series of checkboxes and enter some data into a few textfields. The result of these actions will populate some textboxes with various text, based on the aforementioned actions.
The text that populates the textareas is referenced within a few object literals by each checkbox. This works just fine and the site is quite useable.
In my object literals, I have name:value pairs in which the 'value' is a string of text. I've been trying to include a variable within some name:value pairs to no success. This always breaks the script because the variable is never defined / has a 'null' value on page load.
For example,
Instead of
var example = {
var1:'some string',
var2:'some other string'
}
I tried,
var somevariable = document.getElementById('someId');
var example = {
var1: 'some string' + somevariable + 'some other bit',
var2: 'some other string'
}
My question is whether including a variable referenced elsewhere in the script can be incorporated within the name:value pair in an object literal?
For reference (and because it is a rather long script), my site is: http://www.hematogones.com/bmbiopsies.html.
The trick with an object literal is that it is evaluated immediately; there is no "easy" way to delay the value you get.
var x = "Alice";
var obj = { greeting: "Hello, " + x + "." };
console.log(obj.greeting); // "Hello, Alice."
x = "Bob";
console.log(obj.greeting); // "Hello, Alice."
obj = { greeting: "Hello, " + x + "." };
console.log(obj.greeting); // "Hello, Bob."
Something to come back to later:
If you really really really need to put a variable into a string literal you define later, you could create an inline function. This way, instead of obj.greeting being a simple string, it is a function that you call (which will look like obj.greeting()).This means that instead of the string value being calculated when you declare your object, it will be declared when you call the function. This is a very powerful feature of Javascript called "closures" and you could spend all day learning about the behaviors, expected and unexpected, that you get with them.
var x = "Alice";
var obj = { greeting: function() { return "Hello, " + x + "." }};
console.log(obj.greeting()); // "Hello, Alice."
x = "Bob";
console.log(obj.greeting()); // "Hello, Bob."
The short answer is yes. Here's a working example:
var myStr = "world";
var obj = { var1: "hello " + myStr };
console.log(obj.var1); // Outputs: hello world
As I mentioned in my comment, your specific example has a couple syntax errors, so perhaps just fixing those will correct your issue.

What does this Javascript do? `new Function("_", "at" , "with(_) {return (" + text + ");}" )`

What is this line doing:
var tfun = new Function("_", "at" , "with(_) {return (" + text + ");}" );
What is the _, at, and with(_)?
I've read this:
http://www.permadi.com/tutorial/jsFunc/index.html
I understand that it's creating a new function object, but am still quite puzzled at what his is supposed to do.
Forgot to put the source:
http://kite.googlecode.com/svn/trunk/kite.js
http://www.terrainformatica.com/2011/03/the-kite-template-engine-for-javascript/
Here a function is being created that will return the value of the key stored in the variable text on the object passed in to tfun().
When a new Function is created in this manner, the first arguments refer to the parameters of the function and the last argument is the function itself. So here we have two parameters named _ and at and then the function body.
with() is a statement saying to conduct the following lines of code within the context of the object specified. So with(_) is saying to conduct the return statement pulling the key text stored in _.
Here's an example:
var text = "name";
var obj = { "name" : "Bob" };
var tfun = new Function("_", "at" , "with(_) {return (" + text + ");}" );
tfun( obj ); // returns "Bob"
I'm not sure why the at parameter is there as it's not being used.
First comes the function arguments, then code code, so it's basically the same as:
var tfun = function(_, at) {
with(_) { return (eval(text)); };
}
So, whatever is in the text variable will be evaluated and returned from the function.
Note: The use of the eval function should generally be avoided, and as creating code dynamically from a variable does the same thing, it should also generally be avoided. There are a few situations where eval is needed, but most of the time it's not, so you should instead try find out the proper way of doing what you are trying to do.

Passing unknown amounts of variables using through a string string and eval and multiple functions and all sorts

In short, I want to use an object literal to allow me to pass a unknown amount of variables in any order to a function. Whilst this is not big deal in theory, in my code, this object literal is passed to a second function called on_change.
on_change works by comparing an element's innerHTML to a string; If it is the same, it sets a timeout to call the function again. If the element's innerHTML is different from the string, then the third parameter is executed, this will either be a function or a string. either way it will execute. I have tested this function plenty and used it for a while now.
However, I cannot seem to get the object literal to flow through the function calls...
var params = { xpos:'false'};
on_change('window_3_cont_buffer','','
if(Window_manager.windows[3].window_cont_buffer.getElementsByTagName(\'content\')[0].getElementsByTagName(\'p\')[0].innerHTML == \'ERROR\'){
alert(Window_manager.windows[3].window_cont_buffer.getElementsByTagName(\'content\')[0].getElementsByTagName(\'p\')[1].innerHTML);
return false;
} else {
Window_manager.windows[3].load_xml(\'location/view.php?location_ID=3\', \'\', ' + params + ' ); }
');
I call this as part of the form submission. After this line, I then call a function to load some content via ajax, which works fine and will trigger the code from the on_change function.
I have tested the load_xml function, it is able to call alert(param.xpos) and get the correct response. I can even added in a check for being undefined so that rest of the times I call load_xml I don't get swamped with alerts.
The load_xml function first sets up the on_change function, then calls the function to load the content to a hidden div. Once the AJAX request has updated that DIV, the on_change function should now call the parse_xml function. This pulls out the information from the xml file. However... The idea of this object literal param is that it can tell this parse_xml function to ignore certain things.
on_change("window_" + this.id + "_cont_buffer", "", "Window_manager.windows[" + this.id + "].parse_xml('" + param + "')");
This is part of load_xml, it works perfectly fine, even with the param bit in there. except, parse_xml does not seem to be able to use that parameter.
I have been able to get it to a point where parse_xml can alert(param) and give [object object] which I would of thought meant that the object literal had been passed through, but when I try and call alert(param.xpos) I get undefined.
I know this is a pig of a problem, and I could get around it by just having the function take a zillion boolean parameters, but its just not a very nice solution.
In effect, what you have is this:
var params = {param: "value"};
foo("bar('one', 'two', 'three');");
...where foo uses eval on that string, something like:
function foo(codestring) {
eval(codestring);
}
...and you're looking for how to embed params in that.
You could do this by serializing the object literal as a string so that when you combine it with the other string, and the total string is evaluated, it gets evaluated. Browsers are slowly getting JSON serialization built in, but for now you want to use jQuery, Prototype, or (if you just want this part) json2.js from Crockford, which offers JSON.stringify for turning objects that can be turned into JSON strings, into JSON strings. So:
var params = {param: "value"};
foo("bar(" + JSON.stringify(params) + ");");
But what you really want to do is refactor so that all of that logic is expressed as code, not code within a string. Then you could pass the literal directly, plus a whole raft of other benefits, including modularization, debugging, etc.
var params = {param: "value"};
function callBar() {
bar(params);
}
foo(callBar);
...changing foo so that it calls a function rather than evaling a string. (eval is to be avoided whenever possible, and to paraphrase the Dalai Lama, it's [almost] always possible.) My sample foo changes to:
function foo(func) {
func();
}
If foo needs to include additional information for bar (and if callBar is set up to handle those extra arguments), it can use Function#call or Function#apply to do that. (Those links are to MDC, but don't worry, they're not Firefox-specific, they've been in the ECMA spec for years and are nearly universally supported.)
You can't put an object inside a string. You would have to serialise the object, add it into the string, then parse it back into a structured object on the other side. The simplest way to do that would be to use JSON (via JSON.stringify or a library fallback for older browsers that don't have it), since JSON evaluates as simple JavaScript.
Note that you wouldn't get the exact same object back, but a new one with the same attributes and properties, and it only works for simple types, so you can't include a function in the object or anything.
However, in any case, passing JavaScript code around in strings is an anti-pattern to be strenuously avoided. Instead use inline functions, and you don't have to worry about what you can and can't put in a string, and you can get rid of all that unreadable wrapping and \-escaping:
var params = {xpos: 'false'};
on_change('window_3_cont_buffer', '', function() {
var w= Window_manager.windows[3];
var ps= w.window_cont_buffer.getElementsByTagName('content')[0].getElementsByTagName('p');
if (ps[0].innerHTML==='ERROR') {
alert(ps[1].innerHTML);
return false;
} else {
w.load_xml('location/view.php?location_ID=3', '', params);
}
});
Some general techniques which may be helpful for you:
// Example of calling function objects given as arguments:
function on_change(foo, callback1, callback2) {
if (foo)
callback1();
else
callback2.call(available_as_this);
}
on_change(foo, function() { your code }, function() { another code });
// Example of function taking arbitrary number of arguments:
function any_params() {
console.log('I got ' + arguments.length + 'arguments!');
console.log('First argument: ' + arguments[0]);
}
any_params('one', 'two', 'three', [], null, {});
See arguments variable and call().
I want to use object literal to allow me to pass a random amount of variables in any order to a function.
Why oh why don't you just create an object which contains the parameters and functions, and pass that around? The receiving function can just test to see if a property of the object is set before trying to use it.

Categories