jQuery 对象和 DOM 对象

  • DOM 对象:通过原生 javascript(如getElementsByTagNamegetElementId)获取的 html 节点。
var dom = document.getElementById("app"); // 获取DOM对象
var html = dom.innerHTML; // 获取DOM元素内的HTML代码
  • jQuery 对象:被 jQuery 包装过的 DOM 对象。
var jq = $("#app"); // 获取jQuery对象
var html = jq.html(); // 获取jQuery对象内的HTML代码

不能交换使用 jQuery 对象、DOM 对象上的属性,例如上面代码中的innerHTMLhtml()

jQuery 对象->DOM 对象

jQuery 提供了 2 种转换 DOM 对象的方法:

(1)jQuery 对象是一个类似于数组的对象,因此可以通过数组运算符[]获取指定索引的 DOM 对象。

var jq = $(".test"); // jQuery对象
var dom = jq[0]; // DOM对象

(2)通过 jQuery 本身提供的get(index)方法来获取指定 index 的对象。

var jq = $(".test"); // jQuery对象
var dom = jq.get(0); // DOM对象

DOM 对象->jQuery 对象

将 DOM 对象通过$()函数包装起来,就可以获得 jQuery 对象。

var dom = document.getElementById("test");
var jq = $(dom);

可以考虑使用****作为前缀为 jQuery 对象进行命名,例如上面代码中的变量`jq`可以命名为`jq`。

jQuery 选择器性能

提升选择器性能的有效途径是为选择器指定上下文,并以上下文为基础使用first()last()find()filter()hasClass()等 jQuery 筛选 API。

下面对 jQuery 选择器的性能由高向低进行排序:

1、ID 选择器

底层通过调用document.getElementById()实现。

$("#id");

2、标签选择器

底层通过调用document.getElementsByTagName()实现。

$("input");

3、类选择器

底层通过调用document.getElementsByClassName()实现。

$(".class");

4、属性及其它选择器

底层通过对 HTML 字符串进行正则表达式匹配来实现。

$("[contenteditable]");

jQuery 底层有使用原生document.querySelectorAll(),可以有效提升 IE8 及以上浏览器当中选择器的性能。

jQuery 作者已经将选择器引擎独立为Sizzleopen in new window库,而 Sizzle 会按照从右到左的顺序来解析选择器字符串,从而提高查询效率,缩小查找范围和遍历次数。

缓存常用的 jQuery 选择器对象

将需要常用的 jQuery 选择器对象赋值给一个局部或全局变量,是有效提升 jQuery 运行性能的良好开端。

var $jq = $("#app");
$jq.find("div.tree").append("string");
$jq.find("[contenteditable].test").html();
$jq.find(".demo.test").html();

减少循环时的 DOM 操作

forwhile$.each()等循环语句中,尽量减少 DOM 操作的次数,最好先将模板在循环中组装完成之后再一次性插入 DOM。

var $app = $("#app");
var template = "";
for (var i = 0; i <= 100; i++) {
  template += "<p>This is a paragraph!<p>";
}
$app.find("div").html(template);

使用原生方式处理 jQuery 数组

jQuery 选择器的结果是一个数组类型的对象,建议使用forwhile等原生语法对其进行处理,而非 jQuery 封装过的$.each()

<body>
  <div id="app">
    <div class="demo">1</div>
    <div class="demo">2</div>
    <div class="demo">3</div>
    <div class="demo">4</div>
    <div class="demo">5</div>
  </div>
</body>
var $demo = $(".demo");
for (var i = 0; i < $demo.length; i++) {
  // 通过数组索引提取后,jQuery对象转换成DOM对象,所以这里使用了DOM对象的innerHTML属性
  console.info($demo[i].innerHTML);
}

可以通过关键字length检查数组长度,从而判断 jQuery 对象是否存在。

var $demo = $(".demo");
if ($demo.length !== 0) {
  // do something
}

尽可能使用事件委托

jQuery3.x 版本继续简化了事件委托函数,仅剩下on()off()one()trigger()triggerHandler()五个事件处理函数。

<div id="app">
  <div id="parent">
    <p>parent</p>
    <div id="current">
      <p>current</p>
      <div id="child">child</div>
    </div>
  </div>
</div>
/* on可以用于处理冒泡事件,但是无法捕获事件 */
$("#current").on("click", function () {
  console.info("current is clicked!");
});

$("#child").trigger("click"); // on可以处理向上冒泡的click事件

$("#parent").trigger("click"); // on无法捕获父级元素的click事件

单页面场景下绑定的on事件,必须在路由切换时通过off解除事件绑定,否则会造成大量无用的事件句柄堆积在内存。

通过 extend()封装可复用代码

  • jQuery.extend():拓展**全局对象**,例如下面例子中的`.test()$.demo()`;
  • jQuery.fn.extend():拓展jQuery 对象数组,例如下面例子中的$("div").test()$("div").demo();
(function ($) {
  /* 标准写法 */
  $.extend({
    test: function () {
      alert("$.extend");
    },
  });
  /* 简便写法 */
  $.demo = function () {
    // 返回全局对象$,便于链式调用
    return this;
  };
  /* 标准写法 */
  $.fn.extend({
    test: function () {
      alert("$.fn.extend");
    },
  });
  /* 简便写法 */
  $.fn.demo = function () {
    // 返回jQuery对象数组,便于链式调用
    return this;
  };
})(jQuery);

两种方式的最大区别在于自定义方法所属的宿主对象不同。

使用 HTML5 的 data 属性绑定数据

通过 HTML5 提供的 data 属性可以更加方便的完成数据绑定,特别是在不借助handlebarlodash.template()等模板引擎的时候。

<div
  id="app"
  data-number="13.2"
  data-string="uinika"
  data-boolean="true"
  data-null="null"
  data-undefine="undefine"
  data-object='{"name": "hank"}'
  data-array='["demo1", "demo2"]'
></div>
$("#app").data("number");
$("#app").data("string");
$("#app").data("boolean");
$("#app").data("null");
$("#app").data("undefine");
$("#app").data("object").name;
$("#app").data("array")[1];

尽量使用原生 JavaScript

在不影响浏览器兼容性的情况下,尽量使用 JavaScript 原生 API。

var $demo = $(".demo"); // 缓存选择器

$demo.is(":checked"); // 通过jQuery封装过的is()方法
$demo[0].checked; // 通过DOM原生的checked属性

$demo.css({ color: "red" }); // 通过jQuery封装过的css()方法
$demo[0].style.color = "red"; // 通过DOM原生的style.color属性

$("<p></p>"); // 通过jQuery新建<p>标签
$(document.createElement("p")); // 通过DOM原生的createElement方法

$(document).ready()

该函数内的代码会在 DOM 加载完毕后,内容(如图片)加载完成前执行;生产环境中,尽可能在每个 js 文件下使用该函数。

$(document).ready(function () {
  // 标准写法
});

$(function () {
  // 简化写法
});

JavaScript 原生的window.onload()只会在 DOM 和图片等资源全部加载完成之后才执行。

延迟对象$.Deferred()

Deferred()是一个工厂函数,用来建立新的 deferred 对象(deferred [dɪ’fɜ:d] adj.延缓的),该对象上可以注册多个回调函数队列,这些函数的执行依赖于任意同步或异步函数的执行结果(sucessfailure)。该对象可以视为 jQuery 版本的Promise实现,可以更加优雅的解决 JavaScript 回调嵌套的问题。

jQuery 的 Deferred 对象是基于CommonJS Promises/Aopen in new window规范设计的。

  • deferred.notify() 触发 Deferred 上progress相关的回调函数。
  • deferred.resolve() Resolve 一个 Deferred 对象,并触发resolve状态相关的回调函数。
  • deferred.reject() Reject 一个 Deferred 对象,并触发reject状态相关的回调函数。
  • deferred.progress() 该函数在 Deferred 对象生成progress通知时被调用。
  • deferred.done() 该函数在 Deferred 对象被resolve时调用。
  • deferred.fail() 该函数在 Deferred 对象被rejecte时调用。
  • deferred.catch() 该函数在 Deferred 对象被rejecte时调用。
  • deferred.then() Deferred 对象resolvedrejectedprogress时,都会被触发的回调函数。
  • deferred.promise() 返回一个延迟的 Promise 对象。
  • &.when() 提供一种基于零个或多个 Thenable 对象执行回调函数的方式,其参数是一个代表异步事件的 Deferred 对象。
  • $("selector").promise() 返回一个 Promise 对象去观察所有绑定到集合、队列的确定类型行为是否已经完成。
var deferred = $.Deferred();

var demo = function (deferred) {
  var task = function () {
    deferred.resolve();
    // deferred.notify();
    // deferred.reject();
  };
  setTimeout(task, 2000);
  return deferred;
};

$.when(demo(deferred))
  .progress(function () {
    console.info("progress");
  })
  .then(function () {
    console.info("then");
  })
  .done(function () {
    console.info("done");
  })
  .fail(function () {
    console.info("fail");
  })
  .catch(function () {
    console.info("catch");
  });

/*
输出结果:
then
done
*/

$.ajax()返回的就是一个 deferred 对象。