本文转自:http://www.xuanfengge.com/jquery-plug-in-written-summary-and-summary-of-writing-object-oriented-manner.html

前言

最近在折腾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 '&lt;';
                    case '>':
                        return '&gt;';
                    case '&':
                        return '&amp;';
                    case '"':
                        return '&quot;';
                }
            });
        }
        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插件写法总结以及面向对象方式写法的更多相关文章

  1. jQuery插件写法总结以及面向对象方式写法总结

    前两个是jQuery插件,后面2个是以对象的形式开发,都类似. 写法一 (function($, window){ // 初始态定义 var _oDialogCollections = {}; // ...

  2. jQuery插件实践之轮播练习(一)

    所有文章搬运自我的个人主页:sheilasun.me 因为从来没写过jQuery插件,所以本文要通过一个轮播的例子,练习jQuery插件的写法. 新建插件文件 在讨论细节之前,先新建插件文件(当然也可 ...

  3. 第7章 jQuery插件的使用和写法

    第7章 jQuery插件的使用和写法 插件又称扩展,是一种遵循一定规范的应用程序接口写出来的程序. 插件的编写思想基于面向对象. 获取最新的插件可以查看jquery官网:http://plugins. ...

  4. jQuery 插件写法2

    转载:http://www.xuanfengge.com/jquery-plug-in-written-summary-and-summary-of-writing-object-oriented-m ...

  5. jquery插件的写法

    jquery插件及zepto插件,写法上有些区别. 区别点: 1.自定义事件的命名空间 jq的时间命名空间是用点“.”,而zepto是用冒号“:” 如 //jquery $(this).trigger ...

  6. jQuery 插件写法

    一.jQuery插件的类型 1. jQuery方法 很大一部分的jQuery插件都是这种类型,由于此类插件是将对象方法封装起来,在jQuery选择器获取jQuery对象过程中进行操作,从而发挥jQue ...

  7. JQuery插件的写法和规范

    首先,在具体说明编写插件之前,我们先假定一个使用场景:有一个HTML页面(或.aspx页面),页面上放置了一个5行3列的表格,即:<table></table>标记,具体代码如 ...

  8. JQuery插件使用之Validation 快速完成表单验证的几种方式

    JQuery的Validation插件可以到http://plugins.jquery.com/上去下载.今天来分享一下,关于这个插件的使用. 简易使用 这第一种方式可谓是傻瓜式的使用,我们只需要按照 ...

  9. JQuery插件的写法 (转:太棒啦!)

    JQuery插件写法的总结 最近Web应用程序中越来越多地用到了JQuery等Web前端技术.这些技术框架有效地改善了用户的操作体验,同时也提高了开发人员构造丰富客户 端UI的效率.JQuery本身提 ...

随机推荐

  1. leetcode 6 ZigZag Converesion

    class Solution { public: string convert(string s, int nRows) { if (nRows <= 1) return s; string r ...

  2. jmeter beanShell 修改http请求参数

    转自http://www.tuicool.com/articles/rEri63   http://powertech.iteye.com/blog/2174521 主题 HTTPJMeter 在使用 ...

  3. JavaScript混淆压缩

    比较好用的压缩软件,支持合并 JsCompressor-v3.0 比较好用的混淆站点:http://dean.edwards.name/packer/

  4. sqlserver高版本还原到低版本方法

    想要将Sqlserver2014高版本备份的数据还原到低版本SqlServer2008R2上去,但是这在SqlServer中是没法直接还原数据库的,通过以下方法可以顺利还原. 通过高版本生成sql脚本 ...

  5. Glib之GObject简介(翻译)

    GObject GObject库是Glib库的动态类型系统实现,它实现了: 基于引用计数的内存管理 实例的构造和析构 通用的set/get的属性获取方法 简单易用的信号机制 对象实例化 所述g_obj ...

  6. Shell-2-命令之乐

    1.cat (1)基本用法 [root@cai tmp]# cat 1.txt 2.txt this is a test1 this is a test 2 (2)cat -s file(删除额外空白 ...

  7. log4j配置文件及java调用 每个级别输出到不同的文件

    #配置根Logger log4j.rootLogger = DEBUG , RollingFile,CONSOLE #文件大小达到一定尺寸的时候创建一个新的文件 log4j.appender.Roll ...

  8. 洛谷P3507 [POI2010]GRA-The Minima Game

    题目描述 Alice and Bob learned the minima game, which they like very much, recently. The rules of the ga ...

  9. nginx 搭建 rtmp 服务器

    前言 最近接手了一个跟视频监控相关的项目,用了近年来越来越流行的 Web 服务器 nginx 加上 nginx-rtmp-module 搭建 rtmp 服务器.使用了阿里云的服务器,系统 Ubuntu ...

  10. python 批量下载 spring 的 xsd

    #coding=utf-8 import os import urllib import urllib2 import re from bs4 import BeautifulSoup # 利用 ur ...