本文深入解析jQuery组件的扩展与封装技术。首先利用$.extend方法重写$.ajax的错误处理逻辑,实现全局错误提示而不改变原有调用习惯。随后,通过自研combobox组件实例,演示如何基于jQuery.fn定义私有方法、合并默认参数与用户配置,以及处理远程数据加载。文章涵盖匿名函数封装、参数合并及方法扩展等核心概念,帮助开发者掌握构建可复用JS组件的基础流程。
前言:
此前已分享多篇Bootstrap组件的使用心得,本文旨在探讨JavaScript组件的扩展与封装机制。我们将深入体验jQuery提供的$.extend方法的神奇之处,学习如何自定义组件。例如,若希望扩展一个$("#id").MyJsControl({})作为自有组件,具体应如何操作?接下来我们将逐步解析实现过程。
扩展已经存在的组件
一、扩展已经存在的组件
1、需求背景
在实际开发中,我们常通过jquery.ajax向后端发送请求,典型代码形式如
$.ajax({
type: "post",
url: "/User/Edit",
data: { data: JSON.stringify(postdata) },
success: function (data, status) {
if (status == "success") {
toastr.success('提交数据成功');
$("#tb_aaa").bootstrapTable('refresh');
}
},
error: function (e) {
},
complete: function () {
}
});
此类代码极为常见。此时,我们面临一个需求:在调用ajax请求时,不希望每次手动编写error:function(e){},但又希望每次ajax发生错误时,都能自动将错误信息输出至浏览器供用户查看。该如何实现?
2、实现原理
要实现上述效果并不复杂,我们可以将$.ajax({})进行封装,在封装的公共方法中定义error对应的事件即可。确实,这样做能达到要求,但并非完美方案,原因有二:1)在jQuery基础上再封装一层,效率不够高;2)需要改变调用者的习惯,每次调用ajax时需遵循我们定义的方法规则,而无法直接使用原生$.ajax({})写法,这并非我们期望的结果。
那么,如何做到既不封装控件,又能实现上述需求呢?答案是通过$.extend去扩展原生的jquery.ajax。
实现过程并不复杂,通过以下一段代码即可达成目标。
(function ($) {
//1.得到$.ajax的对象
var _ajax = $.ajax;
$.ajax = function (options) {
//2.每次调用发送ajax请求的时候定义默认的error处理方法
var fn = {
error: function (XMLHttpRequest, textStatus, errorThrown) {
toastr.error(XMLHttpRequest.responseText, '错误消息', { closeButton: true, timeOut: 0, positionClass: 'toast-top-full-width' });
},
success: function (data, textStatus) { },
beforeSend: function (XHR) { },
complete: function (XHR, TS) { }
}
//3.如果在调用的时候写了error的处理方法,就不用默认的
if (options.error) {
fn.error = options.error;
}
if (options.success) {
fn.success = options.success;
}
if (options.beforeSend) {
fn.beforeSend = options.beforeSend;
}
if (options.complete) {
fn.complete = options.complete;
}
//4.扩展原生的$.ajax方法,返回最新的参数
var _options = $.extend(options, {
error: function (XMLHttpRequest, textStatus, errorThrown) {
fn.error(XMLHttpRequest, textStatus, errorThrown);
},
success: function (data, textStatus) {
fn.success(data, textStatus);
},
beforeSend: function (XHR) {
fn.beforeSend(XHR);
},
complete: function (XHR, TS) {
fn.complete(XHR, TS);
}
});
//5.将最新的参数传回ajax对象
_ajax(_options);
};
})(jQuery);
若未接触过jQuery中$.extend方法的读者可能对此感到困惑。首先,我们来看看jQuery API对$.extend()方法的官方解释。

具体含义如何?通过官方提供的两个示例即可明了。
示例一:
var settings = { validate: false, limit: 5, name: "foo" };
var options = { validate: true, name: "bar" };
$.extend(settings, options);
运行结果:
settings == { validate: true, limit: 5, name: "bar" }
示例二:
var empty = {};
var defaults = { validate: false, limit: 5, name: "foo" };
var options = { validate: true, name: "bar" };
var settings = $.extend(empty, defaults, options);
运行结果:
settings == { validate: true, limit: 5, name: "bar" }
empty == { validate: true, limit: 5, name: "bar" }
以上两个简单示例表明,extend()方法的作用是合并两个对象:若存在相同属性则覆盖,不存在则添加。原理十分简单。
理解了$.extend()的作用,我们便能大致看懂前述扩展jquery.ajax的实现逻辑。主要步骤包括:
1)定义默认的error处理方法。
var fn = {
error: function (XMLHttpRequest, textStatus, errorThrown) {
toastr.error(XMLHttpRequest.responseText, '错误消息', { closeButton: true, timeOut: 0, positionClass: 'toast-top-full-width' });
},
success: function (data, textStatus) { },
beforeSend: function (XHR) { },
complete: function (XHR, TS) { }
}
2)判断用户在调用$.ajax({})时是否自定义了error:function(){},若已定义则使用用户自定义的方法,否则使用默认的error处理方法。
3)使用$.extend()将error默认处理方法传入$.ajax()的参数中。观察options参数可知,其包含$.ajax()方法内的所有参数,随后用默认函数fn对其进行扩展即可。
通过以上三步,即可实现对$.ajax()方法内error默认处理方法的扩展。这种扩展方式对用户透明,使用者仍可继续使用$.ajax({});发送ajax请求,若无特殊情况,无需编写error处理方法。
3、组件扩展的意义
使用组件扩展,有助于在原有组件基础上增加与系统业务相关的处理逻辑,同时保持调用方式与原生组件一致,避免了在组件上层再封装一层所带来的冗余代码。
扩展自己组件
二、扩展自己组件
前文通过$.extend()方法扩展了$.ajax()的error事件处理方法。接下来,我们尝试封装一个自己的组件。该组件功能简单但具有代表性,以select组件为例。通常情况下,select中的option数据需从数据库获取,常规做法是发送ajax请求,并在success方法中拼接HTML。现在,我们封装一个select远程获取数据的方法。
1、代码实现以及使用示例
首先展示完整代码:
(function ($) {
//1.定义jquery的扩展方法combobox
$.fn.combobox = function (options, param) {
if (typeof options == 'string') {
return $.fn.combobox.methods[options](this, param);
}
//2.将调用时候传过来的参数和default参数合并
options = $.extend({}, $.fn.combobox.defaults, options || {});
//3.添加默认值
var target = $(this);
target.attr('valuefield', options.valueField);
target.attr('textfield', options.textField);
target.empty();
var option = $('<option></option>');
option.attr('value', '');
option.text(options.placeholder);
target.append(option);
//4.判断用户传过来的参数列表里面是否包含数据data数据集,如果包含,不用发ajax从后台取,否则否送ajax从后台取数据
if (options.data) {
init(target, options.data);
}
else {
//var param = {};
options.onBeforeLoad.call(target, options.param);
if (!options.url) return;
$.getJSON(options.url, options.param, function (data) {
init(target, data);
});
}
function init(target, data) {
$.each(data, function (i, item) {
var option = $('<option></option>');
option.attr('value', item[options.valueField]);
option.text(item[options.textField]);
target.append(option);
});
options.onLoadSuccess.call(target);
}
target.unbind("change");
target.on("change", function (e) {
if (options.onChange)
return options.onChange(target.val());
});
}
//5.如果传过来的是字符串,代表调用方法。
$.fn.combobox.methods = {
getValue: function (jq) {
return jq.val();
},
setValue: function (jq, param) {
jq.val(param);
},
load: function (jq, url) {
$.getJSON(url, function (data) {
jq.empty();
var option = $('<option></option>');
option.attr('value', '');
option.text('请选择');
jq.append(option);
$.each(data, function (i, item) {
var option = $('<option></option>');
option.attr('value', item[jq.attr('valuefield')]);
option.text(item[jq.attr('textfield')]);
jq.append(option);
});
});
}
};
//6.默认参数列表
$.fn.combobox.defaults = {
url: null,
param: null,
data: null,
valueField: 'value',
textField: 'text',
placeholder: '请选择',
onBeforeLoad: function (param) { },
onLoadSuccess: function () { },
onChange: function (value) { }
};
})(jQuery);
接下来查看自定义组件的使用方式:
用法一:通过URL远程取数据并初始化
首先定义一个空的select元素
<select id="sel_search_plant" class="form-control"></select>
随后对其进行初始化
$(function(){
$('#sel_search_plant').combobox({
url: '/apiaction/Plant/Find',
valueField: 'TM_PLANT_ID',
textField: 'NAME_C'
});
})
参数设置简单,此处不再逐一介绍。实现过程十分简洁。
用法二:取值和设置
var strSelectedValue = $('#sel_search_plant').combobox("getValue");
$('#sel_search_plant').combobox("setValue", "aaa");
对于简单的select标签,笔者认为getValu和SetValue方法意义不大,因为直接通过$('#sel_search_plant').val()即可解决。此处仅作演示,试想若封装成类似select2或multiselect等复杂组件,getValue和setValue的意义便凸显出来。
2、代码详细讲解
若读者能一眼看懂上述实现代码,说明具备丰富的组件封装经验,下文可略过。若看不懂,不妨将代码拆解,详细剖析其内部逻辑。
(1)首先观察我们常见的如下写法:
(function ($) {
//....封装组件逻辑
})(jQuery);
初看这种用法令人困惑,不知其意。使用多次后便会明白,这实际上是一种匿名函数的形式。将其拆解如下:
var fn = function($){
//.....组件封装逻辑
};
fn(jQuery);
这种写法表示先定义一个方法,然后立即调用该方法,jQuery作为实参传入。打开jquery.js源文件可见,jQuery是该文件中的全局变量。
(2)定义自己的组件的代码:
$.fn.combobox = function (options, param) {
};
熟悉这种写法的读者应知,这表示向jQuery对象添加自定义方法。例如,若想使用文章开头的$("#id").MyJsControl({})这种调用方式,便可如此定义 $.fn.MyJsControl=function(options){} 。
(3) options = $.extend({}, $.fn.combobox.defaults, options || {}); 这一句,回顾前文的朋友应记得extend方法。此处再次使用,其含义明确,即合并默认参数与用户传入的参数。
(4)默认参数列表
$.fn.combobox.defaults = {
url: null,
param: null,
data: null,
valueField: 'value',
textField: 'text',
placeholder: '请选择',
onBeforeLoad: function (param) { },
onLoadSuccess: function () { },
onChange: function (value) { }
};
若用户未传参,则使用默认参数列表。细心的读者可能发现,博主此前分享的其他Bootstrap组件的js文件中均包含此类default参数列表。我们选取两个示例:
bootstrap上传组件

bootstrap table组件


基本均采用此类用法。如此看来,自行封装JS组件并非难事。
三、总结
以上为博主对JS组件扩展及封装用法的认识与总结。所述内容均为较为基础的封装,若要实现如bootstrap table般复杂的组件,仍有较大差距。然而,万丈高楼平地起,打好基础后,封装自己的table组件并非难题。若有任何疑问,请留言,小编会及时回复。在此衷心感谢大家对本站的支持!