本文深入探讨JavaScript插件开发的进阶实践,重点分析如何将传统的jQuery插件方式重构为面向对象架构。通过引入BigBear类与Overlay组合模式,解决全局变量污染与依赖耦合问题,展示如何在不依赖jQuery特定机制的前提下,提升代码的可读性、可维护性及功能扩展能力。
开篇分析
一、开篇分析
大家好!回顾本系列的第一篇文章,我们主要探讨了基于jQuery机制开发插件的基础方法。
今天,我们将带着上一篇遗留的问题,继续深入插件开发的旅程。之前的疑问主要集中在以下两点:
第一,如果项目技术栈发生变更,不再依赖jQuery,之前编写的强依赖jQuery机制的插件将无法使用,此时应如何进行重构?
第二,在重构插件的核心逻辑时,我们应该采用何种代码组织策略?
带着这些问题,我们开始今天的学习。
需要说明的是,我并不否定jQuery插件的开发方式,而是主张从不同角度审视问题。例如,jQuery插件方式具有以下优点:
首先,将全部代码封装在闭包(即立即执行函数)中,这相当于创建了一个私有作用域。外部无法直接访问内部信息,从而避免了全局变量的污染。
其次,这种方式能够避免全局依赖,防止第三方代码的破坏,同时兼容jQuery的操作符'$'和'jQuery'。
那么,我们的重构将以何种方式组织代码?是采用面向对象的思想(OOP),还是坚持过程化的思路,亦或是两者结合?请继续阅读后续内容。
重构昨天的例子
二、重构昨天的例子
以下是之前文章中的JavaScript源码部分:
代码如下:
(function($){
$.fn.bigbear = function(opts){
opts = $.extend({},$.fn.bigbear.defaults,opts) ;
return this.each(function(){
var elem = $(this) ;
elem.find("span").text(opts["title"]) ;
$.get(opts["url"],function(data){
elem.find("div").text(data["text"]) ;
}) ;
}) ;
} ;
$.fn.bigbear.defaults = {
title : "这是一个简单的测试" ,
url : "data.json"
} ;
})(jQuery) ;
我们来逐行分析这段代码:
首先明确插件的功能需求:
第一,显示用户设置的标题文字信息。
第二,通过异步方式动态获取内容信息。
需求明确后,即可展开讨论。从上述代码可以看出,逻辑组织较为松散,过程化思维特征明显。因此,第一步是将功能需求以类的方式有效组织起来。
请看如下重构后的代码:
代码如下:
$(function(){
"#bb").bigbear() ;
}) ;
(function($){
$.fn.bigbear = function(opts){
opts = $.extend({},$.fn.bigbear.defaults,opts) ;
return this.each(function(){
var elem = $(this) ;
var bb = new BigBear(elem,opts) ;
bb.getElem().trigger("data") ;
}) ;
} ;
$.fn.bigbear.defaults = {
title : "这是一个简单的测试" ,
url : "data.json"
} ;
})(jQuery) ;
function BigBear(elem,opts){
this.elem = elem ;
this.opts = opts ;
this.init() ;
} ;
var bbProto = BigBear.prototype ;
bbProto.getElem = function(){
return this.elem ;
} ;
bbProto.getOpts = function(){
return this.opts ;
} ;
bbProto.init = function(){
var that = this ;
this.getElem().on("data",function(){
that._setTitle(that.getOpts()["title"]) ;
$.get(that.getOpts()["url"],function(result){
that.getElem().find("div").text(result["text"]) ;
}) ;
}) ;
} ;
bbProto._setTitle = function(text){
this.getElem().find("span").text(text) ;
} ;
代码量虽然增加了,但这正是从面向对象角度审视问题的体现。我们先分析功能需求,再设计相应的类。尽管初期设计可能不够完美,但视角的转变提高了代码的可读性,并更利于维护,这正是我们的目标。
以下摘自Bootstrap JavaScript部分的源码实现,如下图所示:

可以看出,这也采用了相似的实现方式,通过类来维护插件的主要逻辑。
增加新功能,引出额外的类
三、增加新功能,引出额外的类
现在需求有所增加,需要在用户体验上做出变化:在加载数据时显示“loading”效果。
实现思路如下:在原始内容区将文字设置为“装载数据中……”,然后引入一个新的类,如下所示:
代码如下:
function Overlay(){
} ;
var olProto = Overlay.prototype ;
olProto.show = function(){} ;
olProto.hide = function(){} ;
// 具体实现就不写了
遮罩层类已经就绪,现在我们需要将其集成进来。我们采用组合的方式接入,如下所示:
代码如下:
function BigBear(elem,opts){
this.elem = elem ;
this.opts = opts ;
this.overlay = new Overlay() ;
this.init() ;
} ;
var bbProto = BigBear.prototype ;
bbProto.getElem = function(){
return this.elem ;
} ;
bbProto.getOpts = function(){
return this.opts ;
} ;
bbProto.init = function(){
var that = this ;
var loadingText = "数据装载中。。。" ;
this.getElem().on("data",function(){
that._setTitle(that.getOpts()["title"]) ;
that.overlay.show() ;
that.getElem().find("div").text(loadingText) ;
$.get(that.getOpts()["url"],function(result){
that.overlay.hide() ;
that.getElem().find("div").text(result["text"]) ;
}) ;
}) ;
} ;
bbProto._setTitle = function(text){
this.getElem().find("span").text(text) ;
} ;
至此,我们的功能开发基本完成。相比第一个版本,这种写法有了显著提升。当然,这并非最优实现,仍需从细节上不断重构,但这提供了一种可选的插件开发方式。
以下是完整代码:
代码如下:
$(function(){
"#bb").bigbear() ;
}) ;
(function($){
$.fn.bigbear = function(opts){
opts = $.extend({},$.fn.bigbear.defaults,opts) ;
return this.each(function(){
var elem = $(this) ;
var bb = new BigBear(elem,opts) ;
bb.getElem().trigger("data") ;
}) ;
} ;
$.fn.bigbear.defaults = {
title : "这是一个简单的测试" ,
url : "data.json"
} ;
})(jQuery) ;
function BigBear(elem,opts){
this.elem = elem ;
this.opts = opts ;
this.overlay = new Overlay() ;
this.init() ;
} ;
var bbProto = BigBear.prototype ;
bbProto.getElem = function(){
return this.elem ;
} ;
bbProto.getOpts = function(){
return this.opts ;
} ;
bbProto.init = function(){
var that = this ;
var loadingText = "数据装载中。。。" ;
this.getElem().on("data",function(){
that._setTitle(that.getOpts()["title"]) ;
that.overlay.show() ;
that.getElem().find("div").text(loadingText) ;
$.get(that.getOpts()["url"],function(result){
that.overlay.hide() ;
that.getElem().find("div").text(result["text"]) ;
}) ;
}) ;
} ;
bbProto._setTitle = function(text){
this.getElem().find("span").text(text) ;
} ;
function Overlay(){
} ;
var olProto = Overlay.prototype ;
olProto.show = function(){} ;
olProto.hide = function(){} ;
// 具体实现就不写了
本文暂时到此结束,希望小伙伴们对JavaScript插件化开发有了全新的认识。