pseudo
英 ['sju:dəʊ] 美 ['su:doʊ]
adj.假的,虚伪的
n.[口]假冒的人,伪君子javascriptpseudo-array
英 [s'ju:dəʊər'eɪ] 美 [s'ju:dəʊər'eɪ]
[计] 伪数组java
无论之前知道不知道,至少立刻会知道git
jQuery 对象是一个伪数组。es6
var $obj = jQuery(); Array.isArray($obj); // false
jQuery.fn
是 jQuery.prototype
的简写github
jQuery.fn === jQuery.prototype; // true
既然 jQuery 对象是伪数组,那 ES6 的 for...of 想用在 jQuery 对象上就不会那么顺利。毕竟 jQuery 尚未按 ES6 重写。数组
那么,想用 for..of 遍历 jQuery 对象中的 DOM 引用,就本身实现吧——这得从 iterable 和 iterator 开始。dom
The for...of statement creates a loop Iterating over iterable objects (including Array, Map, Set, arguments object and so on), invoking a custom iteration hook with statements to be executed for the value of each distinct property.ide
引用自:for...of statement | MDN函数
为了使某个对象成为可迭代对象象,它必须实现 @@iterator 方法,也就是说,它得有一个 key 是 Symbol.iterator
的属性。说人话,就是必须得实现这么个东东:oop
jQuery.fn[Symbol.iterator] = ....
而这个所谓的 @@iterator 方法,返回的是一个迭代器。迭代器这活也不是随便谁都能干的,它必须得有一个 next()
方法,而这个 next()
方法每次调用,都返回下一个迭代对象。
固然迭代对象也是有标准的,它必须是这么个结构:
{ done: "(boolean),true 表示迭代完成,false 表示还有下一个", value: "这个才是正主,for...of 迭代出来的值" }
注意 done
这个小坑,其它语言中一般是用 hasNext()
或者 hasMore()
之类的来判断是否有下一个值,而 javascript 是用 done
来判断,它们的逻辑意思正好相反,因此千万注意不要给错了值。
注:Symbol 是 ES6 中引入的新的键类型。以前的键类型只能是字符串,而在 ES6 中,有两种了。关于 Symbol,能够参阅 【探秘ES6】系列专栏(八):JS的第七种基本类型Symbols 和 Symbol - JavaScript | MDN
知道了规矩,实现起来就好办了
jQuery.fn[Symbol.iterator] = function() { return (function(_this) { var index = 0; return { next: function() { return { done: index >= _this.length, value: _this[index++] }; } }; })(this); };
for (var dom in $("div")) { console.log(dom); }
正确执行,经过……话虽如此,代码写起来好累。因此,其实应该用 Generator……
ES6 的又一新特性,Generator 对象(生成器对象),简直就是为迭代而生的。每一个生成器对象都符合上面提到的 iterable 和 iterator 两个规矩。换句话说,生成器对象既是一个可迭代对象,又是一个迭代器,而它做为可迭代对象的时候,返回的迭代器就是它本身。
然而生成器对象并非 new 出来的,而是经过 generator function(生成器函数)生成的;生成器函数得本身写,又不能 new Generator()
,那么这个生成器对象从哪里来呢?固然是生成器函数生成的,并且这会用到新语法,以及新的关键字 yield
。
生成器函数的定义与普通函数略有不一样,形式上的区别是,它在 function
关键字后加了一个 *
号,就像这样:
function *aGenerator() { ... }
生成器函数在内容上的区别就是,它的内容其实并非它本身的内容,而是描述了它产生生成器对象的行为。
有点乱,来捋一捋:
next()
方法用来返回迭代值(以及判断是否完成迭代)捋清楚了,来讲生成器函数的内容——其实就是干上面最后一条描述的事情:描述每次迭代返回的值,以及是否完成迭代。这是与普通 function
彻底不一样的语法,它是怎么作到的呢?凭空提及来太吃力,先上代码
function *aGenerator() { yield 1; yield 2; yield 3; }
每次 yield
,就至关于一次经过 next()
返回值,也就上面提到的迭代对象的 value
属性。那么 done
属性如何肯定呢?若是从生成器函数返回,就 done 了。这有两种状况,一种是天然执行完全部语法,函数结束返回;另外一种是在函数体中调用 return
返回。
新问题:眼看例中 3 个 yield
语句排在一块儿,不是“啪啪啪”一会儿就搞完了?最后就 next()
出来一个 3
了事?
非也,yield
返回值与 return
不一样。yield
反回值(也就是 next()
)以后会将代码暂停在那个位置,等下一次 next()
的时候,继续执行,到下一个 yield
再暂停……如此直到显示或隐匿的 return
。
jQuery.fn[Symbol.iterator]
jQuery.fn[Symbol.iterator] = function*() { for (var i = 0; i < this.length; i++) { yield this[i]; } };
比上一个实现简单了很多吧,可是你觉得这是最简单的么……
除了能够用 yield
返回值以外,还能够用 yield *
返回可迭代对象。这时,控制权会暂时交给这个可迭代对象,由它接替实现 next()
,直到 done
,再由当前生成器函数中的下一个 yield
接手继续。形象一点的理解——这个过程有点像树型结构的深度遍历。
由于原生数组也是可迭代对象,因此能够取个巧
jQuery.fn[Symbol.iterator] = function*() { yield *[].slice.call(this); };
上面说了一通,用了 N 种方法,无非是讲解 ES6 的新特性而已。要为 jQuery 实现 for...of 遍历,最简单的方法实际上是拿来主义:
jQuery.fn[Symbol.iterator] = [][Symbol.iterator];
jQuery 对象是一个伪数组,它的每个元素都是一个 DOM(或原对象)而不是被封装的 jQuery 对象,因此,用 for..of
遍历的时候,和用 jQuery.fn.each()
遍历同样,若是想继续在每一个元素上使用 jQuery 的特性,就要记得用 jQuery()
包装。
// for...of for (var dom in $("span")) { var $span = $(dom); } // jQuey.fn.each $("span").each(function() { var $span = $(this); });
其实,我不当心又骗了你们。jQuery 压根没有实现 for...of
的必要,即便没有 ES6,用 for...in
遍历 jQuery 对象也是一件很悲催的事情,不信你试试。
jQuery 的遍历,绝对应该用 jQuery.fn.each()
。
可是,看在我要以此为例来讲 ES6 的几样新特性的份上,原谅我吧!^_^!