jQuery插件写法总结以及面向对象方式写法总结
写法一
插件主体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | ( function ($, window){ // 初始态定义 var _oDialogCollections = {}; // 插件定义 $.fn.MNDialog = function (_aoConfig) { // 默认参数,可被重写 var defaults = { // string sId : "" , // num nWidth : 400, // bollean bDisplayHeader : true , // object oContentHtml : "" , // function fCloseCallback : null }; var _oSelf = this , $ this = $( this ); // 插件配置 this .oConfig = $.extend(defaults, _aoConfig); // 初始化函数 var _init = function () { if (_oDialogCollections) { // 对于已初始化的处理 // 如果此时已经存在弹框,则remove掉再添加新的弹框 } // 初始化弹出框数据 _initData(); // 事件绑定 _loadEvent(); // 加载内容 _loadContent(); } // 私有函数 var _initData = function () {}; var _loadEvent = function () {}; var _loadContent = function () { // 内容(分字符和函数两种,字符为静态模板,函数为异步请求后组装的模板,会延迟,所以特殊处理) if ($.isFunction(_oSelf.oConfig.oContentHtml)) { _oSelf.oConfig.oContentHtml.call(_oSelf, function (oCallbackHtml) { // 便于传带参函数进来并且执行 _oSelf.html(oCallbackHtml); // 有回调函数则执行 _oSelf.oConfig.fLoadedCallback && _oSelf.oConfig.fLoadedCallback.call(_oSelf, _oSelf._oContainer$); }); } else if ($.type(_oSelf.oConfig.oContentHtml) === "string" ) { _oSelf.html(_oSelf.oConfig.oContentHtml); _oSelf.oConfig.fLoadedCallback && _oSelf.oConfig.fLoadedCallback.call(_oSelf, _oSelf._oContainer$); } else { console.log( "弹出框的内容格式不对,应为function或者string。" ); } }; // 内部使用参数 var _oEventAlias = { click : 'D_ck' , dblclick : 'D_dbl' }; // 提供外部函数 this .close = function () { _close(); } // 启动插件 _init(); // 链式调用 return this ; }; // 插件结束 })(jQuery, window); |
调用
1 2 3 4 5 6 7 8 9 10 11 12 13 | var MNDialog = $( "#header" ).MNDialog({ sId : "#footer" , //覆盖默认值 fCloseCallback : dialog, //回调函数 oContentHtml : function (_aoCallback){ _aoCallback(_oEditGrpDlgView.el); } } }); // 调用提供的函数 MNDialog.close; function dialog(){ } |
点评
1. 自调用匿名函数
1 2 3 | ( function ($, window) { // jquery code })(jQuery, window); |
用处:通过定义一个匿名函数,创建了一个“私有”的命名空间,该命名空间的变量和方法,不会破坏全局的命名空间。这点非常有用也是一个JS框架必须支持的功能,jQuery被应用在成千上万的JavaScript程序中,必须确保jQuery创建的变量不能和导入他的程序所使用的变量发生冲突。
2. 匿名函数为什么要传入window
通过传入window变量,使得window由全局变量变为局部变量,当在jQuery代码块中访问window时,不需要将作用域链回退到顶层作用域,这样可以更快的访问window;这还不是关键所在,更重要的是,将window作为参数传入,可以在压缩代码时进行优化,看看jquery.min.js:
1 | ( function (a,b){})(jQuery, window); // jQuery被优化为a, window 被优化为 b |
3. 全局变量this定义
1 2 | var _oSelf = this , $ this = $( this ); |
使得在插件的函数内可以使用指向插件的this
4. 插件配置
1 | this .oConfig = $.extend(defaults, _aoConfig); |
设置默认参数,同时也可以再插件定义时传入参数覆盖默认值
5. 初始化函数
一般的插件会有init初始化函数并在插件的尾部初始化
6. 私有函数、公有函数
私有函数:插件内使用,函数名使用”_”作为前缀标识
共有函数:可在插件外使用,函数名使用”this.”作为前缀标识,作为插件的一个方法供外部使用
7. return this
最后返回jQuery对象,便于jQuery的链式操作
写法二
主体结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | ( function ($){ $.fn.addrInput = function (_aoOptions){ var _oSelf = this ; _oSelf.sVersion = 'version-1.0.0' ; _oSelf.oConfig = { nInputLimitNum : 9 }; // 插件配置 $.extend(_oSelf.oConfig, _aoOptions); // 调用这个对象的方法,传递this $.fn.addrInput._initUI.call(_oSelf, event); $.fn.addrInput._initEvents.call(_oSelf); // 提供外部函数 this .close = function () { _close(); } //返回jQuery对象,便于Jquery的链式操作 return _oSelf; } $.fn.addrInput._initUI = function (event){ var _oSelf = this , _oTarget = $(event.currentTarget); } $.fn.addrInput._initEvents = function (){} })(window.jQuery); |
点评
1. 美观
插件的方法写在外部,并通过在插件主体传递this的方式调用
2. 定义插件版本号
不过在这里还是没有用到
3. 关于call
这里的第一个参数为传递this,后面的均为参数
语法:
1 | call([thisObj[,arg1[, arg2[, [,.argN]]]]]) |
定义:调用一个对象的一个方法,以另一个对象替换当前对象。
说明:call 方法可以用来代替另一个对象调用一个方法。call 方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。如果没有提供 thisObj 参数,那么 Global 对象被用作 thisObj。
4. 关于”this”
在插件的方法中,可能有用到指向插件的this、和指向事件触发的this,所以事件触发的this用event来获取:event.cuerrntTarget
- event.currentTarget:指向事件所绑定的元素,按照事件冒泡的方式,向上找到元素
- event.target:始终指向事件发生时的元素
如:
html代码
1 2 3 | < div id="wrapper"> < a href="#" id="inner">click here!</ a > </ div > |
js代码
1 2 3 4 5 6 7 8 9 10 | $( '#wrapper' ).click( function (e) { console.log( '#wrapper' ); console.log(e.currentTarget); console.log(e.target); }); $( '#inner' ).click( function (e) { console.log( '#inner' ); console.log(e.currentTarget); console.log(e.target); }); |
结果输出
1 2 3 4 5 6 | #inner < a href="#" id="inner">click here!</ a > < a href="#" id="inner">click here!</ a > #wrapper < div id="wrapper">< a href="#" id="inner">click here!</ a ></ div > < a href="#" id="inner">click here!</ a > |
写法三(原生写法)
主体结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | var MemberCard = function(_aoOption){ // 配置(默认是从外面传进来) _aoOption || (_aoOption = {}) ; // 初始化函数 _init(this); } var _init = function(_aoSelf) { // 函数执行 _initData(_aoSelf); // 调用对象的私有方法 _aoSelf._timedHide(); } var _initData = function ( _aoSelf ) {} // 私有方法 MemberCard.prototype._timedHide = function(_aoOptions) { var _oSelf = this; clearTimeout(this.iHideTimer); // 使用underscore.js的extend方法来实现属性覆盖 var oDefault = extend( { nHideTime: 300 }, _aoOptions ); _oSelf.iHideTimer = setTimeout( function(){ // 调用对象的共有方法 _oSelf.hide(); }, oDefault.nHideTime); } // 公有方法 MemberCard.prototype.hide = function(_aoOptions) {} |
使用
1 2 | var oColleagueCard = new MemberCard({ nHideTime: 200 }); oColleagueCard.hide(); |
点评
1. 关于属性覆盖(对象深拷贝)
原生函数实现方法
1 2 3 4 5 6 7 8 9 10 11 12 13 | function getType(o){ return ((_t = typeof (o)) == "object" ? o== null && "null" || Object.prototype.toString.call(o).slice(8,-1):_t).toLowerCase(); } function extend(destination,source){ for ( var p in source){ if (getType(source[p])== "array" ||getType(source[p])== "object" ){ destination[p]=getType(source[p])== "array" ?[]:{}; arguments.callee(destination[p],source[p]); } else { destination[p]=source[p]; } } } |
demo:
1 2 3 4 5 6 | var test={a: "ss" ,b:[1,2,3],c:{d: "css" ,e: "cdd" }}; var test1={}; extend(test1,test); test1.b[0]= "change" ; //改变test1的b属性对象的第0个数组元素 alert(test.b[0]); //不影响test,返回1 alert(test1.b[0]); //返回change |
基于jQuery的实现方法
1 | jQuery.extend([deep], target, object1, [objectN]); |
用一个或多个其他对象来扩展一个对象,返回被扩展的对象。
如果不指定target,则给jQuery命名空间本身进行扩展。这有助于插件作者为jQuery增加新方法。 如果第一个参数设置为true,则jQuery返回一个深层次的副本,递归地复制找到的任何对象。否则的话,副本会与原对象共享结构。 未定义的属性将不会被复制,然而从对象的原型继承的属性将会被复制。
demo:
1 2 | var options = {id: "nav" , class : "header" } var config = $.extend({id: "navi" }, options); //config={id: "nav", class: "header"} |
2. 关于this
这个对象的所有方法的this都指向这个对象,所以就不需要重新指定
写法四
主体结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | function EditorUtil() { this ._editorContent = $( '#editor_content' ); this ._picBtn = $( '#project_pic' ); this .ieBookmark = null ; } EditorUtil.prototype = { consturctor: EditorUtil, noteBookmark: function () { }, htmlReplace: function ( text ) { if ( typeof text === 'string' ) { return text.replace( /[<> "&]/g, function( match, pos, originalText ) { switch( match ) { case '<': return '<'; case '>': return '>'; case '&': return '&'; case '" ': return '"' ; } }); } return '' ; }, init: function () { this ._memBtn.bind( 'click' , function ( event ) { $(".error_content").hide(); return false ; }); } }; // 初始化富文本编辑器 var editor = new EditorUtil(); editor.init(); |
点评
写法四和写法三其实都差不多,但是你们有没有看出其中的不一样呢?
1. 两种都是利用原型链给对象添加方法
写法三:
1 2 | MemberCard.prototype._timedHide MemberCard.prototype.hide |
写法四:
1 2 3 4 5 | EditorUtil.prototype = { consturctor: EditorUtil, noteBookmark: function (){}, htmlReplace: function (){} } |
细看写法四利用“对象直接量”的写法给EditorUtil对象添加方法,和写法三的区别在于写法四这样写会造成consturctor属性的改变
constructor属性:始终指向创建当前对象的构造函数
每个函数都有一个默认的属性prototype,而这个prototype的constructor默认指向这个函数。如下例所示:
1 2 3 4 5 6 7 8 9 10 11 12 | function Person(name) { this .name = name; }; Person.prototype.getName = function () { return this .name; }; var p = new Person( "ZhangSan" ); console.log(p.constructor === Person); // true console.log(Person.prototype.constructor === Person); // true // 将上两行代码合并就得到如下结果 console.log(p.constructor.prototype.constructor === Person); // true |
当时当我们重新定义函数的prototype时(注意:和上例的区别,这里不是修改而是覆盖),constructor属性的行为就有点奇怪了,如下示例:
1 2 3 4 5 6 7 8 9 10 11 12 | function Person(name) { this .name = name; }; Person.prototype = { getName: function () { return this .name; } }; var p = new Person( "ZhangSan" ); console.log(p.constructor === Person); // false console.log(Person.prototype.constructor === Person); // false console.log(p.constructor.prototype.constructor === Person); // false |
为什么呢?
原来是因为覆盖Person.prototype时,等价于进行如下代码操作:
1 2 3 4 5 | Person.prototype = new Object({ getName: function () { return this .name; } }); |
而constructor属性始终指向创建自身的构造函数,所以此时Person.prototype.constructor === Object,即是:
1 2 3 4 5 6 7 8 9 10 11 12 | function Person(name) { this .name = name; }; Person.prototype = { getName: function () { return this .name; } }; var p = new Person( "ZhangSan" ); console.log(p.constructor === Object); // true console.log(Person.prototype.constructor === Object); // true console.log(p.constructor.prototype.constructor === Object); // true |
怎么修正这种问题呢?方法也很简单,重新覆盖Person.prototype.constructor即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 | function Person(name) { this .name = name; }; Person.prototype = new Object({ getName: function () { return this .name; } }); Person.prototype.constructor = Person; var p = new Person( "ZhangSan" ); console.log(p.constructor === Person); // true console.log(Person.prototype.constructor === Person); // true console.log(p.constructor.prototype.constructor === Person); // true |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?