8.2 添加新的全局函数

jQuery内置的某些功能是通过全局函数提供的。所谓全局函数,实际上就是jQuery对象的方法,但从实践的角度上看,它们是位于jQuery命名空间内部的函数。

使用这种技术的一个典型的例子就是$.ajax()函数。$.ajax()所做的一切都可以通过简单地调用一个名为ajax()的常规全局函数来实现,但是,这种方式会给我们带来函数名冲突的问题。通过把这个函数放在jQuery的命名空间内,我们只需避免它与其他的jQuery方法冲突即可。对想要使用插件的人而言,jQuery命名空间也是一个提醒,即要使用这个插件,必须要有jQuery库。

核心jQuery库提供的很多全局函数都是实用方法;所谓实用方法,就是一些常用功能的快捷方式,但即使手工编写同样功能的代码也不是很难。数组处理方法$.each()$.map()$.grep()都是实用方法。为了演示这些实用方法的创建方式,我们再给jQuery核心库添加两个小函数。

要向jQuery的命名空间中添加一个函数,只需将这个新函数指定为jQuery对象的一个属性即可参见代码清单8-1。

代码清单8-1

  1. (function($) {
  2. $.sum = function(array) {
  3. //在这里添加代码
  4. };
  5. })(jQuery);

于是,我们就可以在使用这个插件的任何代码中,编写如下代码:

  1. $.sum();

这跟一个基本的调用没什么两样,调用之后就会执行函数体内的代码。

这个求和函数接受一个数组作为参数,然后把数组的值加在一起,最后返回结果。这个插件的代码非常简单,如代码清单8-2所示。

代码清单8-2

  1. (function($) {
  2. $.sum = function(array) {
  3. var total = 0;
  4.  
  5. $.each(array, function(index, value) {
  6. value = $.trim(value);
  7. value = parseFloat(value) || 0;
  8.  
  9. total += value;
  10. });
  11. return total;
  12. };
  13. })(jQuery);

注意,我们在这里使用了$.each()方法遍历了数组的值。当然也可以在此使用for循环,但既然我们能够确定页面会在加载插件之前先加载jQuery库,使用习以为常的语法是很自然的。同样,$.each()的好处在于它的第一个参数是一个对象。

为了测试这个新插件,我们创建了一个表格,其中包含库存的食品:

  1. <table id="inventory">
  2. <thead>
  3. <tr class="one">
  4. <th>Product</th> <th>Quantity</th> <th>Price</th>
  5. </tr>
  6. </thead>
  7. <tfoot>
  8. <tr class="two" id="sum">
  9. <td>Total</td> <td></td> <td></td>
  10. </tr>
  11. <tr id="average">
  12. <td>Average</td> <td></td> <td></td>
  13. </tr>
  14. </tfoot>
  15. <tbody>
  16. <tr>
  17. <td><a href="spam.html" data-tooltip-text="Nutritious and delicious!">Spam
  18. </a></td> <td>4</td> <td>2.50</td>
  19. </tr>
  20. <tr>
  21. <td><a href="egg.html" data-tooltip-text="Farm fresh or scrambled!">Egg</a>
  22. </td> <td>12</td> <td>4.32</td>
  23. </tr>
  24. <tr>
  25. <td><a href="gourmet-spam.html" data-tooltip-text="Chef Hermann's recipe.">
  26. Gourmet Spam</a></td> <td>14</td> <td>7.89</td>
  27. </tr>
  28. </tbody>
  29. </table>

 下载代码示例

如同本书其他HTML、CSS以及JavaScript示例一样,上面的标记只是完整文档的一个片段。如果读者想试一试这些示例,可以从以下地址下载完整的示例 代码: Packt Publishing 网站 http://www.packtpub.com/support ,或者本书网站 http://book.learningjquery.com/

接下来,我们再写几行脚本,让它负责填写表格中表示数量之和的单元格,参见代码清单8-3。

代码清单8-3

  1. $(document).ready(function() {
  2. var $inventory = $('#inventory tbody');
  3. var quantities = $inventory.find('td:nth-child(2)')
  4. .map(function(index, qty) {
  5. return $(qty).text();
  6. }).get();
  7. var sum = $.sum(quantities);
  8. $('#sum').find('td:nth-child(2)').text(sum);
  9. });

通过浏览器查看HTML页面的结果表明,我们的插件工作正常,如图8-1所示。

添加多个函数 - 图1

图 8-1

添加多个函数

如果我们想在插件中提供多个全局函数,可以独立地声明这些函数。下面,我们再来增强插件,添加一个用于计算数值数组平均值的函数,参见代码清单8-4。

代码清单8-4

  1. (function($) {
  2. $.sum = function(array) {
  3. var total = 0;
  4.  
  5. $.each(array, function(index, value) {
  6. value = $.trim(value);
  7. value = parseFloat(value) || 0;
  8.  
  9. total += value;
  10. });
  11. return total;
  12. };
  13. $.average = function(array) {
  14. if ($.isArray(array)) {
  15. return $.sum(array) / array.length;
  16. }
  17. return '';
  18. };
  19. })(jQuery);

为了方便起见,我们使用了$.sum()插件作为辅助,以方便地返回$.average()的值。同时,为避免出错,这里还检测了传入的参数,在计算平均值之前确保它是一个数组:

好了,现在第二个方法也就绪了,接下我们就用同样的方式来调用它,参见代码清单8-5。

代码清单8-5

  1. $(document).ready(function() {
  2. var $inventory = $('#inventory tbody');
  3. var prices = $inventory.find('td:nth-child(3)')
  4. .map(function(index, qty) {
  5. return $(qty).text();
  6. }).get();
  7. var average = $.average(prices);
  8. $('#average').find('td:nth-child(3)').text(average.toFixed(2));
  9. });

于是,平均值出现了在表格的第三栏,如图8-2所示。

添加多个函数 - 图2

图 8-2

  • 扩展全局jQuery对象

事实上,利用$.extend()函数,还可以通过另外一种语法来定义全局函数,参见代码清单8-6。

代码清单8-6

  1. (function($) {
  2. $.extend({
  3. sum: function(array) {
  4. var total = 0;
  5.  
  6. $.each(array, function(index, value) {
  7. value = $.trim(value);
  8. value = parseFloat(value) || 0;
  9.  
  10. total += value;
  11. });
  12. return total;
  13. },
  14. average: function(array) {
  15. if ($.isArray(array)) {
  16. return $.sum(array) / array.length;
  17. }
  18. return '';
  19. }
  20. });
  21. })(jQuery);

这样调用$.extend()就可以给全局jQuery对象添加属性(如果原来有相同的属性,就会替换原来的属性)。这样也定义了相同的$.sum()$.average()方法。

  • 使用命名空间隔离函数

我们的插件在jQuery命名空间中创建了两个独立的全局函数。但这样写有可能污染命名空间。换句话说,其他jQuery插件也可能定义相同的函数名。为了避免冲突,最好的办法是把属于一个插件的全局函数都封装到一个对象中,如代码清单8-7所示。

代码清单8-7

  1. (function($) {
  2. $.mathUtils = {
  3. sum: function(array) {
  4. var total = 0;
  5.  
  6. $.each(array, function(index, value) {
  7. value = $.trim(value);
  8. value = parseFloat(value) || 0;
  9.  
  10. total += value;
  11. });
  12. return total;
  13. },
  14. average: function(array) {
  15. if ($.isArray(array)) {
  16. return $.mathUtils.sum(array) / array.length;
  17. }
  18. return '';
  19. }
  20. };
  21. })(jQuery);

这个模式的本质是为所有的全局函数又创建了一个命名空间,叫做jQuery.mathUtils。虽然我们还称它们为全局函数,但实际上它们已经成了 mathUtils 对象的方法了,而mathUtils对象则保存在jQuery对象的属性中。结果,在调用它们时就必须得加上插件的名字了:

  1. $.mathUtils.sum(sum);
  2. $.mathUtils.average(average);

使用这种技术(以及足够独特的命名空间),就能够避免全局函数污染命名空间。至此,我们已经掌握了开发插件的基本方法。在把这些函数保存到名为jquery.mathutils.js的文件中之后,就可以将其包含在其他页面中通过其他脚本来使用这些函数了。

 选择命名空间

对于仅限于个人使用的函数,一般来说还是把它保存在项目的命名空间中最方便。换句话说,不要保存在jQuery命名空间中,而要选择一个我们自己的全局对象。比如说,可以将 ljQ 作为全局对象,那么 $.mathUtils.sum()$.mathUtils.average() 就要写成 ljQ.mathUtils.sum()ljQ.mathUtils.average()了。这样,就可以彻底避免自定义的插件方法与第三方插件方法发生命名冲突。

我们已经介绍了如何保护命名空间,以及确保jQuery插件假定的库的有效性。不过,这些都还是组织上的好处。要想真正体验到jQuery插件的威力,还需要学会为个别jQuery对象实例创建新的方法。