I just read the MDN page for the ES6 spread syntax, and most of the examples on the page worked, but the last one did not:
var obj = {"key1":"value1"};
function myFunction(x) {
console.log(x); // undefined
}
myFunction(...obj);
var args = [...obj];
console.log(args, args.length) //[] 0
I tried it in both Chrome and Firefox, and I'm running the latest browser versions, so the page says that the code ought to work.
Can anyone tell me what the problem is?
The issue is most likely that using the spread syntax with objects isn't currently supported with browsers. Doing something like this:
let inventory = {
apples: 3,
oranges: 4
}
let updatedInventory = {
...inventory,
grapes: 4
}
console.log(updatedInventory)
should print out this:
{"apples":3,"oranges":4,"grapes":4}
but as you can see, the browsers are throwing an error. If I recall correctly, object-spread was an ES7 proposal while array spreading was an ES6 one which is probably why object-spreading hasn't been fully implemented yet.
To try out the latest ES6/ES7 stuff that hasn't been implemented yet, you can use the online REPL provided by Babel. It's cool because you can see the equivalent ES5 code output on the right side.
If you put the above code example in the repl, you'll see the correct console output (on the bottom right).
Related
I'm trying to minify my javascript code using an online tool but everytime I try to do that I get this error:
// Error : Unexpected token: operator (>)
// Line : 1
// Col : 41
and this is on line 1:
var result = parsedObject.filter( audio => audio.filename === ''+audioFile+'' );
Could someone please advice on this issue and how to resolve it?
Thanks in advance.
Apparently, your minifier doesn't understand arrow functions, or it needs some option to be set to know you're doing ES2015+ ("ES6+") stuff. Your options are:
If it has an option, turn the option on; or
(you've now told us that you tried both https://jscompress.com/ and https://javascript-minifier.com/. jscompress.com has an "ECMAScript 2018 (via Babili)" tickbox in the upper right-hand corner that, when ticked, minifies your example code. I didn't find an option on javascript-minifier.com.)
If it doesn't, switch to a mninifier that does understand them; or
Don't use arrow functions. In this particular case that would look like:
var result = parsedObject.filter(function(audio) {
return audio.filename === ''+audioFile+'';
});
Use arrow function, but turn them into non-arrows before minifying by using a transpiler like Babel.
If you need to support any version of IE, you need to not send arrow functions to the browser (by using option 3 or 4 above). If you don't have to support IE, just modern browsers like Edge, Chrome, Firefox, and Safari, sending arrow functions to the browser is just fine.
Side note: You don't need those '' on either side of audioFile. If it's already a string, just remove them (=== audioFile). If it isn't already a string, just do one or the other, or use String(audioFile) to convert it, and do it once before the filter loop:
var audioFileString = String(audioFile); // or `'' + audioFile` or `audioFile + ''`
var result = parsedObject.filter(function(audio) {
return audio.filename === audioFileString;
});
The tool you are using does not support arrow functions (which are a relatively new feature).
You can:
Find a minifying tool which supports modern JS
Not use arrow functions in the first place
Use a tool to transform your JS to ES5 before minifying it
I am using the underscore js library (http://underscorejs.org/#filter) for functionality in my app.
Everything works as expected in chrome. However when I run the same code in IE11 I get an js error in the console
SCRIPT1002: Syntax error
File: OptionSrv.js, Line: 197, Column: 62
When I clicked the file to bring me to the error the cursor is placed on the => - is this a red herring or should there be another way of doing this which works in both chrome and IE?
Note if I comment out the line in IE I don't get the console error however this obviously isn't the fix that I need
var group = myOptions.filter(g => g.options.indexOf(option.OptionCode) > -1);
Internet Explorer 11 does not support arrow functions.
That's the g => g.options.indexOf(option.OptionCode) > -1 part of your code.
You can use a normal anonymous (or named) function instead here, and it should work fine:
var group = myOptions.filter(function(g) {
g.options.indexOf(option.OptionCode) > -1);
});
IE11 does not support ES6 syntax. If you want to write ES6 syntax like Arrow functions, you can run your code through a transpiler like Babel.
If you like your client-side code to be compatible with older browsers and you don't care about new syntax, simply use ES5 syntax :)
I'm coding a (free software) application (the MELT monitor, on GNU/Linux/Debian/x86-64) which embeds its specific web server.
See this question for gory details. If interested, look into commit 880419d370d749 on github then build it, run ./monimelt -Dweb,run -W localhost.localdomain:8086/ and open http://localhost.localdomain:8086/canvedit.html in your Firefox. FWIW I'm now trying to use canvases. Relevant code might go in my webroot/canvasedit.js, but I don't know what to code there yet...
Firefox is version 38 or 42. I don't understand all the details of ConsoleAPI.js which probably is very relevant to my question.
Let suppose I have a prototype
var fooproto = {
// perhaps adding a toString function is enough here?
// I want to show num & sons
};
then I make some objects using it:
var foo1 = Object();
foo1.num = 11;
foo1.__proto__ = fooproto;
var foo2 = Object();
foo2.num = 37;
foo2.sons = [foo1];
foo2.__proto__ = fooproto;
I would like
console.log("foo2=", foo2);
to show something like foo2=Foo#37[Foo#11] on the console (if possible with the italics)
Is there a way to change fooproto to make that work? I guess that some mechanism exist, since console.log is displaying nicely DOM objects.
It is probably a FAQ asked many times, but I was not able after several minutes of searching to find the appropriate search keywords. I don't know what terminology to use
I’m not developing for Firefox and apologies if you’ve already seen this, but I’ve stumbled across this:
Custom output in the Web Console on MDN, and specifically console API
Seems like something that might answer your needs.
A brief summary for anyone landing here from Google: There is a bug in iOS8 (on 64-bit devices only) that intermittently causes a phantom "length" property to appear on objects that only have numeric properties. This causes functions such as $.each() and _.each() to incorrectly try to iterate your object as an array.
I have filed an issue report (really a workaround request) with jQuery (https://github.com/jquery/jquery/issues/2145), and there is a similar issue on the Underscore tracker (https://github.com/jashkenas/underscore/issues/2081).
Update: This is a confirmed webkit bug. A fix was comitted on 2015-03-27, but there is no indication as to which version of iOS will have the fix. See https://bugs.webkit.org/show_bug.cgi?id=142792. Currently iOS 8.0 - 8.3 are known to be affected.
Update 2: A workaround for the iOS bug can be found in jQuery 2.1.4+ and 1.11.3+ as well as Underscore 1.8.3+. If you're using any of these versions, then the library itself will behave properly. However, it's still up to you to ensure that your own code isn't affected.
This question can also be called: "How can an object without a length have a length?"
I'm having a twilight zone kind of issue with mobile Safari (seen on both iPhones and iPads running iOS 8). My code has a lot of intermittent failures using the "each" implementation of both jQuery ($.each()) and Underscore (_.each()).
After some investigation, I discovered that in all cases of failure, the each function was treating my object as an array. It would then try to iterate it like an array (obj[0], obj[1], etc.) and would fail.
Both jQuery and Underscore use the length property to determine if an argument is an object or an array/array-like collection. For example, Underscore uses this test:
if (length === +length) { ... this is an array
My objects had no length parameter, yet they were triggering the above if statements. I double validated that there was no length by:
Sending the value of obj.length to the server for logging prior to calling each() (confirming that length was undefined)
Calling delete obj.length prior to calling each() (this didn't change anything.)
I have finally been able to capture this behavior in the debugger with an iPhone attached to Safari on a Mac.
The following picture shows that $.isArrayLike thinks that length is 7.
However, a console trace shows that length is undefined, as expected:
At this point I believe this is a bug in iOS Safari, especially since it's intermittent. I'd love to hear from others who's seen this problem and perhaps found a way to counter it.
Update
I was asked to create a fiddle of this, but unfortunately I can't. There seems to be a timing issue (which may even differ between devices) and I can't reproduce it in a fiddle. This is the minimum set of code I was able to repro the problem with, and it requires an external .js file. With this code happens 100% of the time on my iPhone 6 running 8.1.2. If I change anything (e.g. making the JS inline, removing any of the unrelated JS code, etc), the problem goes away.
Here is the code:
index.html
<html>
<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="script.js"></script>
</head>
<body>
Should say 3:
<div id="res"></div>
<script>
function trigger_failure() {
var obj = { 1: '1', 2: '2', 3: '3' };
print_last(obj);
}
$(window).load(trigger_failure);
</script>
</body>
</html>
script.js
function init_menu()
{
var elemMenu = $('#menu');
elemMenu
.on('mouseenter', function() {})
.on('mouseleave', function() {});
elemMenu.find('.menu-btn').on('touchstart', function(ev) {});
$(document).on('touchstart', function(ev) { });
return;
}
function main_init()
{
$(document).ready(function() {
init_menu();
});
}
function print_last(obj)
{
var a = $($.parseHTML('<div></div>'));
var b = $($.parseHTML('<div></div>'));
b.append($.parseHTML('foo'));
$.each(obj, function(key, btnText) {
document.getElementById('res').innerHTML = ("adding " + btnText);
});
}
main_init();
This isn't an answer but rather an analysis of what's going on under the covers after much testing. I hope that, after reading this, someone on either safari mobile side or the JavaScript VM on iOS side can take a look and correct the issue.
We can confirm that the _.each() function is treating js objects {} as arrays [] because the safari browser returns the 'length' property of an object as an integer. BUT ONLY IN CERTAIN CASES.
If we use an object map where the keys are integers:
var obj = {
23:'some value',
24:'some value',
25:'some value'
}
obj.hasOwnProperty('length'); //...this comes out as 'false'
var length = obj.length; //...this returns 26!!!
Inspecting with the debugger on mobile Safari browser we clearly see that obj.length is "undefined". However stepping to next line:
var length = obj.length;
the length variable is clearly being assigned the value 26 which is an integer. The integer part is important because the bug in underscore.js occurs at these two lines in the underscore.js code:
var i, length = obj.length;
if (length === +length) { //... it treats an object as an array because
//... it has assigned the obj (which has no own
//... 'length' property) an actual length (integer)
However if we were to change the object in question just slightly and add a key-value pair where the key is a string (and is the last item in object) such as:
var obj = {
23:'some value',
24:'some value',
25:'some value',
'foo':'bar'
}
obj.hasOwnProperty('length'); //...this comes out as 'false'
var length = obj.length; //...this now returns 'undefined'
More interestingly, if we change the object again and a key-value pair such as:
var obj = {
23:'some value',
24:'some value',
25:'some value',
75:'bar'
}
obj.hasOwnProperty('length'); //...this comes out as 'false'
var length = obj.length; //...this now returns 76!
It appears that the bug (wherever it is happening: Safari/JavaScript VM) looks at the key of last item in the object and if it is an integer adds one (+1) to it and reports that as a length of the object...even though obj.hasOwnProperty('length') comes back as false.
This occurs on:
some iPads (but NOT ALL that we have) with iOS version 8.1.1, 8.1.2, 8.1.3
the iPads that it does occur on, it happens consistently...every time
only Safari browser on iOS
This does not occur on:
any iPhones we tried with iOS 8.1.3 (using both Safari and Chrome)
any iPads with iOS 7.x.x (using both Safari and Chrome)
chrome browser on iOS
any js fiddles we attempted to create using the above mentioned iPads that consistently created the error
Because we can't really prove it with a jsFiddle we did the next best thing and got a screen capture of it stepping through the debugger. We posted the video on youTube and it can be seen at this location:
https://www.youtube.com/watch?v=IR3ZzSK0zKU&feature=youtu.be
As stated above this is just an analysis of the problem in more detail. We are hoping someone with more understanding under the hood can comment on the situation.
One simple solution is to NOT USE _.each function (or the jQuery equivalent). We can confirm that using angular.js forEach function remedies this issue. However, we use underscore.js pretty extensively and _.each is used nearly everywhere we iterate through arrays/collections.
Update
This was confirmed as a bug and there is now a fix for this bug on WebKit as of 2015-03-27:
fix: http://trac.webkit.org/changeset/182058
original bug report: https://bugs.webkit.org/show_bug.cgi?id=142792
For anyone looking at this and using jQuery, just a heads' up that this has now been fixed in versions 2.1.4 and 1.11.3, which specifically only contain a hot-fix to the above issue:
http://blog.jquery.com/2015/04/28/jquery-1-11-3-and-2-1-4-released-ios-fail-safe-edition/
Upgrading to the latest version of lodash (3.10.0) fixed this problem for me.
Take note that there is a breaking change in this lodash version with _.first vs _.take.
For anyone who isn't familiar with lodash - it's a fork of underscore that is nowadays (imo) a better solution.
Really a big thanks to #OzSolomon for explaning, describing and figuring out this problem.
If I would be able to give a bounty to a question I would've done it.
I have worked a while with something that could be similar or the same problem. The company I work for have a game that uses KineticJS 4.3.2. Kinetic makes intensive use of Arrays when grouping graphic objects on a html5 canvas.
The problems that occurred was that push was missing on the array sometimes or that properties on the object that was stored in the array was missing. The problem occurred in Safari and when run from homescreen in iOS8. For some reason the problem did not occur when run in Chrome on iOS. The problem also did not occur with a debugger connected.
After a lot of testing and searching on the net we think this is a JIT optimisation bug in webkit on iOS. A colleague found the following links.
TypeError: Attempted to assign to readonly property. in Angularjs application on iOS8 Safari
https://github.com/angular/angular.js/issues/9128
http://tracker.zkoss.org/browse/ZK-2487
Currently we have made a workaround by removing dot-notation when accessing the array that did not work (.children was replaced with [ "children" ]).
I have not been able to create a jsFiddle.
The problem is the user code, not iOS8 webkit.
var obj = {
23: 'a',
24: 'b',
25: 'c'
}
The map keys above are integers, when strings should be used. It's not a valid map by the javascript standard so the behaviour is undefined, and Apple is choosing to interpret `obj' as an array of 26 elements (indexes 0 to 25 inclusive).
Use string keys and the length problem will go away:
var obj = {
'23': 'a',
'24': 'b',
'25': 'c'
}
I am wondering if JavaScript has an enhanced for loop syntax that allows you to iterate over arrays. For example, in Java, you can simply do the following:
String[] array = "hello there my friend".split(" ");
for (String s : array){
System.out.println(s);
}
output is:
hello
there
my
friend
Is there a way to do this in JavaScript? Or do I have to use array.length and use standard for loop syntax as below?
var array = "hello there my friend".split(" ");
for (i=0;i<array.length;i++){
document.write(array[i]);
}
JavaScript has a foreach-style loop (for (x in a)), but it is extremely bad coding practice to use it on an Array. Basically, the array.length approach is correct. There is also a a.forEach(fn) method in newer JavaScripts you can use, but it is not guaranteed to be present in all browsers - and it's slower than the array.length way.
EDIT 2017: "We'll see how it goes", indeed. In most engines now, .forEach() is now as fast or faster than for(;;), as long as the function is inline, i.e. arr.forEach(function() { ... }) is fast, foo = function() { ... }; arr.forEach(foo) might not be. One might think that the two should be identical, but the first is easier for the compiler to optimise than the second.
Belated EDIT 2020: There is now for (const item of iterable), which solves the downsides of using for (item in iterable).
Using the latest versions of JavaScript available to most modern browsers, you can do this:
array.forEach(function(x){
document.write(x);
});
Details are at https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/forEach. If you're worried that a browser may not have support for this, you can add it yourself, using a (hopefully minified) version of the implementation that they have listed under "Compatibility".
This is a bit outdated, but this is a minified compatibility version of forEach that I derived from Mozilla's page a few years ago:
if(!Array.prototype.forEach){Array.prototype.forEach=function(b){if(typeof b!="function"){throw new TypeError()}var a=this.length,d=arguments[1],c;for(c=0;c<a;c++){if(c in this){b.call(d,this[c],c,this)}}}};
I've never run into any issues with this, but the implementation on Mozilla's page has since been expanded with some additional checks and code to make it compatible with ECMA-262, Edition 5, 15.4.4.18.
I have a file called common.js that I use and include on all of my pages to include this, as well as all of the other "Array extras" that were introduced with JavaScript 1.6, as listed at https://developer.mozilla.org/en/JavaScript/New_in_JavaScript/1.6#Array_extras. (I've been meaning to get this updated and published for public use.)
This may not be the fastest approach (see http://jsperf.com/for-vs-foreach/15 for some specifics - thanks for the link, Amadan) - but there is something to be said for conciseness and maintainability, etc. Additionally, it'll be very interesting to see how much of this disparity is optimized away by further JavaScript engine improvements over the next few months and years. :-)
In ES2015(ES6), you can use the for-of loop. It's supported in most browser with the exception of IE.
let array = [10, 20, 30];
for (let value of array) {
console.log(value);
}
See the Mozilla explanation here
You can do for(s in array), but be careful, it's not the same as a foreach.
In this case s is the key (index), not the value. You also need to use hasOwnProperty because in loops though the object's prototype also.
for(s in array){
if(array.hasOwnProperty(s)){
console.log(array[s]);
}
}
EDIT: As #Amadan pointed out, hasOwnProperty does iterate properties when they're added like this: array.test = function(){}. I suggest not using for...in.
EDIT2: If your using a modern web browser (anything that isn't IE < 9), you can use Array.forEach). #ziesemer points out that Mozilla has a shim for this if you need to support IE < 9.
array.forEach(function(s){
console.log(s);
});
NOTE: Personally I use jQuery for my JavaScript projects, and I use $.each.
$.each(array, function(i,s){
console.log(s);
});
There's the "forEach" method on the Array prototype in newer JavaScript engines. Some libraries extend the prototype themselves with a similar method.
Try this:
var errorList = new Array();
errorList.push("e1");
errorList.push("e2");
for (var indx in errorList) {
alert(errorList[indx]);
}
x = [1,2,3];
for (i in x) {
console.log(i);
}