[转]jQuery插件写法总结以及面向对象方式写法
前言
最近在折腾jQuery插件,写成插件的目的就是为了实现功能与项目相分离,使得这个代码在下一个项目中能直接引用不出错。这使得我们在写插件的时候,就得考虑清楚,怎么写才能使得插件能够通用、灵活度高、可配置、兼容性好、易用性高、耦合度低等。
接下来就对以下几种写法进行分析,前两个是jQuery插件,后面2个是以对象的形式开发,都类似。而且写法也很多,我们要懂得这样写的利弊。另一篇基础文章: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
|
一个最近写的拖拽排序的demo(惯例F12查看代码吧):http://xuanfengge.com/demo/201401/dragSort/
[转]jQuery插件写法总结以及面向对象方式写法的更多相关文章
- jQuery插件写法总结以及面向对象方式写法总结
前两个是jQuery插件,后面2个是以对象的形式开发,都类似. 写法一 (function($, window){ // 初始态定义 var _oDialogCollections = {}; // ...
- jQuery插件实践之轮播练习(一)
所有文章搬运自我的个人主页:sheilasun.me 因为从来没写过jQuery插件,所以本文要通过一个轮播的例子,练习jQuery插件的写法. 新建插件文件 在讨论细节之前,先新建插件文件(当然也可 ...
- 第7章 jQuery插件的使用和写法
第7章 jQuery插件的使用和写法 插件又称扩展,是一种遵循一定规范的应用程序接口写出来的程序. 插件的编写思想基于面向对象. 获取最新的插件可以查看jquery官网:http://plugins. ...
- jQuery 插件写法2
转载:http://www.xuanfengge.com/jquery-plug-in-written-summary-and-summary-of-writing-object-oriented-m ...
- jquery插件的写法
jquery插件及zepto插件,写法上有些区别. 区别点: 1.自定义事件的命名空间 jq的时间命名空间是用点“.”,而zepto是用冒号“:” 如 //jquery $(this).trigger ...
- jQuery 插件写法
一.jQuery插件的类型 1. jQuery方法 很大一部分的jQuery插件都是这种类型,由于此类插件是将对象方法封装起来,在jQuery选择器获取jQuery对象过程中进行操作,从而发挥jQue ...
- JQuery插件的写法和规范
首先,在具体说明编写插件之前,我们先假定一个使用场景:有一个HTML页面(或.aspx页面),页面上放置了一个5行3列的表格,即:<table></table>标记,具体代码如 ...
- JQuery插件使用之Validation 快速完成表单验证的几种方式
JQuery的Validation插件可以到http://plugins.jquery.com/上去下载.今天来分享一下,关于这个插件的使用. 简易使用 这第一种方式可谓是傻瓜式的使用,我们只需要按照 ...
- JQuery插件的写法 (转:太棒啦!)
JQuery插件写法的总结 最近Web应用程序中越来越多地用到了JQuery等Web前端技术.这些技术框架有效地改善了用户的操作体验,同时也提高了开发人员构造丰富客户 端UI的效率.JQuery本身提 ...
随机推荐
- 类的继承与super()的意义以即如何写一个正确的异常类
这些东西都是我看了许多名师课程和自己研究的成果,严禁转载,这里指出了如何正确的自己定义一个异常类并看一看sun写的java的源代码话题一:子类的构造器执行是否一定会伴随着父类的构造执行? 1.this ...
- iOS开发进制转换
1.十进制转换为二进制 /** 十进制转换为二进制 @param decimal 十进制数 @return 二进制数 */ + (NSString *)getBinaryByDecimal:(NSIn ...
- Go:创建新进程(os.StartProcess源码解读)
关于如何使用go语言实现新进程的创建和进程间通信,我在网上找了不少的资料,但是始终未能发现让自己满意的答案,因此我打算自己来分析这部分源代码,然后善加利用,并且分享给大家,期望大家能从中获得启发. 首 ...
- B - Factors of Factorial
Problem Statement You are given an integer N. Find the number of the positive divisors of N!, modulo ...
- 给label添加点击事件
后台代码: lb1.Attributes.Add("onclick", "getSN('" + lb1.Text.Trim() + "')" ...
- [51nod1220] 约数之和(杜教筛+莫比乌斯反演)
题面 传送门 题解 嗯--还是懒得写了--这里 //minamoto #include<bits/stdc++.h> #define R register #define IT map&l ...
- 模板 可并堆【洛谷P3377】 【模板】左偏树(可并堆)
P3377 [模板]左偏树(可并堆) 如题,一开始有N个小根堆,每个堆包含且仅包含一个数.接下来需要支持两种操作: 操作1: 1 x y 将第x个数和第y个数所在的小根堆合并(若第x或第y个数已经被删 ...
- [HAOI2006]受欢迎的牛 tarjan缩点 BZOJ1051
题目背景 本题测试数据已修复. 题目描述 每头奶牛都梦想成为牛棚里的明星.被所有奶牛喜欢的奶牛就是一头明星奶牛.所有奶 牛都是自恋狂,每头奶牛总是喜欢自己的.奶牛之间的“喜欢”是可以传递的——如果A喜 ...
- kuangbin专题十二 HDU1176 免费馅饼 (dp)
免费馅饼 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submis ...
- nginx的使用(启动、重启、关闭)
1. 首先利用配置文件启动nginx. 命令: nginx -c /usr/local/nginx/conf/nginx.conf 重启服务: service nginx restart 2. 快速停 ...