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');
Related
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");
Hi consider below code
let test={
a:function(){
console.log('Hi')
}
}
Is there a way to invoke property 'a' using a variable ?
This works ,
let test={
a:function(tmp){
console.log(tmp)
}
}
let functn='a';
test[functn]("hello");
but is there anyway to invoke the same like below code :
let test={
a:function(){
console.log('Hi')
}
}
let functn='a("hello")';
test.function;
The actual use case:
This is protractor system test related question,
I have a parent object with css locator [id=1] and the child elements has the locators [id=1]>div , [id=1]>span etc.
so currently parent element is stored as
let parent = element(by.css('[id=1']) ,
child as
let child1= element(by.css('[div]')
So to find all child elements of the parent element the function is :
element(by.css('[id=1']).element(by.css('[div]')
so instead of writing the locator again, i want to achieve:
parent.child1
This works ,
Yes, it does. Do that.
but is there anyway to invoke the same like below code :
No.
let functn=a("hello"); calls the function in the variable a and assigns its return value to functn.
Assuming that doesn't fail (because a isn't declared), then test.function looks at the value of the property named function (which has nothing to do with the variable function you just declared) on the object stored in test and does nothing with it.
Was able to make it work, i am converting everything to string and calling it using eval function:
let test={
a:function(tmp){
document.write(tmp)
}
}
let functn='a("hello")';
eval(Object.keys({test})[0]+'.'+functn)
If in case you are looking for a currying solution:
let test={
a:function(str){
return () => console.log(str)
}
}
let functn=test.a("hello");
functn();
At first you are passing a string which is not neccessary if you dont accept any parameters.
What you can do is using getters.
Documentation: https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Functions/get
let test={
get a (){
console.log('Hi')
}
}
So every time you are trying to access a it will be executed and so will print you 'Hi' in your console.
test.a;
There is and simpler form of the code
// Using Function Arrow
let test = (message => 'Hello World!')()
console.log(test)
// Using factory function
let data = function() {
return {
nome:'Jack'
}
}
console.log(data())
console.log(Object.values(data()))
You could change your approach to one without using eval by taking an array of key and parameter and a function which take the key and parameter and calls the function of the object.
let call = ([key, parameter]) => test[key](parameter),
test = {
a: function(tmp) { console.log(tmp); }
},
parameters = ['a', 'hello'];
call(parameters);
I am a relative beginner in Angular, and I am struggling to understand some source I am reading from the ng-bootstrap project. The source code can be found here.
I am very confused by the code in ngOnInit:
ngOnInit(): void {
const inputValues$ = _do.call(this._valueChanges, value => {
this._userInput = value;
if (this.editable) {
this._onChange(value);
}
});
const results$ = letProto.call(inputValues$, this.ngbTypeahead);
const processedResults$ = _do.call(results$, () => {
if (!this.editable) {
this._onChange(undefined);
}
});
const userInput$ = switchMap.call(this._resubscribeTypeahead, () => processedResults$);
this._subscription = this._subscribeToUserInput(userInput$);
}
What is the point of calling .call(...) on these Observable functions? What kind of behaviour is this trying to achieve? Is this a normal pattern?
I've done a lot of reading/watching about Observables (no pun intended) as part of my Angular education but I have never come across anything like this. Any explanation would be appreciated
My personal opinion is that they were using this for RxJS prior 5.5 which introduced lettable operators. The same style is used internally by Angular. For example: https://github.com/angular/angular/blob/master/packages/router/src/router_preloader.ts#L91.
The reason for this is that by default they would have to patch the Observable class with rxjs/add/operators/XXX. The disadvantage of this is that some 3rd party library is modifying a global object that might unexpectedly cause problems somewhere else in your app. See https://github.com/ReactiveX/rxjs/blob/master/doc/lettable-operators.md#why.
You can see at the beginning of the file that they import each operator separately https://github.com/ng-bootstrap/ng-bootstrap/blob/master/src/typeahead/typeahead.ts#L22-L25.
So by using .call() they can use any operator and still avoid patching the Observable class.
To understand it, first you can have a look at the predefined JavaScript function method "call":
var person = {
firstName:"John",
lastName: "Doe",
fullName: function() {
return this.firstName + " " + this.lastName;
}
}
var myObject = {
firstName:"Mary",
lastName: "Doe",
}
person.fullName.call(myObject); // Will return "Mary Doe"
The reason of calling "call" is to invoke a function in object "person" and pass the context to it "myObject".
Similarly, the reason of this calling "call" below:
const inputValues$ = _do.call(this._valueChanges, value => {
this._userInput = value;
if (this.editable) {
this._onChange(value);
}
});
is providing the context "this._valueChanges", but also provide the function to be called base on that context, that is the second parameter, the anonymous function
value => {
this._userInput = value;
if (this.editable) {
this._onChange(value);
}
}
In the example that you're using:
this._valueChanges is the Input Event Observerable
The _do.call is for doing some side affects whenever the event input happens, then it returns a mirrored Observable of the source Observable (the event observable)
UPDATED
Example code: https://plnkr.co/edit/dJNRNI?p=preview
About the do calling:
You can call it on an Observable like this:
const source = Rx.Observable.of(1,2,3,4,5);
const example = source
.do(val => console.log(`BEFORE MAP: ${val}`))
.map(val => val + 10)
.do(val => console.log(`AFTER MAP: ${val}`));
const subscribe = example.subscribe(val => console.log(val));
In this case you don't have to pass the first parameter as the context "Observable".
But when you call it from its own place like you said, you need to pass the first parameter as the "Observable" that you want to call on. That's the different.
as #Fan Cheung mentioned, if you don't want to call it from its own place, you can do it like:
const inputValues$=this._valueChanges.do(value=>{
this._userInput = value;
if (this.editable) {
this._onChange(value);
}
})
I suppose
const inputValues$ = _do.call(this._valueChanges, value => {
this._userInput = value;
if (this.editable) {
this._onChange(value);
}
});
is equivalent to
const inputValues$=this._valueChanges.do(value=>{
this._userInput = value;
if (this.editable) {
this._onChange(value);
}
})
In my opinion it's not an usual pattern(I think it is the same pattern but written in different fashion) for working with observable. _do() in the code is being used as standalone function take a callback as argument and required to be binded to the scope of the source Observable
https://github.com/ReactiveX/rxjs/blob/master/src/operator/do.ts
This javascript code needs to take in a sentence, extract the first word, and call a function named as that first word, but do nothing if no function is present for that word. But is it not compiling. Any idea why and how to fix it?
i.e. how can I pass the parameters to the function?
refactor = (function () {
const john = function (description) {
console.log(description);
};
const factoring = {
'john': john(description) // <--- description is not defined ---
};
return Object.freeze({
'byName': function (description) { // calls the correct private method if present
let name = description.match(/(\S+)\s/)[1];
if (name in factoring) factoring[name](description);
}
});
}());
desc is not defined. Either define desc and give it a value (const desc = "Some random string") or replace desc with some value ('john': john("Some random string")).
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.