Javascript Scope and local variables - javascript

I'm really not sure if this is possible in Javascript. Here's my function:
var tree = function(name, callback) {
if (this.name) {
this.name.push(name)
print(this.name)
} else {
this.name = []
}
callback()
}
I'd like to use it as follows and print out the hierarchy:
tree("john", function() {
tree("geoff", function() {
tree("peter", function() {
tree("richard", function() {
})
})
})
tree("dave", function() {
})
})
Here's the desired output:
// ['john']
// ['john', 'geoff']
// ['john', 'geoff', 'peter']
// ['john', 'geoff', 'peter', 'richard']
// ['john', 'dave']
but unfortunately I'm getting
// ['john', 'geoff', 'peter', 'richard', 'dave']
for the last function call. Is there a way to get the desired outcome?
Kind regards
Adam Groves

The reason why the last line is printing all the names is because this.names is never removing the names that are being added to it. You're just appending names onto it. So when the function call is made
callback()
with the value
function() {
tree("richard", function() {
})
this.names = ['john', 'geoff', 'peter'] and after the call this.names = ['john', 'geoff', 'peter', 'richard']. So now when you call
tree("dave", function() {
});
this.names is still ['john', 'geoff', 'peter', 'richard'].
Try the following instead, and notice I changed this.name to this.names to make is easier to read.
var tree = function(name, callback) {
if (!this.names) {
this.names = [];
}
this.names.push(name);
print(this.names);
callback();
this.names.pop();
}

I'm not certain what callback does, but you should probably use apply() or call() when you invoke it.
callback.apply( this, arguments );

Related

How to print out only object values that are true?

I am learning Javascript, and am making a simple user verification object. I need a function that will console.log only users who have "isVerified" set to true.
I have tried numerous ways including loops and if statements. The function name is "showVerified" below.
var person = {
info: [],
displayPerson: function() {
console.log('People', this.info);
},
addPerson: function(age, firstName, lastName) {
this.info.push({
age: age,
firstName: firstName,
lastName: lastName,
isVerified: false
});
this.displayPerson();
},
deletePerson: function(position) {
this.info.splice(position, 1);
this.displayPerson();
},
verifyPerson: function(position) {
this.info[position].isVerified = true;
this.info[position].firstName = this.info[position].firstName.toUpperCase();
this.displayPerson();
},
showVerified: function() {
for (var key in this.info) {
if (this.info.isVerified = true) {
console.log(this.info.isVerified[key]);
}
}
}
}
What I want to happen when running showVerified on my person object, is for it to print out ONLY the age, first name, and last name of any person who is verified.
I suggest trying to change the way you're naming your properties, so the code is a bit clearer.
Try
showVerified: function() {
this.info.filter(x => x.isVerified).forEach(v => console.log(v))
}
if you don't want to use filter, you can try this as well
this.info.forEach(person => {
if(person.isVerified){
console.log(person);
}
});

How to run a variable where a function that just made console.log was saved?

function a() {
console.log(5);
return;
}
var x = a;
x();
// Line 2 Output: 5 // compiler output read function a ()
// Line 6 Output: undefined // result of execution x ()
var person = {
first: 'John',
last: 'Smith',
full: function() {
console.log(this.first);
}
};
Output: 'john'
var person = {
first: 'John',
last: 'Smith',
full: function() {
console.log(this.first);
}
};
person.first = 'pedro';
person.full()
// Line 5 Output: 'pedro'
// Line 9 Output: undefined // result of execution x ()
Does anyone explain to me what's happening? Because Output is not:
'john' -> here gives 'pedro' by the seen, the person.first statement executes first that the console.log (this.first)
'pedro' -> here gives undefined, it's the same case as above
Your question worded strangely, but I will try to answer as best as I can.
var person = {
first: 'John',
last: 'Smith',
full: function() {
console.log(this.first);
}
};
When a function is defined without a return, undefined will always be returned back to the caller. JavaScript functions always return something.
You would have to state a return if you wanted to use that variable from the function.
You can grab the object's variables through namespacing, in your example, you could do: person.first, person.last.
And if you wanted the function to return something, you would declare person like so:
var person = {
first: 'John',
last: 'Smith',
full: function() {
return this.first + " " + this.last;
}
};

ES6 Default Parameters in nested objects

I want to have a function with default parameters inside nested objects, and I want to be able to call it either f() or specifying only individual parameters.
// A function with nested objects with default parameters:
function f({ a = 1, callback = ({ name, param } = { name: "qwe", param: 123 }) } = {}) {
console.log("a:", a);
console.log("callback:", callback);
}
// And I want to run it like this:
f();
f({ callback: { params: "456" } });
// But 'callback.name' becomes undefined.
When destructuring is mixed with default parameters, I admit the code is hard to read and write (especially when there are nested objects...).
But I think you are trying to do that:
function f({callback: {name = "cbFunction", params = "123"} = {}} = {}) {
console.log(name);
console.log(params);
}
f();
f({callback: {params: '789'}});
I found none of the answers here to be what he wanted. But it IS actually possible in a somewhat sexy way by doing this:
(EDIT: Simplified syntax and also show how to add default values for subobjects)
function f({
a = 1,
callback = {}
} = {}) {
callback = { // default values
name: "cbFunction",
params: "123",
...callback // overwrites it with given values
}
// do the same for any subobjects
callback.subObject = {
arg1: 'hi',
arg2: 'hello',
...callback.subObject
}
console.log("a:", a)
console.log("callback:", callback)
}
f()
f({a: 2, callback: {params: '789', subObject: {arg2: 'goodbye'}}})
Turned out to call it like this solves the problem, but is it the best way?
function f({
a = 1,
callback = ({
name,
param
} = {
name: "qwe",
param: 123
})
} = {}) {
console.log("a:", a);
console.log("callback:", callback);
}
f();
f({ callback: { name, params: "456" } });
Answer by #Badacadabra is nearly correct but missing the other top level parameter specified in the question.
function f({a = 1, callback: {name = "qwe", params = "123"} = {}} = {}) {
console.log(a);
console.log(name);
console.log(params);
}
However note that within the function body the properties of callback are addressed without the containing object. You could reconstitute them into such an object if you wanted with the line:
const callback = { name, params }
Either way, from the invocation point this works to fill in all missing values from all levels such as:
f({a: 2})
f({a: 2, callback: { name: "abc"}})
f({a: 2, callback: { params: "456" }})
etc.
EDIT
In response to Joakim's comment:
TotalAMD also said in a comment that "I want to use several nested objects with same fields name". So if he tries that approach with callback1 and callback2 as arguments then he would have to use different field names in them.
I missed that original requirement. One way to maintain the desired, duplicated nested names within the function interface would be to alias them within the scope of the function, as follows:
function f({
a = 1,
callback1: {name: name1 = "abc", params: params1 = "123"} = {},
callback2: {name: name2 = "def", params: params2 = "456"} = {},
} = {}) {
console.log(a);
console.log(name1);
console.log(params1);
console.log(name2);
console.log(params2);
}
You can then call the function with the designed interface and expected results:
f ({ callback1: { name: "One" }, callback2: { name: "Two" } })
Caveat: Whilst technically possible and potentially useful, this could get messy at deeper nesting levels. It might then be worth looking for an alternative interface design with less indirection.

JavaScript - ForEach

I have a JavaScript array of objects. Something like this:
var people = [
{ id:1, firstName: 'Joe', lastName: 'Smith' },
{ id:2, firstName: 'Bill', lastName: 'Smith' }
];
I am iterating through the people using forEach. Here is my code:
function doSomething() {
people.forEach(function(person, self) {
self.helpPerson(person);
}, this);
}
function helpPerson(person) {
alert('Welcome ' + person.firstName);
}
I am trying to call helpPerson from within the forEach loop. However, when I attempt to call it, I get an error that says:
TypeError: self.helpPerson is not a function
If I add console.log(self);, "0" gets printed to the console window. This implies that I'm not passing in my parameter correctly. Or, I'm misunderstanding closures (just when I thought I fully understood it :)).
So, why doesn't self exit?
You don't need to invoke helpPerson with a this, self, or any other context. Just invoke it directly:
var people = [
{ id:1, firstName: 'Joe', lastName: 'Smith' },
{ id:2, firstName: 'Bill', lastName: 'Smith' }
];
function doSomething() {
people.forEach(function(person) {
helpPerson(person);
});
}
function helpPerson(person) {
alert('Welcome ' + person.firstName);
}
When you log self to the console you are seeing "0" because it is printing the index of the loop iteration.
See the documentation for forEach to see what callbacks are passed to it's forEach function.
Typically, self is used to capture a context in a closure (var self = this;). Please see the related links to this question because that is a very important concept.
helpPerson is a global variable, not a property of the array.
self.helpPerson(person); should be helpPerson(person);
forEach passes two arguments to the callback: the item being iterated and its index. That's why console is logging 0.
You are expecting this to pass as an argument, when it's actually more magical than that. You can use this inside the callback and it will use the context of whatever this you passed as an argument to the forEach
function doSomething() {
people.forEach(function(person, index) {
this.helpPerson(person); //Or for brevity, you can just remove `this` here
}, this);
}
function helpPerson(person) {
alert('Welcome ' + person.firstName);
}
forEach takes 2 parameters : a function(val, index, arr) and a this binding argument .
people.forEach(function(person, self) {
self.helpPerson(person); // self here would be array index number
}, this);
the way you've defined helpPerson() you can call it directly like helpPerson(person);
Please see comments
var people = [
{ id:1, firstName: 'Joe', lastName: 'Smith' },
{ id:2, firstName: 'Bill', lastName: 'Smith' }
];
function doSomething() {
people.forEach(function(person) { // no parameter for index required
helpPerson(person); // just call the function
}); // no other parameter is required
}
function helpPerson(person) {
alert('Welcome ' + person.firstName);
}
doSomething();

Using object variable within function. JavaScript

I have recently started to learn JavaScript and would like to know if it is possible to use a object variable in a function directly within the same object. Here is my code so far.
var user = {
name: 'Example',
age: 687,
address: {
firstLine: '20',
secondLine: 'St Fake',
thirdLine: 'Fakeland'
},
logName: function(inputName, inputAge){
console.log(user.name);
console.log(user.age);
console.log(inputAge);
console.log(inputName);
}
};
user.logName('Richard', 20);
How is it possible to link to the name and age variables of user in the function without needing to prefix the object name onto the variable?
In most cases, you can just use the this keyword to get the object on which your function was called as a method upon. In your example:
var user = {
name: 'Example',
age: 687,
address: {
firstLine: '20',
secondLine: 'St Fake',
thirdLine: 'Fakeland'
},
logName: function(inputName, inputAge) {
console.log(this.name);
// ^^^^
console.log(this.age);
// ^^^^
console.log(inputAge);
console.log(inputName);
}
};
user.logName('Richard', 20); // method call on `user`,
// so `this` will become the `user` in the function
Welcome to the "this" key word!
Just reference it by this.value
You can use the this keyword . You can better understand this keyword using this article
The code will be like this
var user = {
name: 'Example',
age: 687,
address: {
firstLine: '20',
secondLine: 'St Fake',
thirdLine: 'Fakeland'
},
logName: function (inputName, inputAge) {
console.log(this.name);
console.log(this.age);
console.log(inputAge);
console.log(inputName);
}
};
user.logName('Richard', 20);

Categories