var testvar = 'boat';
function testA() {
console.log(testvar);
}
function testB() {
console.log(window.testvar);
}
I know that if I don't put the "window." for my global variable, then javascript searches all the scopes from method testA onward until it finds the variable testvar, so if I do window.testvar instead does it make it faster because I'm directly telling javascript which scope to look in for the variable? Or slower because I'm first telling javascript to look for the window object and then the variable?
Try both of the codes below separately and see the results for yourself. Indeed this might not be the most accurate testcase however by avoiding all other manipulation and doing a simple assignment inside a long enough for loop it ought to be accurate enough.
I have to say I was also surprised to see that by not specifying window Chrome persistently reported about 20% faster execution for the second code.
CODE 1
// window.testvar testcase.
window.testvar = 'Hi there! I am a testvar!';
var tmp;
var start = new Date();
for(var i = 0; i < 1000000; i++){
tmp = window.testvar;
}
var stop = new Date();
console.log('This took exactlly ' + (stop.getTime() - start.getTime()) + ' milliseconds!');
RESULTS:
1695ms
1715ms
1737ms
1704ms
1695ms
CODE 2
// direct testvar testcase
testvar = 'Hi there! I am a testvar!';
var tmp;
var start = new Date();
for(var i = 0; i < 1000000; i++){
tmp = testvar;
}
var stop = new Date();
console.log('This took exactlly ' + (stop.getTime() - start.getTime()) + ' milliseconds!');
RESULTS:
1415ms
1450ms
1422ms
1428ms
1450ms
Tested in Chrome 20.0.1132.47.
Vedaant's jsperf was not helpful. It was only creating functions, not executing them. Try this one: http://jsperf.com/epictest/9. It too shows that not specifying window is faster. I also added a test to show that copying to a local variable is dramatically faster. Vary the loop counter to see that you win for anything more than a single reference to the global.
Chrome has a useful javascript CPU profiler. Just create a loop to run the function several thousand times and start the profiler. I'm guessing that the difference is very small but this would be a good way to know for sure.
I just made a jsPerf test for you, check it out at: http://jsperf.com/epictest. It seems that
function testA() {
console.log(testvar);
}
is a bit faster.
Related
I heard "eval" function in javascript/node.js is evil, but it's needed in our application which takes a string sent to it by a sister program in certain format and evaluate it and record the result. Yes, we can trust the string it's going to be evaluated.
The problem is on performance. The following code takes 552ms. However, I replace the eval(...) by function add2(a,b) { return a+b}, it took only 12ms.
The question is, how do we improve the performance for code generated after evaluation. Would appreciate if anyone has any idea how to improve the performance with eval.
eval('function add2(a,b) { return a+b}')
let start = Date.now();
let total = 0;
for (let i = 0; i < 10000000; i++) {
total += add2(i, 1);
}
console.log(`took ${Date.now() - start} total=${total}`)
Update 1:
Thanks to CertainPerformance who pointed out the same code runs fast on Chrome browser.
The above snippet (with eval but without the wrapper of ((() => { ...}) ()` took 75ms, 12ms with the wrapper.
The Javascript v8 engine for my Chrome browser is V8 10.3.174.14.
I installed node.js v18.5.0 which uses V8 javascript engine 10.2.154.4-node.8, however, it's not much improvement over node v10.15.1 which uses v8 engine version 6.8.275.32-node.12.
By the way, you can use the command node -p process.versions.v8 to find the V8 engine version for node.js
Since node.js v18.5.0 is the latest release, I can't find a node with v8 engine 10.3.174.14. So I have to wait until the next release of node. I doubt the two javascript v8 engine versions (10.3.174.14 vs 10.2.154.4-node.8) will have such a huge performance difference (12ms vs ~500ms). My guess is that node.js has some inefficiency, especially when it comes to the "evil" function "eval" --- not always evil but need to be used with precaution.
There are two issues here:
eval sometimes produces a slow function for some reason (fix: use new Function)
Changing variables on the top level is slow when done a whole lot (fix: declare the variables that change frequently inside a block)
If you put the total inside an IIFE so that when it gets reassigned, it doesn't change a value on the top level, this micro-benchmark looks to improve to the desired handful of milliseconds.
Below runs in 10-20ms on my machine:
eval('function add2(a,b) { return a+b}')
let start = Date.now();
(() => {
let total = 0;
for (let i = 0; i < 10000000; i++) {
total += add2(i, 1);
}
console.log(`took ${Date.now() - start} total=${total}`)
})();
For some reason, the evaled function appears to be slow if created inside a block. Below runs in 1000+ms on my machine:
(() => {
eval('function add2(a,b) { return a+b}')
let start = Date.now();
let total = 0;
for (let i = 0; i < 10000000; i++) {
total += add2(i, 1);
}
console.log(`took ${Date.now() - start} total=${total}`)
})();
Using new Function instead creates a dramatic improvement (below runs in 10-20ms on my machine):
(() => {
const add2 = new Function('a', 'b', 'return a + b');
let start = Date.now();
let total = 0;
for (let i = 0; i < 10000000; i++) {
total += add2(i, 1);
}
console.log(`took ${Date.now() - start} total=${total}`)
})();
So, consider changing your serializing algorithm to pass values such that the function can be re-created with new Function instead of with eval.
If you can't change the source that's sending the function string, and the parameters aren't too complicated, you could use a regular expression to parse the function string into its parameters and body, and then pass that to new Function.
Another example of the phenomena without dynamic functions in the mix - see how the IIFE version below runs significantly faster.
const start = Date.now();
let i = 0;
while (i < 5e8) {
i = i + 1;
}
console.log(Date.now() - start);
(() => {
const start = Date.now();
let i = 0;
while (i < 5e8) {
i = i + 1;
}
console.log(Date.now() - start);
})();
That said, micro-benchmarks like these are generally a very poor indicator of whether there will actually be a problem when your code runs in the real world. Don't take them to mean much.
I heard "eval" function in javascript/node.js is evil
It has its place. Though, here, new Function would be more appropriate (and is a lot faster in certain circumstances). The general technique of serializing a function to a string to be parsed back into an actual function in a different environment isn't unheard of - this is the same technique that web scrapers like Puppeteer make extensive use of.
I was under the impression that javascript executed all lines at the same time and this is one thing that makes it different from many other programming languages. I have the following code for a simple index;
var newAcceptIndex = 0;
function addNewAccept(event) {
var newAccept = `some new accept ${newAcceptIndex}`
$(event.target).closest("[id^='new_item_']").before(newAccept);
newAcceptIndex += 1
};
I would have expected newAcceptIndex on the first call to be 1 since all code should be executed at the same time and thus set to 1 instead of 0. Am I encountering a race condition or is this working as intended?
JS code does not run all at the same time, you're probably just thinking about the hoisting JS does with their variables and functions so they don't immediately throw errors. You just need to reorganize your code like how it would be in other programming languages - executed from top to bottom:
var newAcceptIndex = 0;
function addNewAccept(event) {
newAcceptIndex += 1
var newAccept = `some new accept ${newAcceptIndex}`
$(event.target).closest("[id^='new_item_']").before(newAccept);
};
Update: This bug affects v28 and was fixed for v29 onward.
Having a function like this:
function arrMany(len) {
var a = new Array(len);
}
If the function is called rapidly the length of the produced array sometimes has length of previous call to the function.
Expanding the function to:
function arrMany(len, show) {
var a = new Array(len);
if (show) {
someElementTextA.value = a.length;
someElementTextB.value = len;
}
}
And calling by e.g.:
function tick() {
arrMany(2);
arrMany(4);
arrMany(6);
arrMany(10, true);
}
setInerval(tick, 1000 / 200);
after a while someElementTextA shows the value 6 and not 10.
Sample fiddle edit
Sample fiddle show
When this happens, and one turn down fps to for example 1, it keeps spitting out wrong length for a long time.
The behavior typically manifests itself after about 20 seconds on my system.
Tested on: Firefox Linux i686; rv:28.0
Notes:
No action in Browser Console or Web Console.
If Web Console is open the error never occurs.
If Web Console is opened when the error manifests, it corrects itself at once.
Given pt. 3. it is rather hard to debug in a sane way using the browser itself.
Any good fix to this?
Update 1:
Have tested this on Firefox, Chrome and Opera. Firefox is the only browser that manifests the bug. No action in console.
From comments and answer: Does not manifest on Window XP, Firefox v31. Does manifest on #jdigital's system – i.e. it's not local to my system.
Update 2:
Always specifying show in above code does not make any difference.
function tick() {
arrMany(2, false);
arrMany(4, false);
arrMany(6, false);
arrMany(10, true);
}
Nor does using default parameter. But another point being that show is only for the convenience of the sample code.
As to solution for conundrum saying:
if (arr.length !== len) { arr = new Array(len); }
seems to work. But not sure if is is a fix, or if this can break the same way. Using
if (arr.length !== len) { arr = new Array(len); retry = 1; }
show that retry is done quite frequent.
Another fix, (as in debug testing); but in another way. By adding this inside the function, the array is always of correct length. As in: retry is always 0.
for (retry = 0; arr.length !== len && retry < 10; ++retry) {
arr = new Array(len);
}
As to question.
Question is more if there is any good fix to the bug, not if it is a bug or not.
Any information for why this is happening and if there is any similar code, e.g. not handling Arrays, that can break in the same way is also interesting, but was not part of the original question as such.
Update 3:
For, "what I am trying to do", it is a question about it in the general sense, not specific to a case.
It is not a syntax I usually use, but happens from time to time, usually in some debug or verbose output where using a sparse array is simpler then a loop. But it is then nice to know that it is not reliable.
At the same time I recognize others might have different needs and as such solutions to it, in the broad general sense, is always nice to have.
Then again; tipping it the other way around I guess it would be another argument against ever using it ... ;)
This worked for me:
function arrMany(w, show) {
(function () {
var a = new Array(w);
if (show) {
arw1.value = a.length;
arw2.value = w;
}
})();
}
Interesting read: JavaScript's Strange Threaded Nature
This code explores the following property:
(function() { new Object(); })(); is garbage collected properly
It looks like a bug in Firefox. This problem does not occur in Chrome etc.
When I add a logging statement:
function arrMany(w, show) {
var a = new Array(w);
if (show) {
if( a.length != w ) {
console.log(a);
}
arw1.value = a.length;
arw2.value = w;
}
}
When I run this, I can see that console.log is occasionally triggered, and the Firefox Web Console window shows that the array has 5 elements.
If I understand correctly, you want a workaround for this bug, even if it is an ugly hack, as long as it is reliable. If so, consider this approach, which explicitly initializes the array:
function arrMany(w, show) {
// should be:
// var a = new Array(w);
// but there's a bug in Firefox, see StackOverflow 22726716
//
var a = [];
for( var i = 0; i < w; i++ ) a[i] = undefined;
if (show) {
arw1.value = a.length;
arw2.value = w;
}
}
I am currently building a small web application with similar functionality across all modules. I want to code small generic functions so that all programmers next to me, call these functions and these functions return necessary but important data for them to implement their functionality. In this example, I am trying to deal with the typical "choose true or false" exercise. So from the template.php they call this function:
function checkAnswers(){
var radiobuttons = document.form1.exer1;
var correctAnswers = answers(); //this is an array of string
var checkedAnswers = checkExerciseRB(radiobuttons, 2, correctAnswers);
for(i=0; i<checkedAnswers.length; i++){
alert(checkedAnswers[i]);
}
}
Function checkExerciseRB is my generic function, it is called from checkAnswers.
function checkExerciseRB(rbuttons, opciones, correct){
var answers = new Array();
var control = 0;
for(i=0; i<rbuttons.length; i++){
var noPick="true";
for(j=0; j<opciones; j++){
if(rbuttons[control+j].checked){
if(rbuttons[control+j].value==correct[i]){
answers[i]= 1;
noPick="false";
break;
}
else{
answers[i]=2;
noPick="false";
break;
}
}
}
if(noPick=="true")
answers[i]=0;
control=control+opciones;
}
return answers;
}
It works great but while looking at my favorite browsers (FireFox, Chrome) error log it says:
TypeError: rbuttons[control + j] is undefined
Any clue on how to deal with this matter?
This probably means that control + j is greater than or equal to the length of the array rbuttons. There's no such array element as rbuttons[control + j].
You should learn how to use the JavaScript debugger in your favorite browsers! Debuggers are great. They let you watch this code run, line by line, as fast or as slow as you want, and watch how the value of control changes as you go.
You’ll watch it, and you’ll think “Oh! That line of code is wrong!”
You're looping through rbuttons.length times, but in each loop you're adding 2 to control. Using control to index your array, you're going to run past the end.
Does the index specified by control + j exist in the array? i.e: If that evaluates to 4, is there at least 5 items in the array?
Also, you should be using var i, var j, etc inside your for loop. Without it your variables are leaking into the scope this code is executed in (most likely the global scope, and that's not good) :)
I have a script that's several lines long and I have a lot of flag types like so
var counter = 0;
var carFlags = {
Audio : counter++ ,
Bentley : counter++ ,
Chrysler : counter++
Datsun : counter++
...
};
later if I create a new flag object the same way
var counter = 0;
var nameFlags = {
Ashley : counter++ ,
Bronwyn : counter++ ,
Catherine : counter++
DakotaFanning : counter++
...
};
It complains that counter is used as part of a greater statement, but I think it's perfectly acceptable here. However, I have used counter twice here, so it warns of redeclaration of var counter. I could move var counter to the top of the page, but that might make it less clear what counter is used for. Is there any real problem with using var more than once? I had a delete counter statement but it did nothing. Should I change it to delete window.counter?
I am not quite sure about your scope here, but if it's global scope then delete counter would indeed do nothing. There is also no need to delete window.counter which will is the same thing anyway (if we are in a browser environment in global scope).
If you wish to re-use the variable - a simple assignment would do:
counter = 0
The reason JSLint complains is, I believe, to warn you that you might have meant to re-use the counter and accidentally typed var (happened to me more than once).
There is absolutely no harm in declaring the same variable twice (aside from increasing slightly the size of the JS file your users' browsers would have to download when loading your page) in the same scope.
EDIT:
there is, of course, a question of why would you use the same name...
No, there isn't a problem. In fact, in Javascript, variable declarations (with var) are automatically hoisted to the top of their enclosing scope anyway. For example, the following:
var n = 5;
if (n < 3) {
var q = 2;
var n = 4;
}
is equivalent to:
var q, n=5;
if (n < 3) {
q = 2;
n = 4;
}
Give them unique names and you won't have any problem.
var carCounter = 0;
var nameCounter = 0;
Nor will you end up with any hard to find bugs because you used the same variable for two different tasks.