Pass variable into function to be defined [duplicate] - javascript

This question already has answers here:
What's the difference between passing by reference vs. passing by value?
(18 answers)
Closed last year.
EDIT 2: Why has this very specific question been marked as a duplicate of this very conceptual one: What's the difference between passing by reference vs. passing by value?. If someone else had the same question as me, they wouldn't end up with this one on Google by searching for it and - even if they did - it wouldn't answer their question.
EDIT: Thanks everyone for your help so far. Pretty difficult stuff to understand from my point of view. The reason I'm doing this is that I've set up a function which I'm calling multiple times and in which I define a variable (a unique one with each call). I need to be able to refer back to each unique variable afterwards. Here is my actual attempted code below. What would the right way to do this be?
let newSeq1
let newSeq2
function sequenceClip(sample, length, sequenceVariable) {
let currentPosition = Tone.Transport.position
let whenToStart
if (currentPosition === '0:0:0') {
whenToStart = '0:0:0'
} else {
const barToStartOn = +currentPosition.slice(0, currentPosition.indexOf(':'))
whenToStart = `${barToStartOn + 1}:0:0`
}
sequenceVariable = new Tone.Sequence((time, note) => {
sampler.triggerAttackRelease(note, '4m', time)
}, [sample], length).start(whenToStart);
}
loop1.addEventListener('mousedown', () => {
sequenceClip("C3", '1m', newSeq1)
})
loop2.addEventListener('mousedown', () => {
sequenceClip("C#3", '4m', newSeq2)
})
How do I pass a variable into a function in Javascript to be assigned a value. E.g.:
Why does the variable not get assigned the value 5? And what's the way around this?
let a
function defineVariable(var2beDefined) {
var2beDefined = 5
}
defineVariable(a)
console.log(a === 5)

You would typically write an initializer function that returns a value and assign that return value to the relevant global variable. For example:
let newSeq1
let newSeq2
function sequenceClip(sample, length, sequenceVariable) {
let currentPosition = Tone.Transport.position
let whenToStart
if (currentPosition === '0:0:0') {
whenToStart = '0:0:0'
} else {
const barToStartOn = +currentPosition.slice(0, currentPosition.indexOf(':'))
whenToStart = `${barToStartOn + 1}:0:0`
}
return new Tone.Sequence((time, note) => {
sampler.triggerAttackRelease(note, '4m', time)
}, [sample], length).start(whenToStart);
}
loop1.addEventListener('mousedown', () => {
newSeq1 = sequenceClip("C3", '1m')
})
loop2.addEventListener('mousedown', () => {
newSeq2 = sequenceClip("C#3", '4m')
})
Note that both newSeq1 and newSeq2 will be undefined until the first mousedown/mouseup events.

Reassigning an identifier, by itself, never has any side-effects (in most circumstances) - the only change resulting from someIdentifier = someNewValue will be when other parts of the code reference that same someIdentifier.
If you want to pass in something to be assigned to, pass in a function which, when called, assigns to the outer variable.
let a;
function defineVariable(callback) {
callback(5);
}
defineVariable(newVal => a = newVal);
console.log(a === 5)
The only time that assigning to an identifier will have side effects is if:
Inside a function with a simple argument list, you reassign one of the parameters (in which case the arguments object will be changed as well)
You're using ES6 modules, and you reassign an identifier that's being exported, in which case other modules that import it will see the change as well

Related

How to get returned values from my listener's callback ES6 way

I made an input that let me filter a table of softwares.
<input type="text" id="softwares-search" class="form-control" aria-label="Input de recherche" aria-describedby="softwares-search">
Then in javascript my filter work well if I console.log(....)
But when I replace it with a return, nothing is returned. I think it is due to my var affectation through the event listener :
const maxwell = () => {
search = document.querySelector('#softwares-search').value;
return softwares.filter(row => row.name.includes(search) || row.description.includes(search));
}
const softwaresSearch = document.querySelector('#softwares-search');
if (softwaresSearch) {
var results = softwaresSearch.addEventListener('keyup', maxwell)
console.log(results);
}
Thank all
EDIT 1 :
I was so angry, so blind, I had S#!t in my eyes, no need to use a global :(
const softwaresSearch = document.getElementById('softwares-search');
if (softwaresSearch) {
softwaresSearch.addEventListener('keyup', (e) => {
search = document.getElementById('softwares-search').value;
var filtredSoftwares = softwares.filter(e => e.name.includes(search) || e.description.includes(search) );
renderTable(filtredSoftwares);
});
}
const renderTable = (softwares) => {
Object.values(softwares).forEach(value=>{
console.log(value);
});
// Todo build HTML table
}
Instead of returning I think you just need to replace the current array like this
const maxwell = () => {
search = document.querySelector('#softwares-search').value;
softwares = softwares.filter(row => row.name.includes(search) || row.description.includes(search));
}
And results is not needed:
const softwaresSearch = document.querySelector('#softwares-search');
if (softwaresSearch) {
softwaresSearch.addEventListener('keyup', maxwell)
}
As far as I know, softwareSearch.addEventListener won't return anything, since that is an event listener, and does not return any value. It simply executes the function passed in the 2nd parameter. You could try doing this instead
softwaresSearch.addEventListener('keyup', () => {
var results = maxwell();
console.log(results);
});
What this would do is that, it would call your maxwell function when the keyup event, since that is what it looks you are trying to do.
Please share all relevant code before posting a question, this code includes the variable "softwares" that exist outside what is visible to us.
Additionally, there are some issues with your code.
I don't understand your naming of the maxwell function. You should name functions as verbs, not anything else. A function is a machine that is doing something, and possibly returning something. It should be named to what it is doing.
On the second line, you say "search = ... ", but you didn't declare it as a variable.
You are returning something based on a value that isn't validated ('search' can be either undefined or a string value in this case), hence, your return will most likely just return undefined and not any value at all.
Your function can possibly not return anything at all since you are returning something within your if-statement. You can use a closure to always return something.
I would also suggest passing a search string as a variable to your function that should return a list based on the search query. Getting in the habit of short, concise functions with expected inputs/outputs, will make your code more readable and less error-prone and less likely to produce unwanted side-effects.
I don't know the rest of your code, but I don't recommend assigning variables in the global scope. Your "maxwell", "softwareSearch" variables both exist in the global space, unless you have wrapped them in another function block already (such as jquerys $(document).ready(() => { ...everything here is scoped })
You are getting the same element in two different places in your code.
Here is an updated code sample, but I can't test it since I don't know the rest of your code.
/*
* Executing the whole thing in this IIFE will make all variables declared inside here scoped to this block only,
* thus they can't interfere with other code you may write
*/
(() => {
const listOfSoftwares = softwares; // --- get your softwares variable here somehow, I don't know where "software" comes from.
// input element
const search = document.querySelector('#softwares-search');
/**
* Filter search results
* #param {string} query Search query
* #returns {Array} The results array
*/
const filterSoftwareSearchResults = (query) => {
let results = [];
results = listOfSoftwares.filter(software => software.description.includes(query) || software.title.includes(query))
// Verify
console.log(results);
// Should return array of results, even if empty
return results;
}
if (search) {
search.addEventListener('keyup', () => {
filterSoftwareSearchResults(search.value)
})
}
})()
The addEventListener function always returns undefined, so your results variable is undefined.
Returning from the callback function (maxwell) is also of no use.
You either need to do something with the data inside of your callback, or maybe pass the data to a global variable.

Javascript getter/setter scope access [duplicate]

This question already has an answer here:
Object.assign getters and setters in constructor
(1 answer)
Closed 3 years ago.
I'm trying to use JS with classic prototypical inheritance, instead of the new ES6 class model, mainly to be able to access the closure scope.
In the example bellow, I want to expose a variable current declared inside the function Counter trough this object created by the new operator.
function Counter(start, stop) {
var current = start;
function inc() { if (current < stop) return current++ }
function getCurrent() { return current }
Object.assign(this, { inc, getCurrent,
get current() { return current }, set current(value) { current = value }
})
}
counter = new Counter(0, 3)
while ((v = counter.inc()) !== undefined)
console.log(counter.getCurrent(), counter.current)
I expected the following output:
1 1
2 2
3 3
Because counter.current & counter.getCurrent() should both return the same result. But instead, I'm receiving
1 0
2 0
3 0
If I replace that Object.assign(...) with the code bellow, it works as expected.
Object.assign(inc, getCurrent })
Object.defineProperty(Counter.prototype, 'current',
{ get: () => { return current }, set: (value) => { current = value }
I could use this model, (and currently using), but I would like to use the former, because is simpler and less verbose. It seems that there are 2 different scopes here.
I tested with node 10, Chrome 73 & Firefox 68, and received the same results.
What I'm missing here?
In the example above, I tried to be as terser as I could. But to be more precise and better illustrate the point, follow a more complete test, with some commented things I've tried.
Here, I renamed the variable current to _current in order to avoid confusion with the current property, but that shouldn't be obligatory.
function Counter(start, stop) {
var _current = start;
function inc() { if (_current < stop) return _current++ }
function getCurrent() { return _current }
// Object.assign(this.constructor.prototype,
// Object.assign(this.__proto__,
// Object.assign(Counter.prototype, {
Object.assign(this, {inc, getCurrent,
get current() { return _current }, set current(value) { _current = value }
// get current() { return current } // supposed to be read-only, but not
})
// This works as expected
// Object.defineProperty(Counter.prototype, 'current',
// { get: () => { return _current }, set: (value) => { _current = value } })
}
counter = new Counter(0, 3)
while ((v = counter.inc()) !== undefined) {
console.log(counter.getCurrent(), counter.current)
counter.current -= 0.5
}
the output of the code above is:
1 0
2 -0.5
3 -1
Where that counter.current -= 0.5 is storing its value?
When your code calls Object.assign(), it's passing in an object that has a getter and setter for current. Thus the Object.assign() process will itself invoke that getter to get the value for the property "current" while it's copying the property values to the new object. Thus, the Counter object ends up without the getter and setter, which explains your results. Its "counter" property is just a simple property with a copy of the value of the local counter variable at the time the constructor code ran.
Object.assign() just copies property values, accessing them the same way any other code would.
Note that if you don't call Object.assign() at all, and just return the object you're passing into it, you'll get a working object that behaves like you expect.

Turn a for loop on an object into a .map

I have been told my function:
for (const key of Object.keys(temp)) {
this.sessionData.push(temp[key]);
}
Must now use a .map instead,
I have tried this below:
Object.keys(temp).map(function(key) {
this.sessionData[key];
})
But 1 I don't know if it's actually accurate, and also, it cant access the data outside of the scope of the function it is in, here is the whole function below:
public sessionData;
sessionDates(sessionsData) {
const temp = {};
this.sessionData = [];
sessionsData.forEach(session => {
const date = moment(session.startDatetime).format('DDMMYYYY');
if (temp[date]) {
temp[date].push(session);
} else {
temp[date] = [session];
}
});
Object.keys(temp).map(function(key) {
this.sessionData[key];
})
TRYING TO USE THIS BELOW... session data is undefined, it can't access out of the scope?
Object.keys(temp).map(function(key) {
this.sessionData[key];
})
But this works..
for (const key of Object.keys(temp)) {
this.sessionData.push(temp[key]);
}
So this new .map method can't access anything out of its scope.. sigh!
If anybody can help that would be amazing! Thanks!
In Javascript all functions can access variables from outside (called "higher scope") - it's one of the strengths of the language and is called "capture".
The reason your code is failing is because it's using this.sessionData inside a function declaration, which cases problems because this in javascript is... somewhat complex. But you don't need it!
You also need to make sure you return the value you want to output. Here's how I would write it:
this.sessionData = Object.keys(temp).map(key => temp[key]);

Compare functions in Javascript

I have an API that takes a function as an input, and then inside the API, the intent is to add the function to an Array if the function is not already added to the Array.
The call to the API is of the form:
myApiHandle.addIfUnique(function(){
myResource.get(myObj);
});
The API is:
myApiHandle.addIfUnique(myFunc) {
if (myArray.indexOf(myFunc) === -1) {
return;
}
// add to array
}
Now this obviously does not work as expected, since each time a new function is being passed in.
My Question is: Is there a way to pass in a function into the myApiHandle.addIfUnique call that will allow me to compare the existing functions in the array with this function that is currently passed in? The comparison should compare the function name and the object, and if both are the same, then not add the function to the array. I want to avoid adding another argument to the addIfUnique call if at all possible.
In other words, is the below possible:
myApiCall.addIfUnique (someFunc) {
}
If so, what is the someFunc. And what would be the logic inside the API to detect if the function already exists in myArray?
The same problem occurs with addEventListener and removeEventListener, where the callback must be identical (in the === sense) for removeEventListener to remove it.
As you've found, obviously if you call addIfUnique like this:
addIfUnique(function() { })
the function passed each time will be a unique object. The solution is to create the function once:
var fn = function() { };
addIfUnique(fn);
addIfUnique(fn);
A related problem occurs when the function being passed in is a method invocation, so I need to bind it:
var x = { val: 42, method: function() { console.log(this.val); } };
I want to pass a bound version of it, so
addIfUnique(x.method.bind(x));
addIfUnique(x.method.bind(x));
But again, each call to x.method.bind(x) will return a separate function. So I need to pre-bind:
var boundMethod = x.method.bind(x);
addIfUnique(boundMethod);
addIfUnique(boundMethod);
First of all, comparing functions is meaningless, even if two functions are literally different, they may be functionally the same.
And for your problem, you can compare whether it's exactly the same object, or you can compare it literally by using toString() function and regExp.
var addIfUnique = (function() {
var arr = [];
return function(func) {
if (~arr.indexOf(func)) return false;
var nameArr = [];
var funcName = func.name;
var funcRegExp = new RegExp('[^\{]+\{(.+)\}$', 'i');
var funcStr = func.toString().match(funcRegExp);
funcStr = funcStr && funcStr[1];
if (!funcStr) return false;
var strArr = arr.map(function(v){
nameArr.push(v.name);
return v.toString().match(funcRegExp)[1];
});
if (~strArr.indexOf(funcStr) && ~nameArr.indexOf(funcName)) return false;
arr.push(func);
};
}());

How do I pass an extra parameter to the callback function in Javascript .filter() method?

I want to compare each string in an Array with a given string. My current implementation is:
function startsWith(element) {
return element.indexOf(wordToCompare) === 0;
}
addressBook.filter(startsWith);
This simple function works, but only because right now wordToCompare is being set as a global variable, but of course I want to avoid this and pass it as a parameter. My problem is that I am not sure how to define startsWith() so it accepts one extra parameter, because I dont really understand how the default parameters it takes are passed. I've tried all the different ways I can think of and none of them work.
If you could also explain how the passed parameters to 'built in' callback functions (sorry, I dont know of a better term for these) work that would be great
Make startsWith accept the word to compare against and return a function which will then be used as filter/callback function:
function startsWith(wordToCompare) {
return function(element) {
return element.indexOf(wordToCompare) === 0;
}
}
addressBook.filter(startsWith(wordToCompare));
Another option would be to use Function.prototype.bind [MDN] (only available in browser supporting ECMAScript 5, follow a link for a shim for older browsers) and "fix" the first argument:
function startsWith(wordToCompare, element) {
return element.indexOf(wordToCompare) === 0;
}
addressBook.filter(startsWith.bind(this, wordToCompare));
I dont really understand how the default parameters it takes are passed
There is nothing special about it. At some point, filter just calls the callback and passes the current element of the array. So it's a function calling another function, in this case the callback you pass as argument.
Here is an example of a similar function:
function filter(array, callback) {
var result = [];
for(var i = 0, l = array.length; i < l; i++) {
if(callback(array[i])) { // here callback is called with the current element
result.push(array[i]);
}
}
return result;
}
The second parameter of filter will set this inside of the callback.
arr.filter(callback[, thisArg])
So you could do something like:
function startsWith(element) {
return element.indexOf(this) === 0;
}
addressBook.filter(startsWith, wordToCompare);
For those looking for an ES6 alternative using arrow functions, you can do the following.
let startsWith = wordToCompare => (element, index, array) => {
return element.indexOf(wordToCompare) === 0;
}
// where word would be your argument
let result = addressBook.filter(startsWith("word"));
Updated version using includes:
const startsWith = wordToCompare => (element, index, array) => {
return element.includes(wordToCompare);
}
function startsWith(element, wordToCompare) {
return element.indexOf(wordToCompare) === 0;
}
// ...
var word = "SOMETHING";
addressBook.filter(function(element){
return startsWith(element, word);
});
You can use the arrow function inside a filter, like this:
result = addressBook.filter(element => element.indexOf(wordToCompare) === 0);
Arrow functions on MDN
An arrow function expression has a shorter syntax compared to function expressions and lexically binds the this value (does not bind its own this, arguments, super, or new.target). Arrow functions are always anonymous. These function expressions are best suited for non-method functions and they can not be used as constructors.
For anyone wondering why their fat arrow function is ignoring [, thisArg], e.g. why
["DOG", "CAT", "DOG"].filter(animal => animal === this, "DOG")
returns []
it's because this inside those arrow functions are bound when the function is created and are set to the value of this in the broader encompassing scope, so the thisArg argument is ignored. I got around this pretty easily by declaring a new variable in a parent scope:
let bestPet = "DOG";
["DOG", "CAT", "DOG"].filter(animal => animal === bestPet);
=> ["DOG", "DOG"]
Here is a link to some more reading:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions#No_separate_this
based on oddRaven answer
and
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
i did it 2 different way .
1) using function way .
2) using inline way .
//Here is sample codes :
var templateList = [
{ name: "name1", index: 1, dimension: 1 } ,
{ name: "name2", index: 2, dimension: 1 } ,
{ name: "name3", index: 3, dimension: 2 } ];
//Method 1) using function :
function getDimension1(obj) {
if (obj.dimension === 1) // This is hardcoded .
return true;
else return false;
}
var tl = templateList.filter(getDimension1); // it will return 2 results. 1st and 2nd objects.
console.log(tl) ;
//Method 2) using inline way
var tl3 = templateList.filter(element => element.index === 1 || element.dimension === 2 );
// it will return 1st and 3rd objects
console.log(tl3) ;
There is an easy way to use the filter function, access all params, and not over complicate it.
Unless the callback's thisArg is set to another scope filter does not create its own scope, and we can access params within the current scope. We can set 'this' to define a different scope in order to access other values if needed, but by default it is set to the scope it's called from. You can see this being used for Angular scopes in this stack.
Using indexOf is defeating the purpose of filter, and adding more overhead. Filter is already going through the array, so why do we need to iterate through it again? We can instead make it a simple pure function.
Here's a use-case scenario within a React class method where the state has an array called items, and by using filter we can check the existing state:
checkList = (item) => { // we can access this param and globals within filter
var result = this.state.filter(value => value === item); // returns array of matching items
result.length ? return `${item} exists` : this.setState({
items: items.push(item) // bad practice, but to keep it light
});
}

Categories

Resources