>웹 프론트엔드 >JS 튜토리얼 >jQuery 대신 네이티브 JavaScript?

jQuery 대신 네이티브 JavaScript?

伊谢尔伦
伊谢尔伦원래의
2017-02-08 13:26:461205검색

JavaScript 자체의 발전으로 점점 더 많은 사람들이 다양한 라이브러리 대신 Native JavaScript 개발을 선호하기 시작했고, 그 중 많은 사람들이 jQuery 대신 Native JavaScript를 사용하자는 목소리를 냈습니다. 그것이 나쁜 것은 아니지만 반드시 좋은 것도 아닙니다. 프런트엔드 종속성 라이브러리에서 jQuery를 정말로 제거하고 싶다면 신중하게 생각하는 것이 좋습니다.

우선 jQuery는 타사 라이브러리입니다. 라이브러리의 가치 중 하나는 개발을 크게 단순화한다는 것입니다. 일반적으로 타사 라이브러리는 기본 언어 기능과 기본 API 라이브러리로 구현됩니다. 따라서 이론적으로 모든 타사 라이브러리를 모국어 기능으로 대체할 수 있습니다. 그만한 가치가 있습니까?

jQuery의 역할

jQuery 공식 웹사이트의 인용문:

jQuery는 빠르고 작으며 기능이 풍부한 JavaScript 라이브러리입니다. . HTML 문서 순회조작, 이벤트 처리와 같은 작업을 수행합니다. 애니메이션Ajax 다양한 에서 작동하는 사용하기 쉬운 API를 통해 훨씬 더 간단해졌습니다. 브라우저.

이 단락에서는 DOM 처리 및 크로스 브라우저 처리에 대한 jQuery의 기여를 간략하게 소개합니다. 실제로 이것이 우리가 jQuery를 선택하고 배열 도구, Deferred 등과 같은 일부 도구를 사용하는 주된 이유입니다.

저에게 가장 일반적으로 사용되는 기능은 다음과 같습니다.

  • DOM 트리 쿼리

  • DOM 트리 및 DOM 수정 관련 작업

  • 이벤트 처리

  • Ajax

  • 지연 및 약속

  • 객체 및 배열 처리

  • 제가 사용해오던 기능이 하나 더 있는데 목록을 만들 때 생각나기 힘든 것 - 크로스 브라우저

누가 누구를 대신하나요?

위에서 언급한 모든 기능은 네이티브 코드를 사용하여 구현할 수 있습니다. 본질적으로 jQuery는 코드를 줄이고 가독성을 높이기 위해 네이티브 구현을 대체하는 데 사용됩니다. 그렇다면 네이티브 코드 대신 jQuery를 사용해야 할까요, 아니면 jQuery 대신 네이티브 코드를 사용해야 할까요? 이 인과관계를 이해할 수 있는가?

querySelectorAll() 대신 $()을 써야 한다는 걸 봤을 때, jQuery가 한 글자로 풀 수 있는데 왜 16자를 써야 하지 않을까 하는 생각이 들었습니다. 대부분의 브라우저는 $()을 구현하는데, 네이티브 코드를 작성할 때 $()의 브라우저 호환성을 고려하시나요? jQuery가 고려되었습니다!

// 创建一个 ul 列表并加在 #container 中
$("ff6d136ddc5fdfeffaf53ff6ee95f185").append(
    $("25edfb22a4f469ecb59f1190150159c6").append(
        $("3499910bf9dac5ae3c52d5ede7383485").attr("href", "#").text("first")),
    $("25edfb22a4f469ecb59f1190150159c6").append(
        $("3499910bf9dac5ae3c52d5ede7383485").attr("href", "#").text("second")),
    $("25edfb22a4f469ecb59f1190150159c6").append(
        $("3499910bf9dac5ae3c52d5ede7383485").attr("href", "#").text("third"))
).appendTo($("#container"));

이 코드를 구현하기 위해

를 사용하는 데에는 문제가 없지만 코드의 양이 훨씬 많아지고 반복되는 부분이 많아집니다( 또는 이와 유사한) 코드. 물론 이러한 반복되는 코드를 추출하여 함수에 쓰는 것도 가능하지만... jQuery는 이미 이를 수행했습니다.

document.createElement()

참고로 HTML 철자법은 매우 약하고 오류가 발생하기 쉽고 읽기가 어렵습니다. ES6 문자열 템플릿이 있다면 이를 사용하여 HTML을 작성하는 것도 좋은 생각입니다.

DOM 조작에 관한 한 jQuery는 여전히 매우 유용한 도구입니다. 이는 예전에도 그랬고 지금도 그렇듯이 네이티브 JavaScript를 jQuery로 대체한 것입니다.

쇠퇴하는 jQuery 도구 기능

2006년 jQuery가 발명될 당시에는 ES5(2011년 6월 출시)가 없었습니다. ES5가 출시된 지 오랜 후에도 모든 브라우저가 ES5를 지원하지는 않았습니다. 따라서 이 기간 동안 jQuery의 큰 공헌은 DOM 작업 외에도 크로스 브라우저 문제를 해결하고

,

, each() 등과 같은 편리한 객체 및 배열 조작 도구를 제공한 것이었습니다. index()filter이제 ECMAScript가 2017 표준을 출시하면서 혼란스러운 브라우저 표준 문제가 잘 해결되었습니다. Babel과 같은 번역 도구와 TypeScript와 같은 새로운 언어도 프런트엔드 세계에 등장했습니다. 따라서 이제 ECMAScript에 대한 관련 표준이 아직 공식화되는 중이더라도 누구나 자유롭게 다양한 새로운 언어 기능을 사용할 수 있습니다. 이 기간 동안 jQuery에서 제공하는 많은 도구 메서드에는 이미 기본 대안이 있었습니다. 사용법에 큰 차이가 없으면 기본 구현을 사용하는 것이 실제로 더 좋습니다.

실제로 jQuery도 실행 효율성을 높이기 위해 네이티브 구현을 최대한 활용하고 있습니다. jQuery는 이전 버전과의 호환성과 지속적인 브라우저 호환성 때문에 기본적으로 구현된 도구 기능/방법을 포기하지 않았습니다. 결국 jQuery를 사용하는 모든 개발자가 번역 도구를 사용하는 것은 아닙니다.

자바스크립트 개발자를 위해 jQuery에는 기본 JavaScript 함수/메서드로 대체할 수 있는 많은 도구 메서드가 있습니다. 예를 들어

  • 으로 대체할 수 있으며, $.parseJSON()JSON.parse() JSON.stringify() 없이 jQuery 의 단점을 보완합니다. 🎜>

  • $.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() 来转换。

// 将普通数组转换成 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/mapforEach/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 Promise

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 = $(&#39;<div/>&#39;);
// Native
var newEl = document.createElement(&#39;div&#39;);

3.添加事件

// jQuery
$(&#39;.el&#39;).on(&#39;event&#39;, function() {
});
// Native
[].forEach.call(document.querySelectorAll(&#39;.el&#39;), function (el) {
  el.addEventListener(&#39;event&#39;, function() {
  }, false);
});

4.get/set属性

// jQuery
$(&#39;.el&#39;).filter(&#39;:first&#39;).attr(&#39;key&#39;, &#39;value&#39;);
$(&#39;.el&#39;).filter(&#39;:first&#39;).attr(&#39;key&#39;);
// Native
document.querySelector(&#39;.el&#39;).setAttribute(&#39;key&#39;, &#39;value&#39;);
document.querySelector(&#39;.el&#39;).getAttribute(&#39;key&#39;);

5.添加和移除样式Class

DOM元素本身有一个可读写的className属性,可以用来操作class。

HTML 5还提供一个classList对象,功能更强大(IE 9不支持)。

// jQuery
$(&#39;.el&#39;).addClass(&#39;class&#39;);
$(&#39;.el&#39;).removeClass(&#39;class&#39;);
$(&#39;.el&#39;).toggleClass(&#39;class&#39;);
// Native
document.querySelector(&#39;.el&#39;).classList.add(&#39;class&#39;);
document.querySelector(&#39;.el&#39;).classList.remove(&#39;class&#39;);
document.querySelector(&#39;.el&#39;).classList.toggle(&#39;class&#39;);

6.追加元素

尾部追加元素:

// jQuery
$(&#39;.el&#39;).append($(&#39;<div/>&#39;));
// Native
document.querySelector(&#39;.el&#39;).appendChild(document.createElement(&#39;div&#39;));

头部追加元素:

//jQuery
$(‘.el’).prepend(&#39;<div></div>&#39;)
//Native
var parent = document.querySelector(&#39;.el&#39;);
parent.insertBefore("<div></div>",parent.childNodes[0])

7.克隆元素

// jQuery
var clonedEl = $(&#39;.el&#39;).clone();
// Native
var clonedEl = document.querySelector(&#39;.el&#39;).cloneNode(true);

8.移除元素

Remove
// jQuery
$(&#39;.el&#39;).remove();
// Native
remove(&#39;.el&#39;);
function remove(el) {
  var toRemove = document.querySelector(el);
  toRemove.parentNode.removeChild(toRemove);
}

9.获取父级元素

// jQuery
$(&#39;.el&#39;).parent();
// Native
document.querySelector(&#39;.el&#39;).parentNode;

10.获取上一个/下一个元素(Prev/next element)

// jQuery
$(&#39;.el&#39;).prev();
$(&#39;.el&#39;).next();
// Native
document.querySelector(&#39;.el&#39;).previousElementSibling;
document.querySelector(&#39;.el&#39;).nextElementSibling;

11.XHR and AJAX

// jQuery
$.get(&#39;url&#39;, function (data) {
});
$.post(&#39;url&#39;, {data: data}, function (data) {
});
// Native
// get
var xhr = new XMLHttpRequest();
xhr.open(&#39;GET&#39;, url);
xhr.onreadystatechange = function (data) {
}
xhr.send();
// post
var xhr = new XMLHttpRequest()
xhr.open(&#39;POST&#39;, 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(&#39;slow&#39;, { x: &#39;+=10px&#39; }

jQuery的动画效果,很大部分基于DOM。但是目前,CSS 3的动画远比DOM强大,所以可以把动画效果写进CSS,然后通过操作DOM元素的class,来展示动画。

foo.classList.add(&#39;animate&#39;)

如果需要对动画使用回调函数,CSS 3也定义了相应的事件。

el.addEventListener("webkitTransitionEnd", transitionEnded);
el.addEventListener("transitionend", transitionEnded);


성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.