{"id":311,"date":"2009-03-15T04:04:11","date_gmt":"2009-03-15T04:04:11","guid":{"rendered":"http:\/\/mozillacalendar2.wordpress.com\/2009\/03\/15\/philipps_developer_notes_using_1\/"},"modified":"2012-03-27T13:54:13","modified_gmt":"2012-03-27T13:54:13","slug":"philipps_developer_notes_using_1","status":"publish","type":"post","link":"https:\/\/blog.mozilla.org\/calendar\/2009\/03\/philipps_developer_notes_using_1\/","title":{"rendered":"Philipp&#8217;s Developer Notes: Using JavaScript 1.7 Iterators to simplify code"},"content":{"rendered":"<p>The list of new features in <a href=\"https:\/\/developer.mozilla.org\/en\/New_in_JavaScript_1.7\">JavaScript 1.7<\/a> have been out for a while but only since the move to Trunk (Gecko 1.9.1 codebase) have we taken a look into it.<\/p>\n<p>The concept of iterators is nothing new, the way they are implemented in JavaScript initially confused me. Consider the following bits of code:<\/p>\n<pre>\nfunction range(begin, end) {\nfor (let i = begin; i++; i &lt; end) {\nyield i;\n}\n}\nlet it = range(10, 20);\n<\/pre>\n<p>What is this!? A function with a loop that doesn&#8217;t return anything and contains a strange statement called yield? Without much reading, my next though was: why does |it| contain anything? Shouldn&#8217;t |it| be undefined? The function doesn&#8217;t return anything, so why should yield make a difference?<\/p>\n<p>I guess the return &#8220;issue&#8221; just takes a bit getting used to. Most iterator functions are very small, so |yield| should be easy to spot. Now I&#8217;d like you to see why this strange construct is indeed very useful. Consider this (old) bit of code. It was used when turning an ICS string into an item:<\/p>\n<pre>\nfor (var attprop = icalcomp.getFirstProperty(\"ATTENDEE\");\nattprop;\nattprop = icalcomp.getNextProperty(\"ATTENDEE\")) {\nvar att = new CalAttendee();\natt.icalProperty = attprop;\nthis.addAttendee(att);\n}\n<\/pre>\n<p>A multi-line for() looks very cumbersome even aside from the fact that the different line lengths make it harder to read, don&#8217;t you think? Now lets see what magic we can do with iterators:<\/p>\n<pre>\nfor (let attprop in cal.ical.propertyIterator(icalcomp, \"ATTENDEE\")) {\nlet att = cal.createAttendee();\natt.icalProperty = attprop;\nthis.addAttendee(att);\n}\n<\/pre>\n<p>You&#8217;ll notice a few things changed. The loop now uses the newly introduced property iterator and only one line (even though its a bit longer). You&#8217;ll also see use of the new &#8220;cal&#8221; namespace, which probably deserves its own blog post. While this may look like a minimal change please consider a lot of such for loops are used when turning an ICS string into an item. Also I feel this makes the formerly quite native code pattern (getFirstXXX, getNextXXX) fit more naturaly into JavaScript.<\/p>\n<p>We can go even further, using more language features of JavaScript 1.7. This bit of code used in the same context:<\/p>\n<pre>\nthis.mCategories = [];\nfor (var catprop = icalcomp.getFirstProperty(\"CATEGORIES\");\ncatprop;\ncatprop = icalcomp.getNextProperty(\"CATEGORIES\")) {\nthis.mCategories.push(catprop.value);\n}\n<\/pre>\n<p>Can be compromised into a single line, using iterators as described above and array comprehensions:<\/p>\n<pre>\nthis.mCategories = [ catprop.value for (catprop in cal.ical.propertyIterator(icalcomp, \"CATEGORIES\")) ];\n<\/pre>\n<p>I&#8217;ll admit this line is a bit long. I have found no sensible way to split it, comments welcome. You&#8217;re surely interested how to create something that can be used as an iterator so that you can use it in your own code. I&#8217;ll give you the source for the above cal.ical.propertyIterator:<\/p>\n<pre>\nfunction cal_ical_propertyIterator(aComponent, aProperty) {\nreturn {\n__iterator__: function icalPropertyIterator(aWantKeys) {\ncal.ASSERT(aWantKeys, \"Please use for() on the property iterator\");\nlet propertyName = (aProperty || \"ANY\");\nfor (let prop = aComponent.getFirstProperty(propertyName);\nprop;\nprop = aComponent.getNextProperty(propertyName)) {\nyield prop;\n}\n}\n};\n}\n<\/pre>\n<p>Looks quite similar to the old code, doesn&#8217;t it? The concept is quite simple. We return an object that implements the special __iterator__ method. This is where we put the old loop code. Then we &#8220;yield&#8221; the value that should be availibe in our new loop code. The argument aWantKeys differentiates if only the key is wanted or not. If the iterator is used in a for() loop, then aWantKeys is true. If it is used in a for each() loop, then the argument is false.<\/p>\n<pre>\nfor (let key in myIterator()) {\n\/\/ A for(...in..) loop goes through all keys in the array or iterator.\n\/\/ The aWantKeys argument is true.\n}\nfor each (let [key, value] in myIterator()) {\n\/\/ Standard object iterators return an array with key and value\n\/\/ when aWantKeys is false.\n}\nfor each (let value in myOtherIterator()) {\n\/\/ Some iterators prefer only returning the value in this case.\n}\n<\/pre>\n<p>The above code is of course only an example. You are of course free to return anything you want in your iterator.<\/p>\n<p>Note that you can use the __iterator__ on any object, it doesn&#8217;t have to be an object that has no other properties.<\/p>\n<pre>\nfunction calPropertyBag() {\nthis.mData = {};\n}\ncalPropertyBag.prototype = {\nmData: null,\n__iterator__: function cpb_iterator(aWantKeys) {\nfor (let key in this.mData) {\nyield (aWantKeys ? key : [key, this.mData[key]]);\n}\n\/* In this case, instead of the above you can shorten this iterator to:\nreturn Iterator(this.mData, aWantKeys);\n*\/\n},\n\/* Also implement other methods like setProperty,deleteProperty,... *\/\n}\n<\/pre>\n<p>See how this basic property bag now has an iterator that can be used directly? Without the iterator, using this code:<\/p>\n<pre>\nvar bag = new calPropertyBag();\nfor (let key in bag) {\ndump(\"Key: \" + key + \"n\");\n}\n<\/pre>\n<p>Would give you all keys in the object: mData, setProperty, deleteProperty and so on. With the new iterator, the above code would rather give you the actual properties in the bag, since the __iterator__ yields all keys in this.mData.<\/p>\n<p>I hope this inspires you, regardless of if you are working on a Sunbird\/Lightning extension, or any other piece of Javascript code for that matter. Please do feel free to correct me, I&#8217;m quite new to these features so not everything might be 100% correct.<\/p>\n<p>If I come across more new langauges features, I&#8217;ll keep you posted!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The list of new features in JavaScript 1.7 have been out for a while but only since the move to Trunk (Gecko 1.9.1 codebase) have we taken a look into it. The concept of iterators is nothing new, the way they are implemented in JavaScript initially confused me. Consider the following bits of code: function [&hellip;]<\/p>\n","protected":false},"author":460,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[10810],"tags":[],"_links":{"self":[{"href":"https:\/\/blog.mozilla.org\/calendar\/wp-json\/wp\/v2\/posts\/311"}],"collection":[{"href":"https:\/\/blog.mozilla.org\/calendar\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.mozilla.org\/calendar\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/calendar\/wp-json\/wp\/v2\/users\/460"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/calendar\/wp-json\/wp\/v2\/comments?post=311"}],"version-history":[{"count":0,"href":"https:\/\/blog.mozilla.org\/calendar\/wp-json\/wp\/v2\/posts\/311\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.mozilla.org\/calendar\/wp-json\/wp\/v2\/media?parent=311"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.mozilla.org\/calendar\/wp-json\/wp\/v2\/categories?post=311"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.mozilla.org\/calendar\/wp-json\/wp\/v2\/tags?post=311"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}