JavaScript 自体の改善に伴い、さまざまなライブラリの代わりにネイティブ JavaScript 開発を使用することを好む人が増え始めており、その多くが jQuery の代わりにネイティブ JavaScript を使用するという声を上げています。それは悪いことではありませんが、必ずしも良いことでもありません。フロントエンドの依存関係ライブラリから jQuery を本当に削除したい場合は、慎重に検討することをお勧めします。
まず第一に、jQuery はサードパーティのライブラリです。ライブラリの価値の 1 つは、開発が大幅に簡素化されることです。一般に、サードパーティのライブラリは、ネイティブ言語機能と基本的な API ライブラリによって実装されます。したがって、理論的には、どのサードパーティ ライブラリもネイティブ言語機能に置き換えることができます。それだけの価値があるかということです。
jQueryの役割 jQuery公式Webサイトからの引用:jQueryは、HTMLドキュメントトラバーサルや操作などを実現する、高速で小さく、機能が豊富なJavaScriptライブラリです。 、 イベント処理、アニメーション、およびAjaxは、多数のブラウザ間で動作する使いやすいAPIにより非常に簡単になります。 これは非常に簡単な紹介 DOM 処理とクロスブラウザー処理に対する jQuery の貢献について学びます。実際、これが私たちが jQuery を選択し、配列ツールや Deferred などの jQuery が提供するいくつかのツールを使用する主な理由です。
私にとって、最も一般的に使用される関数は次のとおりです
DOM ツリーでのクエリ
DOM ツリーと DOM 関連の操作の変更
イベント処理
Ajax
デフェルドとPromise
オブジェクトと配列の処理
私が使っているもう1つのものがありますが、リストを作成するときに考えるのは難しいです-クロスブラウザ
誰が誰を置き換えていますか?
$()
の代わりに querySelectorAll()
を使用する必要があるのを見たとき、jQuery でできるのに、なぜ 16 個も記述する必要があるのかと思わずにはいられませんでした。 1文字で解決?ほとんどのブラウザは $()
を実装していますが、ネイティブ コードを作成するとき、$()
のブラウザ互換性を考慮しますか? jQueryを検討しました! DOM 構造を作成する多くのネイティブ JavaScript コードを見たとき、jQuery を使用すると、問題を解決するのに 1 つのメソッド チェーンだけが必要で、HTML 構造と同様のコードを使用することもできると思わずにはいられませんでした。インデントを含む)、
// 创建一个 ul 列表并加在 #container 中 $("<ul>").append( $("<li>").append( $("<a>").attr("href", "#").text("first")), $("<li>").append( $("<a>").attr("href", "#").text("second")), $("<li>").append( $("<a>").attr("href", "#").text("third")) ).appendTo($("#container"));
など、document.createElement()
を使用してこのコードを実装することに問題はありませんが、コードの量がはるかに多くなり、繰り返し (または同様の)コード。もちろん、これらの繰り返しコードを抽出して関数に記述することも可能ですが、jQuery はすでにそれを行っています。 querySelectorAll()
代替 $()
的时候,不禁在想,用 jQuery 一个字符就能解决的,为什么要写十六个字符?大部分浏览器是有实现 $()
,但是写原生代码的时候你会考虑 $()
的浏览器兼容性吗?jQuery 已经考虑了!
我看到一大堆创建 DOM 结构的原生 JavaScript 代码的时候,不禁在想,用 jQuery 只需要一个方法链就解决了,我甚至可以用和 HTML 结构类似的代码(包含缩进),比如
// 将普通数组转换成 jQuery 对象 const jo = $([1, 2, 3]); jo instanceof jQuery; // true Array.isArray(jo); // false // 从 jQuery 对象获取元素值 const a1 = jo[0]; // 1 const a2 = jo.get(1); // 2 // 将 jQuery 对象转换成普通数组 const arr1 = jo.toArray(); // [1, 2, 3] Array.isArray(arr1); // true const arr2 = Array.from(jo); // [1, 2, 3] Array.isArray(arr2); // true
这段代码用 document.createElement()
来实现完全没有问题,只不过代码量要大得多,而且会出现大量重复(或类似)的代码。当然是可以把这些重复代码提取出来写成函数的……不过 jQuery 已经做了。
注,拼 HTML 的方法实在弱爆了,既容易出错,又不易阅读。如果有 ES6 的字符串模板之后,用它来写 HTML 也是个不错的主意。
就 DOM 操作这一部分来说,jQuery 仍然是一个非常好用的工具。这是 jQuery 替代了原生 JavaScript,以前如此,现在仍然如此。
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()
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,有时候不需要用,该如何分辨?
jQuery 的优势在于它的 DOM 处理、Ajax,以及跨浏览器。如果在项目中引入 jQuery,多半是因为对这些功能的需求。而对于不操作 DOM,也不需要考虑跨浏览器(比如用于转译工具)的部分,则考虑尽可能的用原生 JavaScript 实现。
如此以来,一定会存在 jQuery 和原生 JavaScript 的交集,那么,就不得不说说需要注意的地方。
首先要注意的一点,就是 jQuery 对象是一个伪数组,它是对原生数组或伪数组(比如 DOM 节点列表)的封装。
如果要获得某个元素,可以用 []
运算符或 get(index)
方法;如果要获得包含所有元素的数组,可以使用 toArray()
方法,或者通过 ES6 中引入的 Array.from()
来转换。
// 将普通数组转换成 jQuery 对象 const jo = $([1, 2, 3]); jo instanceof jQuery; // true Array.isArray(jo); // false // 从 jQuery 对象获取元素值 const a1 = jo[0]; // 1 const a2 = jo.get(1); // 2 // 将 jQuery 对象转换成普通数组 const arr1 = jo.toArray(); // [1, 2, 3] Array.isArray(arr1); // true const arr2 = Array.from(jo); // [1, 2, 3] Array.isArray(arr2); // true
each/map
和 forEach/map
回调函数的参数顺序jQuery 定义在 $.fn
上的 each()
和 map()
方法与定义在 Array.prototype
上的原生方法 forEach()
和 map()
对应,它们的参数都是回调函数,但它们的回调函数定义有一些细节上的差别。
$.fn.each()
的回调定义如下:
Function(Integer index, Element element )
回调的第一个参数是数组元素所在的位置(序号,从 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
。
// 将所有输入控制的 name 设置为其 id $(":input").each((index, input) => { // const $input = $(this) 这是错误的!!! const $input = $(input); $input.prop("name", $input.prop("id")); });
$.fn.map()
返回的并不是数组与 Array.prototype.map()
不同,$.fn.map()
返回的不是数组,而是 jQuery 对象,是伪数组。如果需要得到原生数组,可以采用 toArray()
或 Array.from()
输出。
const codes = $([97, 98, 99]); const chars = codes.map(function() { return String.fromCharCode(this); }); // ["a", "b", "c"] chars instanceof jQuery; // true Array.isArray(chars); // false const chars2 = chars.toArray(); Array.isArray(chars2); // true
jQuery 是通过 $.Deferred()
来实现的 Promise 功能。在 ES6 以前,如果引用了 jQuery,基本上不需要再专门引用一个 Promise 库,jQuery 已经实现了 Promise 的基本功能。
不过 jQuery Promise 虽然实现了 then()
,却没有实现 catch()
,所以它不能兼容原生的 Promise,不过用于 co 或者 ES2017 的 async/await
毫无压力。
// 模拟异步操作 function mock(value, ms = 200) { const d = $.Deferred(); setTimeout(() => { d.resolve(value); }, ms); return d.promise(); }
// co 实现 co(function* () { const r1 = yield mock(["first"]); const r2 = yield mock([...r1, "second"]); const r3 = yield mock([...r2, "third"]); console.log(r1, r2, r3); }); // ['first'] // ['first', 'second'] // ['first', 'second', 'third']
// async/await 实现,需要 Chrome 55 以上版本测试 (async () => { const r1 = await mock(["first"]); const r2 = await mock([...r1, "second"]); const r3 = await mock([...r2, "third"]); console.log(r1, r2, r3); })(); // ['first'] // ['first', 'second'] // ['first', 'second', 'third']
虽然 jQuery 的 Promise 没有 catch()
,但是提供了 fail
事件处理,这个事件在 Deferred reject()
的时候触发。相应的还有 done
事件,在 Deferred resovle()
的时候触发,以及 always
事件,不论什么情况都会触发。
与一次性的 then()
不同,事件可以注册多个处理函数,在事件触发的时候,相应的处理函数会依次执行。另外,事件不具备传递性,所以 fail()
不能在写在 then()
链的最后。
结尾
总的来说,在大量操作 DOM 的前端代码中使用 jQuery 可以带来极大的便利,也使 DOM 操作的相关代码更易读。另一方面,原生 JavaScript 带来的新特性确实可以替代 jQuery 的部分工具函数/方法,以降低项目对 jQuery 的依赖程序。
jQuery 和原生 JavaScript 应该是共生关系,而不是互斥关系。应该在合适的时候选用合适的方法,而不是那么绝对的非要用谁代替谁。
原生JS取代一些JQuery方法
1.选取元素
// jQuery var els = $('.el'); // Native var els = document.querySelectorAll('.el'); // Shorthand var $ = function (el) { return document.querySelectorAll(el); }
querySelectorAll方法返回的是NodeList对象,需要转换为数组。
myList = Array.prototype.slice.call(myNodeList)
2.创建元素
// jQuery var newEl = $('<div/>'); // Native var newEl = document.createElement('div');
3.添加事件
// jQuery $('.el').on('event', function() { }); // Native [].forEach.call(document.querySelectorAll('.el'), function (el) { el.addEventListener('event', function() { }, false); });
4.get/set属性
// jQuery $('.el').filter(':first').attr('key', 'value'); $('.el').filter(':first').attr('key'); // Native document.querySelector('.el').setAttribute('key', 'value'); document.querySelector('.el').getAttribute('key');
5.添加和移除样式Class
DOM元素本身有一个可读写的className属性,可以用来操作class。
HTML 5还提供一个classList对象,功能更强大(IE 9不支持)。
// jQuery $('.el').addClass('class'); $('.el').removeClass('class'); $('.el').toggleClass('class'); // Native document.querySelector('.el').classList.add('class'); document.querySelector('.el').classList.remove('class'); document.querySelector('.el').classList.toggle('class');
6.追加元素
尾部追加元素:
// jQuery $('.el').append($('<div/>')); // Native document.querySelector('.el').appendChild(document.createElement('div'));
头部追加元素:
//jQuery $(‘.el’).prepend('<div></div>') //Native var parent = document.querySelector('.el'); parent.insertBefore("<div></div>",parent.childNodes[0])
7.克隆元素
// jQuery var clonedEl = $('.el').clone(); // Native var clonedEl = document.querySelector('.el').cloneNode(true);
8.移除元素
Remove // jQuery $('.el').remove(); // Native remove('.el'); function remove(el) { var toRemove = document.querySelector(el); toRemove.parentNode.removeChild(toRemove); }
9.获取父级元素
// jQuery $('.el').parent(); // Native document.querySelector('.el').parentNode;
10.获取上一个/下一个元素(Prev/next element)
// jQuery $('.el').prev(); $('.el').next(); // Native document.querySelector('.el').previousElementSibling; document.querySelector('.el').nextElementSibling;
11.XHR and AJAX
// jQuery $.get('url', function (data) { }); $.post('url', {data: data}, function (data) { }); // Native // get var xhr = new XMLHttpRequest(); xhr.open('GET', url); xhr.onreadystatechange = function (data) { } xhr.send(); // post var xhr = new XMLHttpRequest() xhr.open('POST', url); xhr.onreadystatechange = function (data) { } xhr.send({data: data});
12.清空子元素
//jQuery $("#elementID").empty() //Native var element = document.getElementById("elementID") while(element.firstChild) element.removeChild(element.firstChild);
13.检查是否有子元素
//jQuery if (!$("#elementID").is(":empty")){} //Native if (document.getElementById("elementID").hasChildNodes()){}
14.$(document).ready
DOM加载完成,会触发DOMContentLoaded事件,等同于jQuery的$(document).ready方法。
document.addEventListener("DOMContentLoaded", function() { // ... });
15.数据储存
jQuery对象可以储存数据。
$("body").data("foo", 52);
HTML 5有一个dataset对象,也有类似的功能(IE 10不支持),不过只能保存字符串。
element.dataset.user = JSON.stringify(user); element.dataset.score = score;
16.动画
jQuery的animate方法,用于生成动画效果。
$foo.animate('slow', { x: '+=10px' }
jQuery的动画效果,很大部分基于DOM。但是目前,CSS 3的动画远比DOM强大,所以可以把动画效果写进CSS,然后通过操作DOM元素的class,来展示动画。
foo.classList.add('animate')
如果需要对动画使用回调函数,CSS 3也定义了相应的事件。
el.addEventListener("webkitTransitionEnd", transitionEnded); el.addEventListener("transitionend", transitionEnded);