Consider the following code:
const testArray = ['string1', 'string2', 'string3'];
for (let iterator in testArray) {
console.log(iterator == 0);
}
My IDE (Visual Studio Code), complains about the console.log, that it will always return false (error: ts(2367))
But if I input this code directly either here, or in a JSBin, or in my current browser, I always get the true result on the first iteration. Anyone can explain me, if my IDE is to blame for this, or maybe there is some legacy javascript, where that condition indeed always returns false?
Based on this table https://dorey.github.io/JavaScript-Equality-Table/ it also seems that the above code is valid, and will run as expected.
const testArray = ['string1', 'string2', 'string3'];
for (let iterator in testArray) {
console.log(iterator == 0);
}
Related
I’ll start with the code:
var s = ["hi"];
console.log(s);
s[0] = "bye";
console.log(s);
Simple, right? In response to this, the Firefox console says:
[ "hi" ]
[ "bye" ]
Wonderful, but Chrome’s JavaScript console (7.0.517.41 beta) says:
[ "bye" ]
[ "bye" ]
Have I done something wrong, or is Chrome’s JavaScript console being exceptionally lazy about evaluating my array?
Thanks for the comment, tec. I was able to find an existing unconfirmed Webkit bug that explains this issue: https://bugs.webkit.org/show_bug.cgi?id=35801 (EDIT: now fixed!)
There appears to be some debate regarding just how much of a bug it is and whether it's fixable. It does seem like bad behavior to me. It was especially troubling to me because, in Chrome at least, it occurs when the code resides in scripts that are executed immediately (before the page is loaded), even when the console is open, whenever the page is refreshed. Calling console.log when the console is not yet active only results in a reference to the object being queued, not the output the console will contain. Therefore, the array (or any object), will not be evaluated until the console is ready. It really is a case of lazy evaluation.
However, there is a simple way to avoid this in your code:
var s = ["hi"];
console.log(s.toString());
s[0] = "bye";
console.log(s.toString());
By calling toString, you create a representation in memory that will not be altered by following statements, which the console will read when it is ready. The console output is slightly different from passing the object directly, but it seems acceptable:
hi
bye
From Eric's explanation, it is due to console.log() being queued up, and it prints a later value of the array (or object).
There can be 5 solutions:
1. arr.toString() // not well for [1,[2,3]] as it shows 1,2,3
2. arr.join() // same as above
3. arr.slice(0) // a new array is created, but if arr is [1, 2, arr2, 3]
// and arr2 changes, then later value might be shown
4. arr.concat() // a new array is created, but same issue as slice(0)
5. JSON.stringify(arr) // works well as it takes a snapshot of the whole array
// or object, and the format shows the exact structure
You can clone an array with Array#slice:
console.log(s); // ["bye"], i.e. incorrect
console.log(s.slice()); // ["hi"], i.e. correct
A function that you can use instead of console.log that doesn't have this problem is as follows:
console.logShallowCopy = function () {
function slicedIfArray(arg) {
return Array.isArray(arg) ? arg.slice() : arg;
}
var argsSnapshot = Array.prototype.map.call(arguments, slicedIfArray);
return console.log.apply(console, argsSnapshot);
};
For the case of objects, unfortunately, the best method appears to be to debug first with a non-WebKit browser, or to write a complicated function to clone. If you are only working with simple objects, where order of keys doesn't matter and there are no functions, you could always do:
console.logSanitizedCopy = function () {
var args = Array.prototype.slice.call(arguments);
var sanitizedArgs = JSON.parse(JSON.stringify(args));
return console.log.apply(console, sanitizedArgs);
};
All of these methods are obviously very slow, so even more so than with normal console.logs, you have to strip them off after you're done debugging.
This has been patched in Webkit, however when using the React framework this happens for me in some circumstances, if you have such problems just use as others suggest:
console.log(JSON.stringify(the_array));
Looks like Chrome is replacing in its "pre compile" phase any instance of "s" with pointer to the actual array.
One way around is by cloning the array, logging fresh copy instead:
var s = ["hi"];
console.log(CloneArray(s));
s[0] = "bye";
console.log(CloneArray(s));
function CloneArray(array)
{
var clone = new Array();
for (var i = 0; i < array.length; i++)
clone[clone.length] = array[i];
return clone;
}
the shortest solution so far is to use array or object spread syntax to get a clone of values to be preserved as in time of logging, ie:
console.log({...myObject});
console.log([...myArray]);
however be warned as it does a shallow copy, so any deep nested non-primitive values will not be cloned and thus shown in their modified state in the console
This is already answered, but I'll drop my answer anyway. I implemented a simple console wrapper which doesn't suffer from this issue. Requires jQuery.
It implements only log, warn and error methods, you will have to add some more in order for it to be interchangeable with a regular console.
var fixedConsole;
(function($) {
var _freezeOne = function(arg) {
if (typeof arg === 'object') {
return $.extend(true, {}, arg);
} else {
return arg;
}
};
var _freezeAll = function(args) {
var frozen = [];
for (var i=0; i<args.length; i++) {
frozen.push(_freezeOne(args[i]));
}
return frozen;
};
fixedConsole = {
log: function() { console.log.apply(console, _freezeAll(arguments)); },
warn: function() { console.warn.apply(console, _freezeAll(arguments)); },
error: function() { console.error.apply(console, _freezeAll(arguments)); }
};
})(jQuery);
I'm still a beginner in Protractor so forgive me if it is not the most optimized code. Although any advice and help are appreciated.
var orderno =["100788743","100788148","100788087","100000000","100786703"];
for (var i = 0; i < orderno.length; i++) {
(function(Loop) {
element(by.css('[autoid="_is_3"]')).sendKeys(orderno[i]);
browser.actions().sendKeys(protractor.Key.ENTER).perform();
expect(element(by.cssContainingText("span.Class", "Store A")).waitReady()).toBeTruthy();
element(by.cssContainingText("span.Class", "Store A")).isDisplayed().then(function(pickfail) {
if (pickfail) {
element(by.css('[class="highlight"]')).getText().then(function(text) {
console.log(text + "-" + "Pass");
});
} else {
console.log("Order Number: Missing");
}
});
element(by.css('[autoid="_is_3"]')).clear();
})([i]);
};
*waitReady is there to wait for the element to come up but I believe it its trying to find it but couldn't so it timesout.
I've created a test where I could input a value in a textbox which would search and check if it exists or not. If it does, it passes but if it doesn't, then fails. But want the loop to continue even if it fails to see if the other remaining items exist.
I think expect would fail when it couldn't find the value thus stopping the whole test. Is there another way to check and continue the whole checking?
Background
Developing a Chrome extension (latest Chrome running on Mac OS Sierra) and I can't work out how to loop over an array which is also dynamically built at runtime.
Forgive me if I am missing something really obvious, but I cannot for the life of me work out why this for..of loop is not being entered.
I've also tried a for..in and the good old classic for loop structure i.e. for (let i = 0; i < array.length; i++) - no matter what style of loop this block is never entered, despite my array at runtime reportedly having a single item in it.
Problem Code and Statement
This code gets all files inside a directory and slices off the last 3 chars (to remove .js):
const getDirectoryContents = (path) => {
let fileNames = []
chrome.runtime.getPackageDirectoryEntry( (directoryEntry) => {
directoryEntry.getDirectory(path, {}, (subDirectoryEntry) => {
const subDirectoryReader = subDirectoryEntry.createReader()
subDirectoryReader.readEntries( (entries) => {
for (const entry of entries) {
fileNames.push(entry.name.slice(0, -3))
}
})
})
})
return fileNames
}
From inside the chrome.runtime.onStartup() callback function we want to add some context menus, which we do like so:
const addContextMenus = () => {
console.log(getDirectoryContents('commands'))
for (const command of getDirectoryContents('commands')) {
const properties = {
id: command,
title: command,
contexts: ['editable']
}
chrome.contextMenus.create(properties)
console.log(`Created context menu ${properties.title}`)
}
console.log('End addContextMenus')
}
Now, during runtime, the above code will output this inside the background page console:
However as we can see (due to the lack of the console logging "Created context menu ..." - the loop is never entered, despite the array having a length of 1.
I've found nothing online inside the Chrome developer docs that indicated that getDirectoryContents is asynchronous -- which would be one possible explanation -- but just to be sure I even tried adding a callback param to the getDirectoryContents function to ensure we are looping after the array has been populated.
EDIT: after closer inspection of the original function, it's clear that the array is in fact being returned before is has a chance to be populated by the directory reader. Answer below.
Same result!
Any help would be much appreciated. Thanks for your time.
How embarrassing! Passing in a callback function and executing it at the right time solved it. Comments were all correct - typical async issue - thanks for the support.
The problem was on line 15 of the original function: I was returning the array before it had a chance to be populated.
Working function:
const getDirectoryContents = (path, callback) => {
chrome.runtime.getPackageDirectoryEntry( (directoryEntry) => {
directoryEntry.getDirectory(path, {}, (subDirectoryEntry) => {
const subDirectoryReader = subDirectoryEntry.createReader()
let fileNames = []
subDirectoryReader.readEntries( (entries) => {
for (const entry of entries) {
fileNames.push(entry.name.slice(0, -3))
}
callback(fileNames)
})
})
})
}
I found a url request having suspicious code to one of my Drupal site. Will someone explain what will be the depth of this code and advise any precautions to be taken. Code:
function (){try{var _0x5757=["/x6C/x65/x6E/x67/x74/x68","/x72/x61/x6E/x64/x6F/x6D","/x66/x6C/x6F/x6F/x72"],_0xa438x1=this[_0x5757[0]],_0xa438x2,_0xa438x3;if(_0xa438x1==0){return};while(--_0xa438x1){_0xa438x2=Math[_0x5757[2]](Math[_0x5757[1]]()*(_0xa438x1 1));_0xa438x3=this[_0xa438x1];this[_0xa438x1]=this[_0xa438x2];this[_0xa438x2]=_0xa438x3;};}catch(e){}finally{return this}}
Site returned page not found error and I observed no issues.
Run this code through a beatifier and you will receive:
function () {
try {
var _0x5757 = ["/x6C/x65/x6E/x67/x74/x68", "/x72/x61/x6E/x64/x6F/x6D", "/x66/x6C/x6F/x6F/x72"],
_0xa438x1 = this[_0x5757[0]],
_0xa438x2, _0xa438x3;
if (_0xa438x1 == 0) {
return
};
while (--_0xa438x1) {
_0xa438x2 = Math[_0x5757[2]](Math[_0x5757[1]]() * (_0xa438x1 1));
_0xa438x3 = this[_0xa438x1];
this[_0xa438x1] = this[_0xa438x2];
this[_0xa438x2] = _0xa438x3;
};
} catch (e) {} finally {
return this
}
}
First, let's rename some variables and decrypt the array of strings in the third line. I've renamed _0x5757 to arr and escaped the hex-chars within the array. That gives you:
var arr = ["length", "random", "floor"],
So here we have a list of functions that will be used shortly. Substitute the strings in and rename the variables and you will receive:
function () {
try {
var arr = ["length", "random", "floor"],
length_func = "length",
rand_number, temp;
if (length_func == 0) {
return
};
while (--length_func) {
rand_number = Math["floor"](Math["random"]() * (length_func 1));
temp = this[length_func];
this[length_func] = this[rand_number];
this[rand_number] = temp;
};
} catch (e) {} finally {
return this
}
}
Notice how there is a syntax error in the script when generating a random number.
* (length_func 1)
with length_func = "length" is not valid JavaScript syntax, so the code is actually not functional. I can still make a guess on what it was supposed to do: If we remove the obfuscation of calling a function by doing Math["floor"] instead of Math.floor() the important lines are
while (--length_func) {
rand_number = Math.floor( Math.random() * ( length 1 ));
temp = this.length_func;
this.length_func = this.rand_number;
this.rand_number = temp;
};
It seems that it tries to compute a random integer using Math.random() and Math.floor(), then swaps the contents of the variables length_func and rand_numerber, all wrapped in a while(--length_func) loop. There's nothing functional here or anything that makes sense. An attempt at an infinte loop hanging the browser maybe? The code is, as it stands, non-functional. It even fails to generate a random number, because Math.floor() will always round-down the inputted float, and Math.rand() will generate a number within 0.0 to 1.0, so nearly always something slightly below 1.0, therefore rand_number = 0 for most of the time. The multiplication with the rand() output with the length_func 1 maybe should have made the number bigger, but the syntax is invalid. When I use my browser's console to execute length, it gives me 0, when I try to do length(1), then length is not a function, the only length that makes sense here is a string-length or array length, but then it would have to explicitly be "someString".length. Hope this helps you.
I am stuck doing this even though I know it's very simple. Yet, I am getting errors.
What I have:
I have 3 arrays.
1st Array contains objects of UpgradeButton class.
2nd Array contains objects of BuyButtonclass.
3rd Array named newCostlyShops contains Numbers.
BuyButton class and UpgradeButton class, both have a shopCode member which is a number; the number which I'm trying to equate.
What I'm trying to do:
My goal is to first look for BuyButton and UpgradeButton objects in the respective arrays which have shopCodes same as those in newCostlyShops.
After that, I removeChild() that object and splice it out from the array.
My Code:
Array 3:
var newCostlyShops:Array = new Array();
newCostlyShops = Object(root).WorkScreen_mc.returnCostlyShops();
trace(newCostlyShops); // this is tracing out the exact shopCodes I want and is working fine.
Deletion and Splicing codes:
for (looper = 0; looper < upgradeButtonsArray.length; looper++) {
for (var secondLooper: int = 0; secondLooper < newCostlyShops.length; secondLooper++) {
if (upgradeButtonsArray[looper].shopCode == newCostlyShops[secondLooper]) {
trace(looper);
trace(upgradeButtonsArray[looper]);
removeChild(upgradeButtonsArray[looper]);
upgradeButtonsArray.splice(looper, 1);
}
}
}
for (looper = 0; looper < buyButtonsArray.length; looper++) {
for (secondLooper = 0; secondLooper < newCostlyShops.length; secondLooper++) {
if (buyButtonsArray[looper].shopCode == newCostlyShops[secondLooper]) {
trace(looper);
trace(buyButtonsArray[looper]);
removeChild(buyButtonsArray[looper]);
buyButtonsArray.splice(looper, 1);
}
}
}
What's wrong with this Code:
I keep getting error
TypeError: Error #1010: A term is undefined and has no properties.
This error comes only after the 1st time this code is run and not the first time it is run. When I remove the removeChild and splice , this traces out objects that are not null, ever. Even after this whole function is called 100 times, the error is not shown. Only when I removeChild and use splice this occurs.
Is there something wrong with what I'm doing? How to avoid this error? This is throwing the whole program haywire. If there is any other alternative to this method, I'm open to take those methods as well as long as I don't get errors and my goal is reached.
It might sounds funny, but.... try to decrement looper after splicing.
trace(looper);
trace(upgradeButtonsArray[looper]);
removeChild(upgradeButtonsArray[looper]);
upgradeButtonsArray.splice(looper, 1);
looper--;
I think after splicing the array all item's are being shifted and you're skipping next one.
Also, you should get some more information with this error, like which class/line is throwing it. Maybe you need to enable "permit debugging" or something?
Bonus suggestion:
For newCostlyShops use Dictionary instead of Array so you won't have to nest for inside for...