对于jQuery性能的一些优化建议


Posted in Javascript onAugust 13, 2015

不要每次都在循环中访问数组的 length 属性,应在循环开始之前就将其缓存:

var myLength = myArray.length;

for (var i = 0; i < myLength; i++) {
  // do stuff
}

在循环外执行 append 操作

直接操作 DOM 是非常耗费性能的,尤其不要在循环中直接操作 DOM:

// 这样性能很差
$.each(myArray, function(i, item) {
  var newListItem = '<li>' + item + '</li>';
  $('#ballers').append(newListItem);
});
// 这样性能较好
var frag = document.createDocumentFragment();
$.each(myArray, function(i, item) {
    var newListItem = '<li>' + item + '</li>';
    frag.appendChild(newListItem);
});
$('#ballers')[0].appendChild(frag);
// 这样也很好
var myHtml = '';
$.each(myArray, function(i, item) {
    html += '<li>' + item + '</li>';
});
$('#ballers').html(myHtml);

代码要保持精炼

避免做重复的事情。如果你一直在做重复的事情,那么就可能出问题了:

// 丑
if ($eventfade.data('currently') != 'showing') {
  $eventfade.stop();
}

if ($eventhover.data('currently') != 'showing') {
  $eventhover.stop();
}

if ($spans.data('currently') != 'showing') {
  $spans.stop();
}

// 漂亮!!
var $elems = [$eventfade, $eventhover, $spans];
$.each($elems, function(i,elem) {
  if (elem.data('currently') != 'showing') {
    elem.stop();
  }
});

警惕匿名函数

匿名函数满天飞是很痛苦的事情,它们难以调试、维护、测试或者复用,应尽可能的对函数命名并将其封装在对象中,实施有效的管理:

// 不好
$(document).ready(function() {
  $('#magic').click(function(e) {
    $('#yayeffects').slideUp(function() {
      // ...
    });
  });

  $('#happiness').load(url + ' #unicorns', function() {
    // ...
  });
});

// 好
var PI = {
  onReady : function() {
    $('#magic').click(PI.candyMtn);
    $('#happiness').load(PI.url + ' #unicorns', PI.unicornCb);
  },

  candyMtn : function(e) {
    $('#yayeffects').slideUp(PI.slideCb);
  },

  slideCb : function() { ... },

  unicornCb : function() { ... }
};

$(document).ready(PI.onReady);

选择器的优化

随着越来越多的浏览器支持 document.querySelectorAll(),选择器的重担已经慢慢转移给浏览器了,但还是有一些技巧需要注意:

优先并尽可能地使用 ID 选择器:

// 快
$('#container div.robotarm');

// 相当快
$('#container').find('div.robotarm');

使用 $.fn.find 的方式更快,因为在 $.fn.find 之前的选择器并没有使用 jQuery 自带的 Sizzle 选择器引擎,而是使用了浏览器 document.getElementById() 方法,浏览器原生的方法自然更快。

使用组合选择器时,尽可能使右端更明确,而左端不尽量不明确:

// 未优化
$('div.data .gonzalez');

// 已优化
$('.data td.gonzalez');

尽量在选择器右端使用 tag.class,而左端尽可能只使用 tag 或者 .class。

避免过度具体:

$('.data table.attendees td.gonzalez');

// 在不影响结果的情况下尽量删掉中间多余部分
$('.data td.gonzalez');

简洁的 DOM 结构也有助于提升选择器的性能,因为选择器可能少走几层弯路去寻找那些元素。

尽量避免使用通配符,任何显式或隐式的使用通配符,都会降低选择器的性能:

$('.buttons > *');    // 极慢
$('.buttons').children(); // 好多了

$('.gender :radio');   // 隐式地使用通配符,慢
$('.gender *:radio');   // 显式地使用通配符,同上,慢
$('.gender input:radio'); // 嗯,快多了

使用事件代理

事件代理允许将一个事件绑定到某个容器上(例如一个无序列表 ul),而不是绑定到容器内所有元素上(例如列表元素 li)。虽说 $.fn.live 和 $.fn.delegate 都是将事件绑定到容器上,但是应尽可能是用 $.fn.delegate,毕竟其明确的上下文(相较于 $.fn.live 的上下文是 document)要小得多,避免了很多不必要的过滤。

除了性能方面的提升,如果给绑定了事件的容器内添加新元素,那么这些新元素就无须再次绑定事件了,这也是个优点。

// 不好 (如果列表元素非常多,你就悲剧了)
$('li.trigger').click(handlerFn);

// 好些:使用 $.fn.live 进行事件代理
$('li.trigger').live('click', handlerFn);

// 最好:使用 $.fn.delegate 进行事件代理
// 因为这样可以明确的指定一个上下文
$('#myList').delegate('li.trigger', 'click', handlerFn);

将元素从 DOM 卸载出来再操作

DOM 操作是比较慢的,所以应尽量避免直接操作 DOM。jQuery 在其 1.4 版中引入了 $.fn.detach 方法,可以将元素从 DOM 中卸载出来然后进行操作,操作好了之后再添加到 DOM 中:

var $table = $('#myTable');
var $parent = $table.parent();

$table.detach();
// ... 例如这里给表格添加了很多很多行
$parent.append(table);

使用外部样式表为大量元素修改样式

当使用 $.fn.css 为超过 20 个元素修改样式时,应考虑直接在页面中添加 style 标签,据说性能可提升 60%。

// 当元素少于 20 个时使用这个方法,多余 20 个时,速度就慢了
$('a.swedberg').css('color', '#asd123');

// 多余 20 个元素时,应考虑直接在页面中添加 style 标签
$('<style type="text/css">a.swedberg { color : #asd123 }</style>')
  .appendTo('head');

使用 $.data 替代 $.fn.data

将 $.data 应用于 DOM 元素上,比直接在选择器上调用 $.fn.data 要快 10 倍。当然,前提是要先明白 DOM 元素和 jQuery 结果集之间的区别。

// 速度一般
$(elem).data(key,value);

// 速度提升 10 倍
$.data(elem,key,value);

别在空元素上浪费时间

jQuery 不会主动告诉你,你正在一个空白的结果集上运行代码 ? 而且执行过程中并未出错。所以有时候再执行代码之前,需要先判断一下结果集是否为空:

// 不好:执行了三个函数之后
// 才发现结果集上没有任何元素
$('#nosuchthing').slideUp();

// 好
var $mySelection = $('#nosuchthing');
if ($mySelection.length) { $mySelection.slideUp(); }

// 最好:增加一个 doOnce 插件
jQuery.fn.doOnce = function(func){
  this.length && func.apply(this);
  return this;
}

$('li.cartitems').doOnce(function(){
  // 这里可以确保结果集不是空的
});

这种方法特别适用于 jQuery UI 方面,因为即使结果集中不包含任何元素,其开销也会很大。
变量的定义

可以在一条语句中定义多个变量:

// 老掉牙的写法
var test = 1;
var test2 = function() { ... };
var test3 = test2(test);

// 新写法
var test = 1,
  test2 = function() { ... },
  test3 = test2(test);

在自执行函数中,变量甚至可以不用定义:

(function(foo, bar) { ... })(1, 2);

条件判断

// 土方法
if (type == 'foo' || type == 'bar') { ... }

// 较先进的方法
if (/^(foo|bar)$/.test(type)) { ... }

// 使用对象查找
if (({ foo : 1, bar : 1 })[type]) { ... }
Javascript 相关文章推荐
翻译整理的jQuery使用查询手册
Mar 07 Javascript
基于jQuery的图片剪切插件
Aug 03 Javascript
JavaScript window.document的属性、方法和事件小结
Oct 24 Javascript
Javascript表格翻页效果的具体实现
Oct 05 Javascript
基于js实现微信发送好友如何分享到朋友圈、微博
Nov 30 Javascript
浅谈JavaScript中面向对象的的深拷贝和浅拷贝
Aug 01 Javascript
JavaScript鼠标事件,点击鼠标右键,弹出div的简单实例
Aug 03 Javascript
KnockoutJS 3.X API 第四章之数据控制流with绑定
Oct 10 Javascript
轻松理解JavaScript之AJAX
Mar 15 Javascript
深入理解ES6学习笔记之块级作用域绑定
Aug 19 Javascript
基于node搭建服务器,写接口,调接口,跨域的实例
May 13 Javascript
详解javascript 正则表达式之分组与前瞻匹配
May 30 Javascript
使用控制台破解百小度一个月只准改一次名字
Aug 13 #Javascript
js实现文本框宽度自适应文本宽度的方法
Aug 13 #Javascript
理解和运用JavaScript的闭包机制
Aug 13 #Javascript
详解JavaScript中jQuery和Ajax以及JSONP的联合使用
Aug 13 #Javascript
JavaScript的面向对象编程基础
Aug 13 #Javascript
JavaScript简单判断复选框是否选中及取出值的方法
Aug 13 #Javascript
JavaScript实现将文本框的值插入指定位置的方法
Aug 13 #Javascript
You might like
php通过asort()给关联数组按照值排序的方法
2015/03/18 PHP
PHP程序员不应该忽略的3点
2015/10/09 PHP
利用ajax和PHP实现简单的流程管理
2017/03/23 PHP
php + nginx项目中的权限详解
2017/05/23 PHP
解决laravel上传图片之后,目录有图片,但是访问不到(404)的问题
2019/10/14 PHP
js中获取事件对象的方法小结
2011/03/13 Javascript
Jquery 返回json数据在IE浏览器中提示下载的问题
2014/05/18 Javascript
Knockoutjs 学习系列(一)ko初体验
2016/06/07 Javascript
每个程序员都需要学习 JavaScript 的7个理由小结
2016/09/03 Javascript
JavaScript中数组slice和splice的对比小结
2016/09/22 Javascript
ComboBox(下拉列表框)通过url加载调用远程数据的方法
2017/08/06 Javascript
2种简单的js倒计时方式
2017/10/20 Javascript
vue实现某元素吸顶或固定位置显示(监听滚动事件)
2017/12/13 Javascript
react-native 圆弧拖动进度条实现的示例代码
2018/04/12 Javascript
Element UI 自定义正则表达式验证方法
2018/09/04 Javascript
利用JS响应式修改vue实现页面的input值
2019/09/02 Javascript
谈谈IntersectionObserver懒加载的具体使用
2019/10/15 Javascript
解决vue+ element ui 表单验证有值但验证失败问题
2020/01/16 Javascript
JavaScript动画实例之粒子文本的实现方法详解
2020/07/28 Javascript
jQuery实现计算器功能
2020/10/19 jQuery
python字符串连接的N种方式总结
2014/09/17 Python
浅谈Python的Django框架中的缓存控制
2015/07/24 Python
Python编程使用tkinter模块实现计算器软件完整代码示例
2017/11/29 Python
python实现员工管理系统
2018/01/11 Python
Python应用库大全总结
2018/05/30 Python
Python使用numpy产生正态分布随机数的向量或矩阵操作示例
2018/08/22 Python
Pycharm修改python路径过程图解
2020/05/22 Python
python中通过pip安装库文件时出现“EnvironmentError: [WinError 5] 拒绝访问”的问题及解决方案
2020/08/11 Python
CSS3实现超慢速移动动画效果非常流畅无卡顿
2014/06/15 HTML / CSS
英国女装网上商店:I Saw It First
2018/10/18 全球购物
面向对象编程是如何提高软件开发水平的
2014/05/06 面试题
解释一下钝化(Swap out)
2016/12/26 面试题
建筑专业自荐信
2013/10/18 职场文书
车辆转让协议书
2014/09/24 职场文书
优秀党员事迹材料
2014/12/18 职场文书
学雷锋活动简报
2015/07/20 职场文书