程序员总结分析:为什么要用原生JavaScript代替jQuery?

随着JavaScript本身的完善,越来越多的人开始喜欢使用原生JavaScript 开发代替各种库,其中不少人发出了用原生JavaScript代替jQuery的声音。这并不是什么坏事,但也不见得就是好事。如果你真的想把jQuery从前端依赖库中移除掉,我建议你慎重考虑。

首先jQuery是一个第三方库。库存在的价值之一在于它能极大地简化开发。一般情况下,第三方库都是由原生语言特性和基础API库实现的。因此,理论上来说,任何库第三方库都是可以用原生语言特性代替的,问题在于是否值得?

jQuery的作用

引用一段jQuery官网的话:

jQuery is a fast, small, and feature-rich JavaScript library. It makes things like HTML document traversal and manipulation, event handling, animation, and Ajax much simpler with an easy-to-use API that works across a multitude of browsers.

这一段话很谦虚的介绍了jQuery在处理DOM和跨浏览器方面做出的贡献。而事实上,这也正是我们选用jQuery的主要原因,并顺带使用了它带来的一些工具,比如数组工具,Deferred等。

对于我来说,最常用的功能包括

在DOM树中进行查询

修改DOM树及DOM相关操作

事件处理

Ajax

Deferred和Promise

对象和数组处理

还有一个一直在用却很难在列清单时想到的——跨浏览器

到底是谁在替代谁?

上面提到的所有功能都能用原生代码来实现。从本质上来说,jQuery就是用来代替原生实现,以达到减少代码,增强可读性的目的的——所以,到底是用jQuery代替原生代码,还是用原生代码代替jQuery?这个先后因果关系可否搞明白?

我看到说用querySelectorAll()代替$()的时候,不禁在想,用jQuery一个字符就能解决的,为什么要写十六个字符?大部分浏览器是有实现$(),但是写原生代码的时候你会考虑$()的浏览器兼容性吗?jQuery已经考虑了!

我看到一大堆创建DOM结构的原生JavaScript代码的时候,不禁在想,用jQuery只需要一个方法链就解决了,我甚至可以用和HTML结构类似的代码(包含缩进),比如


这段代码用document.createElement()来实现完全没有问题,只不过代码量要大得多,而且会出现大量重复(或类似)的代码。当然是可以把这些重复代码提取出来写成函数的……不过jQuery已经做了。

注,拼HTML的方法实在弱爆了,既容易出错,又不易阅读。如果有ES6的字符串模板之后,用它来写HTML也是个不错的主意。

就DOM操作这一部分来说,jQuery仍然是一个非常好用的工具。这是jQuery替代了原生JavaScript,以前如此,现在仍然如此。

没落的jQuery工具函数

jQuery 2006年被发明出来的时候,还没有ES5(2011年6月发布)。即使在ES5发布之后很长一段时间里,也不是所有浏览器都支持。因此在这一时期,除DOM操作外,jQuery的巨大贡献在于解决跨浏览器的问题,以及提供了方便的对象和数组操作工具,比如each()、index()和filter等。

如今ECMAScript刚刚发布了2017的标准,浏览器标准混乱的问题也已经得到了很好的解决,前端界还出现了Babel这样的转译工具和TypeScript之类的新语言。所以现在大家都尽可放心的使用各种新的语言特性,哪怕ECMAScript的相关标准还在制定中。在这一时期,jQuery提供的大量工具方法都已经有了原生替代品——在使用上差别不大的情况下,确实宁愿用原生实现。

事实上,jQuery也在极尽可能地采用原生实现,以提高执行效率。jQuery没有放弃这些已有原生实现的工具函数/方法,主要还是因为向下兼容,以及一如既往的提供浏览器兼容性——毕竟不是每一个使用jQuery的开发者都会使用转译工具。

那么,对于JavaScript开发者而言,jQuery确实有很多工具方法可以被原生JavaScript函数/方法替代。比如

$.parseJSON()可以用JSON.parse()替代,而且JSON.stringify()还弥补了jQuery没有$.toJSON()的不足;

$.extend()的部分功能可以由Object.assign()替代`

$.fn的一些数据处理工具方法,比如each()、index()等都可以用Array.prototype中相应的工具方法替代,比如forEach()、indexOf()等。

$.Deferred()和jQuery Promise在某些情况下可以用原生Promise替代。它们在没有ES6之前也算是个不错的Promise实现。

......

$.fn就是jQuery.prototype,也就是jQuery对象的原型。所以在其上定义的方法就是jQuery对象的方法。

这些工具方法在原生JavaScript中已经逐渐补充完善,但它们仍然只是在某些情况下可以被替代……因为jQuery对象是一个特有的数据结构,针对jQuery自身创建的工具方法在作用于jQuery对象的时候会有一些针对性的实现——既然DOM操作仍然不能把jQuery抛开,那这些方法也就不可能被完全替换掉。

jQuery与原生JavaScript的结合

有时候需要用jQuery,有时候不需要用,该如何分辨?

jQuery的优势在于它的DOM处理、Ajax,以及跨浏览器。如果在项目中引入jQuery,多半是因为对这些功能的需求。而对于不操作DOM,也不需要考虑跨浏览器(比如用于转译工具)的部分,则考虑尽可能的用原生JavaScript实现。

如此以来,一定会存在jQuery和原生JavaScript的交集,那么,就不得不说说需要注意的地方。

jQuery对象实现了部分数组功能的伪数组

首先要注意的一点,就是jQuery对象是一个伪数组,它是对原生数组或伪数组(比如DOM节点列表)的封装。

如果要获得某个元素,可以用[]运算符或get(index)方法;如果要获得包含所有元素的数组,可以使用toArray()方法,或者通过ES6中引入的Array.from()来转换。


注意each/map和forEach/map回调函数的参数顺序

jQuery定义在$.fn上的each()和map()方法与定义在Array.prototype上的原生方法forEach()和map()对应,它们的参数都是回调函数,但它们的回调函数定义有一些细节上的差别。

$.fn.each()的回调定义如下:

Function(Integerindex,Elementelement)

回调的第一个参数是数组元素所在的位置(序号,从0开始),第二个参数是元素本身。

而Array.prototype.forEach()的回调定义是

Function(currentValue,index,array)

回调的第一个参数是数组元素本身,第二个参数才是元素所有的位置(序号)。而且这个回调有第三个参数,即整个数组的引用。

请特别注意这两个回调定义的第一个参数和第二个参数,所表示的意义正好交换,这在混用jQuery和原生代码的时候很容易发生失误。

对于$.fn.map()和Array.prototype.map()的回调也是如此,而且由于这两个方法同名,发生失误的概率会更大。

注意each()/map()中的this

$.fn.each()和$.fn.map()回调中经常会使用this,这个this指向的就是当前数组元素。正是因为有这个便利,所以jQuery在定义回请贩时候没有把元素本身作为第一个参数,而是把序号作为第一个参数。

不过ES6带来了箭头函数。箭头函数最常见的作用就是用于回调。箭头函数中的this与箭头函数定义的上下文相关,而不像普通函数中的this是与调用者相关。

现在问题来了,如果把箭头函数作为$.fn.each()或$.fn.map()的回调,需要特别注意this的使用——箭头函数中的this不再是元素本身。鉴于这个问题,建议若非必要,仍然使用函数表达式作为$.fn.each()和$.fn.map()的回调,以保持原有的jQuery编程习惯。实在需要使用箭头函数来引用上下文this的情况下,千万记得用其回调定义的第二个参数作为元素引用,而不是this。


$.fn.map()返回的并不是数组

与Array.prototype.map()不同,$.fn.map()返回的不是数组,而是jQuery对象,是伪数组。如果需要得到原生数组,可以采用toArray()或Array.from()输出。


jQuery Promise

jQuery是通过$.Deferred()来实现的Promise功能。在ES6以前,如果引用了jQuery,基本上不需要再专门引用一个Promise库,jQuery已经实现了Promise的基本功能。

不过jQuery Promise虽然实现了then(),却没有实现catch(),所以它不能兼容原生的Promise,不过用于co或者ES2017的async/await毫无压力。

虽然jQuery的Promise没有catch(),但是提供了fail事件处理,这个事件在Deferred reject()的时候触发。相应的还有done事件,在Deferred resovle()的时候触发,以及always事件,不论什么情况都会触发。

与一次性的then()不同,事件可以注册多个处理函数,在事件触发的时候,相应的处理函数会依次执行。另外,事件不具备传递性,所以fail()不能在写在then()链的最后。

结语

总的来说,在大量操作DOM的前端代码中使用jQuery可以带来极大的便利,也使DOM操作的相关代码更易读。另一方面,原生JavaScript带来的新特性确实可以替代jQuery的部分工具函数/方法,以降低项目对jQuery的依赖程序。

jQuery和原生JavaScript应该是共生关系,而不是互斥关系。应该在合适的时候选用合适的方法,而不是那么绝对的非要用谁代替谁。

�S����

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

推荐阅读更多精彩内容