I know you can use template literals to supply the first parameter of a method, for instance:
const f = x => "hello ," + x;
f`world` // returns 'hello, world'
So I can somehow understand why this code works:
String.raw`bla bla`
However, I can't seem to understand why the same method call with parenthesis throws an error:
String.raw(`bla bla`)
throws: Uncaught TypeError: Cannot convert undefined or null to object
My questions are:
Why exactly the first snippet works? why can I replace parenthesis with template literals in a method call?
Why String.raw only works when it's being called this way?
... I can't seem to understand why the same method call with parenthesis throws an error
It's not the same method call.
This:
String.raw`bla blah`
...calls raw passing in the template.
But this:
String.raw(`bla blah`)
...processes the template, creating a string, and then calls raw with that string. Exactly as though you'd written:
const str = `bla blah`;
String.raw(str);
...but without the constant.
That's not the same thing at all.
Why exactly the first snippet works?
Because that's how tagged template literals work. They're their own thing.
Why String.raw only works when it's being called this way?
Because it's designed to be run as a tag function, but you're calling it like a non-tag function and not passing it the information a tag function receives when called as a tag function.
It might help if you see what a tag function receives:
function foo(strings, ...tokenValues) {
console.log("strings: ", JSON.stringify(strings));
console.log("strings.raw: ", JSON.stringify(strings.raw));
console.log("Token values (" + tokenValues.length + "):");
tokenValues.forEach((val, index) => {
console.log(`${index}: ${typeof val}: ${JSON.stringify(val)}`);
});
}
const token = "tokenValue";
const obj = {
foo: "bar",
num: Math.random()
};
foo`bla \nbla ${token} bla ${obj} done`;
Note how the function receives an array with the non-token parts of the template (the "strings"), followed by arguments with the values of each token. Also note that those values are the actual values, not strings of the values.
More about template literals and tag functions:
MDN
Exploring ES6: Template literals
Or if you want to call String.raw as a function you need to call it like this
String.raw({raw: `xyx`})
As mentioned in https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/raw documentation
Two ways of calling String.raw
String.raw(callSite, ...substitutions)
String.raw`templateString`
Related
What is wrong with alert({s[prop]}) but fine with this placeholder={s[prop]}
It says I am missing a ',' after 's' and ':' after ']'
In React, the { }s around attributes are essentially expression delimiters - they indicate that what follows between the brackets is an expression. So, if you have const str = 'foobar', then:
placeholder={str}
evaluates to
placeholder='foobar'
But, in alert, you're not writing JSX - you're writing plain JS. When in an expression context, { indicates the start of an object literal. But the following is not a valid object literal:
const obj = {
s[prop]
}
because objects require keys and values (usually). Perhaps you wanted to do
alert(s[prop])
The only time an object literal doesn't require a value is when you're using the shorthand syntax, when you have a variable in the current scope and want to define an object with a property with the same name as the variable, and the same value as a variable, eg:
const str = 'foobar';
const obj = { str };
This results in an object like { str: 'foobar' }.
In any other situation, you'll need to define the property name in addition to the value, eg
{ somePropertyName: s[prop] }
alert(s['use your key'])
Please use in this way. Avoid using {} in alert(), If you use you will see [object Object] instead of actual dynamic content.
try with
alert(s[prop])
In JSX to print any JS variable it must be wrap inside curly braces, but not required when using in JS, alert is the JavaScript function.
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"]
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"]
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"]
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.