jQuery中DOM对象和伪数组 jQuery 对象的互相转换

  • jQuery 对象是伪数组

  1. jQuery 对象是一个伪数组。
var $obj = jQuery();Array.isArray($obj);    // false
  1. jQuery.fn 是 jQuery.prototype 的简写
jQuery.fn === jQuery.prototype;    // true

提出问题

既然 jQuery 对象是伪数组,那 ES6 的 for...of 想用在 jQuery 对象上就不会那么顺利。毕竟 jQuery 还没有按 ES6 重写。

那么,想用 for..of 遍历 jQuery 对象中的 DOM 引用,就自己实现吧——这得从 iterable 和 iterator 开始。

iterable(可迭代对象) 和 iterator(迭代器)

不以规矩,不成方圆

为了使某个对象成为可迭代对象象,它必须实现 @@iterator 方法,也就是说,它得有一个 key 是 Symbol.iterator 的属性。说人话,就是必须得实现这么个东东:

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……

Generator

ES6 的又一新特性,Generator 对象(生成器对象),简直就是为迭代而生的。每个生成器对象都符合上面提到的 iterable 和 iterator 两个规矩。换句话说,生成器对象既是一个可迭代对象,又是一个迭代器,而它作为可迭代对象的时候,返回的迭代器就是它自己。

然而生成器对象并不是 new 出来的,而是通过 generator function(生成器函数)生成的;生成器函数得自己写,又不能 new Generator(),那么这个生成器对象从哪里来呢?当然是生成器函数生成的,而且这会用到新语法,以及新的关键字 yield。

generator function(生成器函数)和 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];    
      }
  };

最简单的实现

上面说了一通,用了 N 种方法,无非是讲解 ES6 的新特性而已。要为 jQuery 实现 for...of 遍历,最简单的方法其实是拿来主义:

jQuery.fn[Symbol.iterator] = [][Symbol.iterator];

最后的提醒

jQuery 对象是一个伪数组,它的每一个元素都是一个 DOM(或原对象)而不是被封装的 jQuery 对象,所以,用 for..of 遍历的时候,和用 jQuery.fn.each() 遍历一样,如果想继续在每个元素上使用 jQuery 的特性,就要记得用 jQuery() 包装。

// for...offor (var dom in $("span")) {   
  var $span = $(dom);}// jQuey.fn.each$("span").each(function() {    var $span = $(this);});

后记

其实,jQuery 压根没有必要为 for...of 实现 iterator。虽然用 for...in 遍历 jQuery 对象也是一件很悲催的事情(不信你试试),但有 Array.from(),还有 jQuery.fn.toArray() 啊。

但话说回来,jQuery 的遍历,多数情况下还是应该用 jQuery.fn.each()。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,667评论 5 472
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,361评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,700评论 0 333
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,027评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,988评论 5 361
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,230评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,705评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,366评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,496评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,405评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,453评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,126评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,725评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,803评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,015评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,514评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,111评论 2 341

推荐阅读更多精彩内容

  • 在此处先列下本篇文章的主要内容 简介 next方法的参数 for...of循环 Generator.prototy...
    醉生夢死阅读 1,436评论 3 8
  • 你不知道JS:异步 第四章:生成器(Generators) 在第二章,我们明确了采用回调表示异步流的两个关键缺点:...
    purple_force阅读 944评论 0 2
  • 特别说明,为便于查阅,文章转自https://github.com/getify/You-Dont-Know-JS...
    杀破狼real阅读 437评论 0 0
  • 简介 基本概念 Generator函数是ES6提供的一种异步编程解决方案,语法行为与传统函数完全不同。Genera...
    oWSQo阅读 509评论 0 2
  • 无痕 明明懒 四季更迭 人生过场 草木有了心事就容易枯黄 大雁有了牵挂才南来北往 冷血无情 冷月无声 大地苍桑又昼...
    知了___阅读 213评论 5 5