Good Day,
I am working on a pet project using NodeJS and Electron. It is basically a simple text editor at the moment. However I am running into an issue when trying to pass the value of a text-area to a function prior to saving to file.
Specifically when I call a function in another module, the value of the contents becomes 'undefined'. I suspect I am passing it incorrectly, or that it is being over-written between when I make the call and when the call executes, since strings are supposed to be passed by reference.
The code for the Renderer(index.html) is like this :
let otherModule = require('./js/otherModule.js');
let $ = require('jquery');
$('#btn_Save').on('click',() => {
// get the fileName, if empty propmt user with save dialog,
//log it to console for debugging
var contents = $('#txt_Content').val();
console.log('with:',contents.substring(0,9),'...');
var finalContents = contents; // (create a copy?)
if(//someConditionMet//)
{
var otherVar = $('#txt_Other').val();
console.log('Use:',otherVar.substring(0,9),'...');
finalContents = otherModule.someFunc(contents, otherVar);
}
//do something with final contents.
})// end of On-click
I have used console.log() to extensively evaluate the function and can confirm that up to the call to otherModule, the contents are correct, and match those in the textArea.It is once we are in the 'otherModule' that things go awry.
The code for the otherModule is like this:
const someFunc = function(contents, otherVar)
{
console.log('DoThings with:',contents.substring(0,9),'...');
// print shows the value to be undefined...
// do more things
console.log('Did stuff with otherVar:',otherVar.substring(0,9),'...');
// prints just fine as as expected.
// do more things
return someString;
}
module.exports = {
someFunc: someFunc
}
As mentioned in the comment, the very first line of the function logs the contents of the console, which displays the substring as 'undefined'.
Thank you for your time and your consideration!
// Extra context//
I have done some searching but beyond learning that strings are passed by reference and are immutable, I have not seen an answer to a question like this. There has been some discussion of closure issues, but usually in the context of events and callbacks, which I do not believe is the context here.
// Extra Information//
I have since found a solution to get my parameters to pass correctly. I have posted the answer below. I did two things:
1. Changed the function definition from 'const' to 'let'
2. Changed the order of the params, and removed the space following the comma.
If you get the value inside the if you should be fine.
if(//someConditionMet//)
{
var contents = $('#txt_Content').val(); //New line
var otherVar = $('#txt_Other').val();
console.log('Use:',otherVar.substring(0,9),'...');
finalContents = otherModule.someFunc(contents, otherVar);
}
I have found a solution to this problem. I am not certain why it makes a difference but I changed two things in 'otherModule'.
1. I changes the function from 'const' to 'let'
2. I changed the order of the parameters, removing the space after the comma
The new function header looks like:
let someFunc = function(otherVar,contents) {...}
I also updated the call to match the new order ( given):
finalContents = otherModule.someFunc(otherVar,contents);
I hope this helps someone in the future!
Related
To whom it may concern
This post is actually an example of a kind of XY Problem.
The real issue ("X") of the person asking for help is obscured, because instead of asking directly about issue X, they ask how to solve a secondary issue ("Y") which they believe will allow them to resolve issue X.
My original problem was that the array (on line 127 in the pics) was becoming empty. While I was tracing down that problem, I was shocked to find that variables were becoming empty in the middle of execution. Even though that's what this post was about, had I been thinking, I wouldn't have needed to post.
The real problem? Look at line 131 of the images...
if (siblings[childitem].my.parents.length = 0) {
The single-equal results in an empty array.
The original question follows.
I discovered this thru the chrome dev tools.
Here's the before pic.
You can see in the local scope all the relevant info, plus it's echoed in the yellow text inline with the real-time code. All the values are appropriate.
Upon executing the Next Step in the execution. . .
You can see the reticle/cursor has moved to the right in the real-time code. The previous local variables have disappeared. old parents is now [], and children is undefined.
Is it a reference problem somehow? I got the filter syntax from this post here.
What the heck is going on?
Here's the code in text for those of you that need it.
selected.my.children.forEach (childitem => {
// let parentindex = siblings[child].my.parents.indexOf(selected.my.place);
let oldparents = siblings[childitem].my.parents;
let newparents = oldparents.filter(e => {e != selected.my.place});
siblings[childitem].my.parents = newparents;
// if (parentindex > -1) {
// siblings[child].my.parents.splice(parentindex, 1)
// }
if (siblings[childitem].my.parents.length = 0) {
siblings[childitem].faceup();
}
console.log(siblings[childitem].my);
});
Because you entered into a new function scope. That is the callback that you pass to filter function:
e => {e != selected.my.place} // <- this is a new scope, no local variables other than `e`
You can clearly see that there are no local variables other than e.
You can however find childitem, newparents etc. in parent scope. In your example it is Closure (click on Closure (tap))
ElementList is array like not array, you can not use the filter directly, you need transform it to array first:
let oldparents = [...siblings[childitem].my.parents];// or you can use Array.from
let newparents = oldparents.filter(e => e != selected.my.place); // here the arrow function also needs to be updated, remove the braces
I want to get the condition of action pagination that's why i define this in my grid i want how can i define variable to test if the action is paginator or not :
settings.ClientSideEvents.EndCallback = "function (s,e){Test123(s,e)}";
And now when i want to test if action is pagination i display an alert:
function Test123(s, e) {
debugger;
var xx = ListeClient.cpPageChanged;
alert(xx);
}
it display undefined
How can i resolve this issue
You almost certainly don't want quotes around your function definition, because you almost never eval a string in Javascript (but I don't know what API you're working with, so I can't say for sure):
settings.ClientSideEvents.EndCallback = function (s,e){Test123(s,e)};
The other problem could be that this line
var xx = ListeClient.cpPageChanged;
is undefined. Then your problem is that cpPageChanged is not a property of ListeClient, so you might just have a typo.
There is something wrong with my code, but I can't find what it is. The first time I call registerStartDateValidation, the value of the startDateValidation variable in the defineKeyword closure is of course the same than what was affected outside the closure. But the second time, the variable is reaffected with another value, but the value of startDateValidation in the defineKeyword is still equal to the value of the first time we call registerStartDateValidation. Does the closure cache the variable?
Tv4.prototype.registerStartDateValidation = function (attributes) {
var helper = this;
var dateNames = Object.keys(attributes);
var startDateName = dateNames[0];
var startDateString = attributes[startDateName];
var endDateName = dateNames[1];
var endDateString = attributes[endDateName];
var startDateValidation = helper.startDateValidation(startDateString, startDateName, endDateString, endDateName);
console.log(startDateValidation)//First time equal "true", second time equal "false"
tv4.defineKeyword('startDate', function (data, value) {
if (value === 'startDate') {
console.log(startDateValidation)//First time equal "true", second time still equal "true"
return startDateValidation
}
});
};
Note that there is nothing making an ajax request, that could mess up the order the functions are called.
Short answer: no, a closure won't cache the variable, so there is something else going on in your code. Reducing the problem to a simple complete example that demonstrates the problem will usually make it obvious where the problem is coming from, or at least help other people figure it out.
However, since it looks like you're referring to is this project, it may be possible to figure out what's going on. Tv4.defineKeyword() appends new functions to a list if it is called multiple times for the same keyword. Since you're calling tv4.defineKeyword (note the lower-case 't') and not helper.defineKeyword or this.defineKeyword, it looks like each call to registerStartDateValidation will affect the same instance, so you are almost certainly adding a validation function when you meant to replace it.
First of all, the full code is at work so I'm reciting from memory.
There is a js file with containing an object defined like this:
function SomeObject(someArg) {
this.someMember = someVar.split(".");
...
this.someFunc = function() {
return this.someMember;
}
...
}
There is another js file which uses this object and has a function that looks something like this:
someOtherFunc(SomeObject):
var someOtherVar = SomeObject.someFunc();
var length = someOtherVar.length;
....
Now when I add an alert(SomeObject.someFunc()) at the beginning of someOtherFunc all I see is an empty box. However, when I alert SomeObject.someFunc().length I get 1 (because the array has one element). Yet, I have a "length is an undefined..." where var length is defined. Also, when I alert SomeObject.someFunc()[0] I have the value of the element shown (it's a string).
I know it's IE6 (don't ask) and I couldn't provide much context, but I'm still hoping this issue is somewhat known or has some explanation or a workaround.
Is it possible to find the name of an anonymous function?
e.g. trying to find a way to alert either anonyFu or findMe in this code http://jsfiddle.net/L5F5N/1/
function namedFu(){
alert(arguments.callee);
alert(arguments.callee.name);
alert(arguments.callee.caller);
alert(arguments.caller);
alert(arguments.name);
}
var anonyFu = function() {
alert(arguments.callee);
alert(arguments.callee.name);
alert(arguments.callee.caller);
alert(arguments.caller);
alert(arguments.name);
}
var findMe= function(){
namedFu();
anonyFu();
}
findMe();
This is for some internal testing, so it doesn't need to be cross-browser. In fact, I'd be happy even if I had to install a plugin.
You can identify any property of a function from inside it, programmatically, even an unnamed anonymous function, by using arguments.callee. So you can identify the function with this simple trick:
Whenever you're making a function, assign it some property that you can use to identify it later.
For example, always make a property called id:
var fubar = function() {
this.id = "fubar";
//the stuff the function normally does, here
console.log(arguments.callee.id);
}
arguments.callee is the function, itself, so any property of that function can be accessed like id above, even one you assign yourself.
Callee is officially deprecated, but still works in almost all browsers, and there are certain circumstances in which there is still no substitute. You just can't use it in "strict mode".
You can alternatively, of course, name the anonymous function, like:
var fubar = function foobar() {
//the stuff the function normally does, here
console.log(arguments.callee.name);
}
But that's less elegant, obviously, since you can't (in this case) name it fubar in both spots; I had to make the actual name foobar.
If all of your functions have comments describing them, you can even grab that, like this:
var fubar = function() {
/*
fubar is effed up beyond all recognition
this returns some value or other that is described here
*/
//the stuff the function normally does, here
console.log(arguments.callee.toString().substr(0, 128);
}
Note that you can also use argument.callee.caller to access the function that called the current function. This lets you access the name (or properties, like id or the comment in the text) of the function from outside of it.
The reason you would do this is that you want to find out what called the function in question. This is a likely reason for you to be wanting to find this info programmatically, in the first place.
So if one of the fubar() examples above called this following function:
var kludge = function() {
console.log(arguments.callee.caller.id); // return "fubar" with the first version above
console.log(arguments.callee.caller.name); // return "foobar" in the second version above
console.log(arguments.callee.caller.toString().substr(0, 128);
/* that last one would return the first 128 characters in the third example,
which would happen to include the name in the comment.
Obviously, this is to be used only in a desperate case,
as it doesn't give you a concise value you can count on using)
*/
}
Doubt it's possible the way you've got it. For starters, if you added a line
var referenceFu = anonyFu;
which of those names would you expect to be able to log? They're both just references.
However – assuming you have the ability to change the code – this is valid javascript:
var anonyFu = function notActuallyAnonymous() {
console.log(arguments.callee.name);
}
which would log "notActuallyAnonymous". So you could just add names to all the anonymous functions you're interested in checking, without breaking your code.
Not sure that's helpful, but it's all I got.
I will add that if you know in which object that function is then you can add code - to that object or generally to objects prototype - that will get a key name basing on value.
Object.prototype.getKeyByValue = function( value ) {
for( var prop in this ) {
if( this.hasOwnProperty( prop ) ) {
if( this[ prop ] === value )
return prop;
}
}
}
And then you can use
THAT.getKeyByValue(arguments.callee.caller);
Used this approach once for debugging with performance testing involved in project where most of functions are in one object.
Didn't want to name all functions nor double names in code by any other mean, needed to calculate time of each function running - so did this plus pushing times on stack on function start and popping on end.
Why? To add very little code to each function and same for each of them to make measurements and calls list on console. It's temporary ofc.
THAT._TT = [];
THAT._TS = function () {
THAT._TT.push(performance.now());
}
THAT._TE = function () {
var tt = performance.now() - THAT._TT.pop();
var txt = THAT.getKeyByValue(arguments.callee.caller);
console.log('['+tt+'] -> '+txt);
};
THAT.some_function = function (x,y,z) {
THAT._TS();
// ... normal function job
THAT._TE();
}
THAT.some_other_function = function (a,b,c) {
THAT._TS();
// ... normal function job
THAT._TE();
}
Not very useful but maybe it will help someone with similar problem in similar circumstances.
arguments.callee it's deprecated, as MDN states:
You should avoid using arguments.callee() and just give every function
(expression) a name.
In other words:
[1,2,3].forEach(function foo() {
// you can call `foo` here for recursion
})
If what you want is to have a name for an anonymous function assigned to a variable, let's say you're debugging your code and you want to track the name of this function, then you can just name it twice, this is a common pattern:
var foo = function foo() { ... }
Except the evaling case specified in the MDN docs, I can't think of any other case where you'd want to use arguments.callee.
No. By definition, an anonymous function has no name. Yet, if you wanted to ask for function expressions: Yes, you can name them.
And no, it is not possible to get the name of a variable (which references the function) during runtime.