for(;;)
and for a in b
var divs = document.querySelectorAll('div'); for (var i = 0, c = divs.length; i < c; i++) { console.log(divs[i].innerHTML); }
var obj = {a: 1, b: 2, c: 3}; for (var i in obj) { console.log(i, obj[i]); }
map(), filter()
elements. var numbers = [1, 2, 3, 4, 5]; var doubled = numbers.map(function (item) { return item * 2; }); console.log(doubled);
var numbers = [1, 2, 3, 4]; var doubled = [i * 2 for each (i in numbers)]; console.log(doubled); // [2, 4, 6, 8]
for in
and encapsulate the process of getting the next item in the list of objects. var object = [1,2,3]; for (var i in object) { console.log(i, object[i]); }
var object = {a:1, b:2, c:3}; for (var i in object) { console.log(i, object[i]); }
var object = 'abc'; for (var i in object) { console.log(i, object[i]); }
var object = document.querySelectorAll('div'); for (var i in object) { if (object.hasOwnProperty(i)) { console.log(i, object[i]); } }
next()
method that returns the next object in the sequence. This method can cause a StopIteration
exception when the sequence has ended.next()
method or using for...in
or for each
(FF) constructs. var lang = { name: 'JavaScript', birthYear: 1995 }; var it = Iterator(lang); // <<<
it
object can be iterated using next()
var pair = it.next(); // ["name", "JavaScript"] pair = it.next(); // ["birthYear", 1995] pair = it.next(); // StopIteration
for...in
or for each
. The traversal will be stopped as soon as the exception StopIteration
is thrown: var it = Iterator(lang); for (var pair in it) { console.log(pair); // 2 [key, value] }
Iterator()
is that it bypasses only its own properties (remember the example of traversing querySelectorAll
) while traversing you do not need to check object.hasOwnProperty
.Iterator()
can also be used for an array: var langs = ['JavaScript', 'Python', 'C++']; var it = Iterator(langs); for (var pair in it) { console.log(pair); // [index, language] }
Iterator
function Iterator
second argument, then only the indices will be iterated: var langs = ['JavaScript', 'Python', 'C++']; var it = Iterator(langs, true); for (var pair in it) { console.log(pair); // 0, 1, 2 }
let
and destructive assignment (FF only): var langs = ['JavaScript', 'Python', 'C++']; var it = Iterator(langs); for (let [i, lang] in it) { // FF console.log(i + ': ' + lang); // "0: JavaScript" . }
for in
without Iterator
. function Range(low, high){ this.low = low; this.high = high; }
next()
method that will return an element of the sequence and throw a StopIteration
exception at the end. function RangeIterator(range){ this.range = range; this.current = this.range.low; } RangeIterator.prototype.next = function(){ if (this.current > this.range.high) throw StopIteration; else return this.current++; };
var ri = new RangeIterator(new Range(1, 10)); ri.next(); // ... ri.next(); // StopIteration
RangeIterator
constructor, RangeIterator
will use the __iterator__
method __iterator__
the Range
object. This method will be called when trying to iterate over the elements of a Range
object. This method should return a RangeIterator
that includes the iterator logic. Range.prototype.__iterator__ = function(){ return new RangeIterator(this); }; var range = new Range(3, 5); for (var i in range) { console.log(i); // 3, 4, 5 }
yield
becomes a generator.<script type="application/javascript;version=1.7">
next()
will perform the function body until a yield
is returned that returns a result. If the function ends with return
or the function body ends, the StopIteration
exception is StopIteration
and the iterator stops its operation. function simpleGenerator(){ yield "first"; yield "second"; yield "third"; for (var i = 0; i < 3; i++) { yield i; } } var g = simpleGenerator(); console.log(g.next()); // "first" console.log(g.next()); // "second" console.log(g.next()); // "third" console.log(g.next()); // 0 console.log(g.next()); // 1 console.log(g.next()); // 2 console.log(g.next()); // StopIteration
__iterator__
method of an __iterator__
object. This will significantly reduce the amount of code. Let's rewrite the example from Range using generators: function Range(low, high){ this.low = low; this.high = high; } Range.prototype.__iterator__ = function(){ for (var i = this.low; i <= this.high; i++) { yield i; } }; var range = new Range(3, 5); for (var i in range) { console.log(i); // 3, 4, 5 }
function fibonacci(){ var fn1 = 1; var fn2 = 1; while (1){ var current = fn2; fn2 = fn1; fn1 = fn1 + current; yield current; } } var sequence = fibonacci(); console.log(sequence.next()); // 1 console.log(sequence.next()); // 1 console.log(sequence.next()); // 2 console.log(sequence.next()); // 3 console.log(sequence.next()); // 5 console.log(sequence.next()); // 8 // ...
StopIteration
or return
. Rewrite the Fibonacci example using the limit
argument. function fibonacci(limit){ var fn1 = 1; var fn2 = 1; while (1) { var current = fn2; fn2 = fn1; fn1 = fn1 + current; if (limit && current > limit) { return; } yield current; } } var sequence = fibonacci(7); console.log(sequence.next()); // 1 console.log(sequence.next()); // 1 console.log(sequence.next()); // 2 console.log(sequence.next()); // 3 console.log(sequence.next()); // 5 console.log(sequence.next()); // StopIteration
next()
method, the Iterator-Generator has a send()
method that allows you to change the internal state of the generator. The value passed to send()
will be perceived as the result of the last yield
that stopped the generator. You must start the generator with next()
before calling send()
.send()
to restart the sequence: function fibonacci(){ var fn1 = 1; var fn2 = 1; while (1){ var current = fn2; fn2 = fn1; fn1 = fn1 + current; var reset = yield current; if (reset){ fn1 = 1; fn2 = 1; } } } var sequence = fibonacci(); print(sequence.next()); // 1 print(sequence.next()); // 1 print(sequence.next()); // 2 print(sequence.next()); // 3 print(sequence.next()); // 5 print(sequence.send(true)); // 1 print(sequence.next()); // 1 print(sequence.next()); // 2 print(sequence.next()); // 3
throw()
method. This method takes one argument, which will throw the generator ( throw value;
). This exception will be thrown from the current stopped generator context (in the place where the last yield
).next()
method), then throw()
will call next()
and throw an exception at the yield
.close()
method:finally
blocks will be executed.finally
block throws an exception other than StopIteration
, then the exception will be thrown into the context from which the close()
method was called var doubles = [i * 2 for (i in it)];
var it2 = (i * 2 for (i in it)); print(it2.next()); // print(it2.next()); //
var result = doSomething(i * 2 for (i in it));
innerHTML
and if innerHTML
matches the domain, output the domain to the console. <a href="http://ya.ru/"> </a> <a href="http://google.ru/"></a> <a href="http://habrahabr.ru/"></a> <a href="http://twitter.com/">twitter.com</a>
var aList = document.querySelectorAll('a'); for (var i = 0, c = aList.length, a, content, domain; i < c; i++) { a = aList[i]; content = a.innerHTML; domain = a.getAttribute('href').replace('http://', '').split('/').shift(); if (content === domain) { console.log(domain); } }
// type="application/javascript;version=1.7" var aDomainContentGenerator = function () { var aList = document.querySelectorAll('a'); for (var i = 0, c = aList.length, a, content, domain; i < c; i++) { a = aList[i]; content = a.innerHTML; domain = a.getAttribute('href').replace('http://', '').split('/').shift(); yield {domain: domain, content: content}; } }; var ancors = aDomainContentGenerator(); // - // for c if, . , var ancorsWithSameContentAndDomain = (item for (item in ancors) if (item.content === item.domain)); for (item in ancorsWithSameContentAndDomain) { console.log(item.domain); }
aDomainContentGenerator()
generator for other purposes. function Tree(left, label, right) { this.left = left; this.label = label; this.right = right; } // function inorder(t) { if (t) { for (var item in inorder(t.left)) { yield item; } yield t.label; for (var item in inorder(t.right)) { yield item; } } } // function make(array) { // : if (array.length == 1) return new Tree(null, array[0], null); return new Tree(make(array[0]), array[1], make(array[2])); } var tree = make([[['a'], 'b', ['c']], 'd', [['e'], 'f', ['g']]]); // for (var node in inorder(tree)) { console.log(node); // a, b, c, d, ... }
for (var item in inorder(t.right)) { yield item; }
yield for inorder(t.right);
for (var node in inorder(tree)) { console.log(node); // a, b, c, d, ... }
for (let node : inorder(tree)) { console.log(node); // a, b, c, d, ... }
Function#bind
, Object.defineProperty
so the compiled version only works in the TC creator’s browser, Google Chrome :). The compiled code of the generator is obtained monstrous with the use of a finite state machine and dances around try catch
and other perversions. It turns out this "code": function Tree(left, label, right) { this.left = left; this.label = label; this.right = right; } function inorder(t) { var $that = this; return { __traceurIterator__: function() { var $state = 20; var $storedException; var $finallyFallThrough; var $__1; var $__2; var $__3; var $__4; var $result = { moveNext:(function() { while(true) try { switch($state) { case 20: if(t) { $state = 7; break; } else { $state = 15; break; } case 7: $__2 = inorder(t.left).__traceurIterator__(); $state = 8; break; case 8: if($__2.moveNext()) { $state = 2; break; } else { $state = 5; $finallyFallThrough = 4; break; } case 2: $__1 = $__2.current; $state = 3; break; case 3: $result.current = $__1; $state = 8; return true; case 5: { if($__2.close) $__2.close(); } $state = 6; break; case 4: $result.current = t.label; $state = 10; return true; case 10: $__4 = inorder(t.right).__traceurIterator__(); $state = 19; break; case 19: if($__4.moveNext()) { $state = 13; break; } else { $state = 16; $finallyFallThrough = 15; break; } case 13: $__3 = $__4.current; $state = 14; break; case 14: $result.current = $__3; $state = 19; return true; case 16: { if($__4.close) $__4.close(); } $state = 17; break; case 6: $state = $finallyFallThrough; break; case 17: $state = $finallyFallThrough; break; case 15: $state = 22; case 22: return false; case 21: throw $storedException; default: throw "traceur compiler bug: invalid state in state machine" + $state; } } catch($caughtException) { $storedException = $caughtException; switch($state) { case 8: $state = 5; $finallyFallThrough = 21; break; case 2: $state = 5; $finallyFallThrough = 21; break; case 3: $state = 5; $finallyFallThrough = 21; break; case 19: $state = 16; $finallyFallThrough = 21; break; case 13: $state = 16; $finallyFallThrough = 21; break; case 14: $state = 16; $finallyFallThrough = 21; break; default: throw $storedException; } } }).bind($that) }; return $result; } }; } function make(array) { if(array.length == 1) return new Tree(null, array[0], null); return new Tree(make(array[0]), array[1], make(array[2])); } var tree = make([[['a'], 'b',['c']], 'd',[['e'], 'f',['g']]]); { var $__0 = inorder(tree).__traceurIterator__(); try { while($__0.moveNext()) { try { throw undefined; } catch(node) { node = $__0.current; { console.log(node); } } } } finally { if($__0.close) $__0.close(); } }
Code for half the article - left to show that everything is bad with him ...Function#bind
and Object.defineProperty
, in any case, TC turns the code into a brake monster and therefore its use is questionable.Source: https://habr.com/ru/post/122620/
All Articles