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 的幾樣新特性的份上,原諒我吧!^_^!