I'm running some code using eval that will change a $scope object called variables.
Here's my watcher:
$scope.$watch(function () {
return $scope.variables;
}, function (variables) {
console.log('changed!');
}, true);
Here's the code I'm running (this is just example code; it could be any arbitrary code I can't anticipate):
eval('$scope.variables.foo = "bar";\n$scope.variables.bar = "foo";');
In this example, the watcher is triggered twice, and gives me the new values. So far so good. What's missing is the information that the first trigger was caused by the first line in the eval call, and the second one on the second line (note the \n in the eval call denoting a new line number).
Is there a way to achieve this using Angular or just plain JavaScript? I have looked into tools like https://github.com/stacktracejs/stacktrace.js but they don't seem to do the trick.
Just to be clear, I'm not looking for line numbers for when errors are thrown. I need to keep track of which line the eval statement is currently on when the watcher's callback is triggered, so that I can associate that line number with the change that has been made to the object I'm watching.
You could split your input string by \n and insert new statements keeping track of the line numbers
var s = '$scope.variables.foo = "bar";\n$scope.variables.bar = "foo";\n'
var arr = s.split(/\n/);
var out = [];
for (var i = 0; i < arr.length; i++) {
out.push("console.log('current line: " + i + "');");
out.push(arr[i]);
}
out = out.join('\n');
console.log(out);
document.write('<pre>' + out + '</pre>');
You can replace the console.log() by what you want, a call to another function, a debug call...
Related
I'm experimenting with my very basic Javascript and I found a nice Tutorial on how to make a very basic calculator in JS.
I tried following along with the video but I didn't want to just "copy-paste" what he was writing so I stopped and tried to do what I thought was the logic code...WRONG!
Here the problem
Why this doesn't work?
function addNum() {
let first = document.querySelector('.first').value;
let second = document.querySelector('.second').value;
let result = document.querySelector('.resultt').value;
return result = first + second
}
I tried to assign the input related to the result to a variable but it doesn't work.
But when I do this: (as it was done in the tutorial)
function addNum() {
let first = parseInt(document.querySelector('.first').value);
let second = parseInt(document.querySelector('.second').value);
document.querySelector('.resultt').value=first + second;
}
So without assigning the result to a variable, it works.
Why?
When you do
let result = document.querySelector('.resultt').value;
you're copying the value from the value property to the result variable. There's no ongoing link between them after that, they each just contain the same string. That means later, when you do result = first + second, all you're doing is updating result; that has no effect at all on value.
So you have to assign back to value as you do in your second code block.
I have a method in string like:
var str = "function evalTest(param){if(param)return '<div>hello</div>'}else return '<div>world</div>'"
I am replacing param like:
var res = str.replace("param", "param=false");
Now if I do eval like:
var final = eval(res);
I am expecting final should contain result "world" because passed param = false.
How to achieve this result "world"?
First a caveat: There's almost certainly a better solution to whatever problem you're trying to solve by having that function in a string.
And another one: Never eval code supplied by an end user except for that same end user. For instance, never let user A supply the code, then eval it in user B's browser. (Without user B knowing that you're doing that and expressly consenting to it.)
Really, almost any time you're reaching for eval (or its cousin new Function), it's worth stepping back to see if there's another approach you can use.
But answering the question asked:
eval is just creating the function. You don't have anything in that string that calls it.
Because eval works magically in the current scope, evaling your string creates the function in the current scope. You could then call your function to get the result you're looking for:
var str = "function evalTest(param){if(param){return '<div>hello</div>'}else {return '<div>world</div>'}}";
var res = str.replace("param", "param=false");
eval(res);
var final = evalTest();
console.log(final);
Note that I fixed a couple of syntax errors in the function's text (curly brace issues, mostly).
If you don't want the function defined in the current scope, you can modify the string to make it a function expression and call it immediately:
var str = "function evalTest(param){if(param){return '<div>hello</div>'}else {return '<div>world</div>'}}";
var res = str.replace("param", "param=false");
var final = eval("(" + res + ")()");
console.log(final);
very basic JavaScript programmer here!
I was busy on some code with variables that look like this:
blocktype1;
blocktype2;
blocktype3;
blocktype4;
... //everything between blocktype4 and blocktype70, the three dots are not actual code!
blocktype70;
Now I was using eval() in a function where a value was given to one of the blocktype variables. The blocktype depended on the variable "number".
This is what I had for that part:
eval("blocktype" + number) = 3
What I want is, say "number" is 27, then I want the variable blocktype27 to get a value of 3.
When I check the console it says:
ReferenceError: Invalid left-hand side in assignment
Could anyone possibly help me?
I would prefer just vanilla JavaScript and still the use of eval.
Thank you for your time!
The 'correct' solution would probably be to use an Array which is ideal for sequences and are accessible by index.
var number = 1;
var val = 3;
var blocktype = []; // so clean
blocktype[number] = val;
However, properties can be accessed as with the bracket notation as well. This assumes the variables are in global scope and are thus properties of the global (window) object.
var blocktype1; // .. etc
window["blocktype" + number] = val;
The problem with the eval is that is effectively the same as doing f() = 3 which does not make sense: only variables/properties can be assigned to1.
However eval is a built-in function and the results of a function cannot be assigned to, per the error message. It could be written as
var blocktype1; // .. etc (see dandavis' comment)
eval("blocktype" + number + " = " + val);
// What is actually eval'd is:
// eval("blocktype1 = 3")
which quickly exposes a flaw with eval. If val was the string "Hello world!" with would result in eval("blocktype1 = Hello world!") which is clearly invalid.
1 For the gritty: the left-hand side of an assignment has to be a Reference Specification Type, which is a more wordy way of describining the above behavior. (It is not possible for a JavaScript function to return a RST, although it could technically be done for vendor host objects.)
Feel free not to accept this, since it's specifically not using eval(), but:
You can allocate an array of size 71 like so:
var blocktype = new Array(71);
(your number values apparently start at 1, so we'll have to ignore the first element, blocktype[0], and leave room for blocktype[70], the 71st)
You can now assign elements like this:
blocktype[number] = 3;
and use them like so:
alert( blocktype[number] );
How to loop through this data: (I have no control over format)
{"rowCount":3,"1":{"K":"2009","V":"Some Data"},"2":{"K":"2010","V":"More Data"}}
Above is a console.log(results) and results is a string
var r = JSON.parse(results);
var t;
for(var i=1;i<=r.rowCount;i++) {
t=r[i].V;
tableData.push(
{title:t, year:'2009', hasChild:true, color: '#000'}
);
}
Error: TypeError: 'undefined' is not an object (evaluating 'r[i].V')
I cannot get it to evaluate the variable i. What am I doing wrong?
Thanks
UPDATE
The incoming data had a bad rowcount causing the error. The accepted answer however is correct... just user error on my part not catching the bad incoming data. Had I put a console.log inside the loop I would have realized the error was actually happening after two successful loops. oops
I assume r.rowCount should be j.rowCount.
Ideally you should also initialise the i variable if you haven't already (i.e. with the var keyword).
(I've also moved the var t declaration outside the loop, to make it clear that it's the same t throughout and you're just changing its value. You shouldn't redeclare it with var each time – although I doubt this affects the output.)
var j = {"rowCount":2,"1":{"K":"name","V":"john"},"2":{"K":"name","V":"sue"}};
var t;
for (var i = 1; i <= j.rowCount; i++) {
t = j[i].V;
console.log(t);
}
Working demo – JSFiddle
What is this line doing:
var tfun = new Function("_", "at" , "with(_) {return (" + text + ");}" );
What is the _, at, and with(_)?
I've read this:
http://www.permadi.com/tutorial/jsFunc/index.html
I understand that it's creating a new function object, but am still quite puzzled at what his is supposed to do.
Forgot to put the source:
http://kite.googlecode.com/svn/trunk/kite.js
http://www.terrainformatica.com/2011/03/the-kite-template-engine-for-javascript/
Here a function is being created that will return the value of the key stored in the variable text on the object passed in to tfun().
When a new Function is created in this manner, the first arguments refer to the parameters of the function and the last argument is the function itself. So here we have two parameters named _ and at and then the function body.
with() is a statement saying to conduct the following lines of code within the context of the object specified. So with(_) is saying to conduct the return statement pulling the key text stored in _.
Here's an example:
var text = "name";
var obj = { "name" : "Bob" };
var tfun = new Function("_", "at" , "with(_) {return (" + text + ");}" );
tfun( obj ); // returns "Bob"
I'm not sure why the at parameter is there as it's not being used.
First comes the function arguments, then code code, so it's basically the same as:
var tfun = function(_, at) {
with(_) { return (eval(text)); };
}
So, whatever is in the text variable will be evaluated and returned from the function.
Note: The use of the eval function should generally be avoided, and as creating code dynamically from a variable does the same thing, it should also generally be avoided. There are a few situations where eval is needed, but most of the time it's not, so you should instead try find out the proper way of doing what you are trying to do.