Let's say I have these global variables:
var name, car;
Then I declare some values:
const languageStrings = {
WELCOME_MESSAGE: 'Welcome #name, you have a #car',
NAME_USER: "#name",
CAR_USER: '#car'
};
And then I need to assign it to a function.
For example:
if (firstTime){
welcomeMsg = WELCOME_MESSAGE;
}
Now, I have two questions:
1) How would I insert a variable inside of a string so it is dynamically updated when the value pair is called?
2) How would I do the same using JSON?
You can't have a JSON structure or string "automatically" update when some variable changes. There are other ways to do this type of templating, though. You could create a function to create a welcome message when you need one:
function getWelcomeMessage(name, car) {
return "Welcome "+name+", you have a "+car;
}
Then you'd do something like welcomeMsg = getWelcomeMessage("Joe", "Camry");
If you don't want to write a function for every template (i.e. if you have lots of them), then you could use String.replace like this:
function applyTemplate(template, params) {
return template.replace(/#(\w+)/g, (m, name) => params[name]);
}
Example usage:
function applyTemplate(template, params) {
return template.replace(/#(\w+)/g, (m, name) => params[name]);
}
const WELCOME_TEMPLATE = "Welcome #name, you have a #car";
var name = "Joe";
var car = "Camry";
var welcomeMessage = applyTemplate(WELCOME_TEMPLATE, {name, car});
console.log(welcomeMessage);
You would have to make a function that returns the value of the variable.
In your case:
welcomeMessage = function(){
return WELCOME_MESSAGE
}
and you would reference the variable with:
welcomeMessage()
So, you'd be assigning a variable as a function that returns the current value of the other variable. You get the value by calling your variable as a function.
String in JavaScript is primitive type, it's passed by value. So once a variable is assigned with a string, it will never change until you explicitly assign another value (or object reference) to it.
However, you can ask object type for help, which could make your data reactively (or dynamically, if you prefer this word) update under certain conditions.
var languageStrings = {
WELCOME_MESSAGE: '',
NAME_USER: '',
CAR_USER: ''
}
Object.defineProperty(languageStrings, 'name', {
get: function (name) {
return this.NAME_USER
},
set: function (name) {
this.NAME_USER = name
this.WELCOME_MESSAGE = `Welcome ${this.name}, you have a ${this.car}.`
}
})
Object.defineProperty(languageStrings, 'car', {
get: function (car) {
return this.CAR_USER
},
set: function (car) {
this.CAR_USER = car
this.WELCOME_MESSAGE = `Welcome ${this.name}, you have a ${this.car}.`
}
})
Now, whenever you change languageStrings.name or languageStrings.car, all three strings you want will automatically adopt the new value you just set:
languageStrings.name = 'Leo'
languageStrings.car = 'BMW'
for (let key in languageStrings) {
console.log(`${key}: ${languageStrings[key]}`)
}
// WELCOME_MESSAGE: Welcome Leo, you have a BMW.
// NAME_USER: Leo
// CAR_USER: BMW
You don't have to manually call applyTemplate all the time, like in #qxz's answer (I'm not saying his wrong, though).
Also, please notice that even name and car are not enumerable - they will not be accessed with for in, for of, or Object.keys! This is great, your implementation details are concealed, no worries or confusions to other developers who use your code.
In fact, such reactive model is widely used in front-end MV* frameworks nowadays, e.g. Vue.
Regarding your second question, I didn't get it honestly. Just JSON.parse then it's all ordinary JavaScript, isn't it?
the answer to your question on how to insert variables inside a string is:
WELCOME_MESSAGE: 'Welcome ' + name + ', you have a ' + car,
or before defining:
function mesg(name, car){
return "Welcome" + name + ", you have a " + car;
}
mesg(bob, BMW);
in this case, the name and car is defined after.
Related
I was learning from an ES6 essential course and trying default parameters and rest operator for functions.
I have defined a function sayHi as below with default parameters and then rest operator which does not gives the desired output.
const sayHi = (greetings, ...names) => {
names.forEach(item => {
console.log(`${greetings} ${item}`);
});
}
sayHi('Hi', 'Ahsan', 'Awais', 'Haseeb');
The above snippet works as desired. but when I tried to set a default parameter value for greetings variable it works but gives unwanted result i.e. value 'Ahsan' is taken by the greetings variable.
const sayHi = (greetings = ' Hi', ...names) => {
names.forEach(item => {
console.log(`${greetings} ${item}`);
});
}
sayHi('Ahsan', 'Awais', 'Haseeb');
Is there a way I can set default parameters in function like above before rest operator?
You can't, no. The rest parameter only picks up the "rest" of the parameters not consumed by any previous formal parameters, so greeting will always receive the first argument's value.
Separately, since both the names and the greeting have the same type, you can't provide a default at all if you want to accept the names that way.
A couple of options for you:
A curried function
You could have a function that accepts the greeting and returns a function that uses it with whatever you pass it:
const greet = (greeting = "Hi") => (...names) => {
for (const name of names) {
console.log(`${greeting} ${name}`);
}
};
greet()("Ahsan", "Awais", "Haseeb");
greet("Hello")("Ahsan", "Awais", "Haseeb");
Note how we called that:
greet()("Ahsan", "Awais", "Haseeb");
greet() creates the function using the default greeting. Then we call that function by using ("Ahsan", "Awais", "Haseeb") on it. greet("Hello") creates a function that uses the greeting "Hello" (and then we call it).
(I also took the liberty of using for..of rather than forEach, but it's a matter of style.)
Take names as an array
Another option is to accept the names as an array. That way, we can tell inside the function whether we got a greeting or not:
const greet = (greeting, names) => {
if (Array.isArray(greeting)) {
names = greeting;
greeting = "Hi";
}
for (const name of names) {
console.log(`${greeting} ${name}`);
}
};
greet(["Ahsan", "Awais", "Haseeb"]);
greet("Hello", ["Ahsan", "Awais", "Haseeb"]);
you just have to pass undefined in the 1st parameter to skip optional arg
think of it like this greetings = typeof greetings != 'undefined' ? greetings : "hi" which means that check the value of greetings and if it's undefined (not provided) use the default value
edit: here is the code snippet
const sayHi = (greetings = ' Hi', ...names) => {
names.forEach(item => {
console.log(`${greetings} ${item}`);
});
}
sayHi(undefined, 'Ahsan', 'Awais', 'Haseeb');
Let's assume that I've the following object:
let filters = {
brands: { ... },
price: { ... },
sizes: { ... },
...
}
The properties of the filters object will be set by the users. Which means sometimes the filters object may contain just brands, sometimes it may contain brands & price and so on.
I've written the following function to extract a specific property from the filters object:
let extractProperty = (propertyName) => {
({ propertyName, ...rest } = filters); // <-- propertyName isn't working here
console.log(propertyName);
}
extractProperty("brands");
If I invoke the above function, the console displays undefined.
Can anyone please point me out what I'm missing here?
CodePen Link
Note:
I've already resolved this issue using lodash.omit method. But I'm still curious to know why function parameter value isn't working in object-destructuring.
Not Duplicate:
This question is about passing default value
That code is looking for a property called propertyName, literally. To use the value in propertyName as the property name, you need to use computed notation, and you'll need to specify where to put the property's value. For instance, to put it in an existing example variable:
let extractProperty = (propertyName) => {
({ [propertyName]: example, ...rest } = filters);
// ^−−−−−−−−−−−−^^^^^^^^^^
console.log(example);
};
extractProperty("brands");
Your code is written assuming that rest already exists, but I suspect you really want to declare it locally, along with the variable to receive the property value:
let extractProperty = (propertyName) => {
const { [propertyName]: example, ...rest } = filters;
console.log(example);
};
extractProperty("brands");
Without const, let (or var, but var is deprecated), unless you have rest declared in an enclosing scope, that code will either fail with a ReferenceError (in strict mode) or fall prey to what I call The Horror of Implicit Globals (in loose mode), automatically creating a global variable.
why use destructuring here when you just want to get a property?
let filters = {
brands: { value:'b' },
price: { value:'p' },
sizes: { value:'s' },
}
let extractProperty = propertyName =>
console.log(filters[propertyName])
extractProperty("brands");
I have an object, that takes input from an API call to fill it up.
let MyDog = {
Name: 'Dog',
}
let arrayFunctions;
fetchDogsFunctions(dogAPIUrl).then(res => {
//results is an array that has a list of functions the dog has, like //getStats(), or walkDog()
arrayFunctions = res;
})
Now I want to map through the array results and call the function on my dog like...
arrayFunctions.map(item => {
await MyDog.item(//Params)
})
How can I do this??
Where MyDog is set up from a file, and then depending on the array functions, for each function it programmatically fills in the call to the new function like MyDog.item where item is a variable in an array called "Walk()"
I assumed how your data might have structured. Pls take a look below and you might get the idea of how to call functions dynamically using "await"
const MyDog = {
funA(a) { return Promise.resolve('Function A called with parameter ' + a) }
, funB(b) { return Promise.resolve('Function B called with parameter ' + b) }
}
const arrayFunctions = ['funA', 'funB']
Promise.all(arrayFunctions.map(async (item, i) =>
await MyDog[item](++i))
)
.then(console.log)
I can't see any property name "item" in your MyDog Object
The answer is to do :
MyDog[item]()
within the loop.
I was asking the correct syntax to perform this operation
I have a problem where I have a service which exposes a string. There is a function on this service which updates the value of the string. The service internally knows that the value has changed, however, externally the value NEVER updates.
If I nest the string inside an object then it all works, but I don't really want to nest it.
Can anyone explain why this is happening? It feels like it should work and feels like I am missing something basic.
Service:
myApp.service('neverChanges', function () {
var id = 'Hello';
var changeId = function () {
console.log('pre change:' + id);
id = 'World';
console.log('post change:' + id);
};
return {
id: id,
changeId: changeId
};
});
Controller:
myApp.controller('Controller1', ['neverChanges', function (neverChanges) {
this.idValue = function() {
return neverChanges.id;
}
this.clickHandler = function () {
console.log('Trust me, I did fire...');
neverChanges.changeId();
console.log('external post change:' + neverChanges.id);
};
}]);
Markup:
<div ng-app="myApp">
<div ng-controller="Controller1 as vm">
<h3>This will never change:</h3>
<button ng-click="vm.clickHandler()">Click Me!</button>
<p>Values:</p>
<p>id: {{vm.idValue()}}</p>
</div>
Fiddle showing the two scenarios: http://jsfiddle.net/KyleMuir/2nhoc2rz/
You have to use this:
var changeId = function () {
console.log('pre change:' + id);
this.id = 'World';
console.log('post change:' + id);
};
The problem is that you have local variable id: var id = 'Hello';
Later in the function you copy the value of this local variable into an object that you return:
return {
id: id,
changeId: changeId
};
So from here on the returned object has a propery id which is COPY of your original id variable, and your changeId function just changes your local variable, but of course not the copy.
To prevent that you would need to keep a reference to the object that you return. That could e.g. look like this:
var result = {id:'Hello'};
result.changeId = function () {
console.log('pre change:' + result.id);
result.id = 'World';
console.log('post change:' + result.id);
};
return result;
See working version: http://jsfiddle.net/y4mxazqh/
This way you get rid of the local variable id and you can change the object that you returned.
Of course the return also creates a copy of the reference to your local variable result. But since both the returned reference and your local variable point to the same object, you can change the content of that object and after that both references still reference an object that's id has now changed.
EDIT:
Essentially originof's answer solves the same problem with a different approach: Because you call vm.clickHandler() the function clickHandler() gets the this set to vm and vm in turn is the object that you returned. Thus you can access the returned object. However be aware, that if you execute code like this:
var func = vm.clickHandler();
func();
this would not be the same. In this case this would not get set to vm and you are lost. You should be aware of this situation when you choose the this-based solution.
You pass function in another object and function scope is changed
Try:
this.id = "World"
instead of
id = "World"
Hi guys I am writing some code using the object literal pattern, I have function that returns a value:
'currentLocation': function() {
var cL = 0;
return cL;
},
I then need to update the variable 'cL' from another function like this:
teamStatus.currentLocation() = teamStatus.currentLocation() + teamStatus.scrollDistance();
This part is part of another function - however I get an error back stating: invalid assignment left-hand side
I am guessing I can not update the variable in this way, could anyone suggest a better method or point me in the right direction.
Any help would be greatly appreciated.
Going to add more code to highlight what I am trying to do:
'currentLocation': function() {
var cL = 0;
return cL;
},
'increaseTable': function() {
if (teamStatus.currentLocation() <= teamStatus.teamStatusTableHeight() ) {
teamStatus.currentLocation = teamStatus.currentLocation() + teamStatus.scrollDistance();
$("#tableTrackActual").animate({scrollTop: (teamStatus.currentLocation)});
$("#tableMembers").animate({scrollTop: (teamStatus.currentLocation) });
//console.log(teamStatus.currentLocation());
teamStatus.buttonRevealer();
}
}
As you can see increaseTable should update the value of currentLocation - help this sheds more light on what I am trying to achieve.
You're writing teamStatus.currentLocation() =, which calls the function teamStatus.currentLocation and tries to assign to the return value. That isn't valid. You want just teamStatus.currentLocation = — no function call.
The variable inside your function is completely private to that function (and any functions defined within it). If you need to create a number of functions that share a set of private variables, you can do that with a closure. For instance:
var Thing = (function() {
var thingWideData;
function getData() {
return thingWideData;
}
function setData(newData) {
thingWideData = newData;
}
return {
getData: getData,
setData: setData
};
})();
What that does is create a Thing object which has getData and setData functions available for it, which get and set the completely private thingWideData variable contained by the anonymous closure. More about this pattern here and here, although the latter of those is more about private methods than private data.
What your code produces is:
0 = 0 + <some number>
Which variable do you want to update? cL? You are declaring it in the function, you cannot assign a value to it from outside. Depending on the rest of your code, you might be better off with getters and setters:
var object = {
_cL = 0,
get currentLocation() {
return this._cL;
},
set currentLocation(value) {
this._cL = value;
}
}
then you can do:
teamStatus.currentLocation = teamStatus.currentLocation + teamStatus.scrollDistance();
Update:
Regarding IE: If currentLocation should actually be just a number, it might be sufficient to just declare it as property:
var obj = {
currentLocation: 0
}