jQuery插件制作的实例教程


Posted in Javascript onMay 16, 2016

一、jQuery插件的类型

1. jQuery方法

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

2. 全局函数法

可以把自定义的功能函数独立附加到jQuery命名空间下,从而作为jQuery作用域下的一个公共函数使用。
但全局函数没有被绑定到jQuery对象上,故不能在选择器获取的jQuery对象上调用。需要通过jQuery.fn()或$.fn()方式进行引用。

3. 选择器法

如果觉得jQuery提供的选择器不够用或不方便的话,可以考虑自定义选择器。

二、jQuery插件的机制

1.   jQuery.extend()方法

这种方法能够创建全局函数或选择器。

所谓全局函数,就是jQuery对象的方法,实际上就是位于jQuery命名空间内部的函数,有人把这类函数称为实用工具函数,这些函数都有一个共同特征,就是不直接操作DOM元素,而是操作Javascript的非元素对象,或者执行其他非对象的特定操作,如jQuery的each()函数和noConflict()函数。

例如,在jQuery命名空间上创建两个公共函数:

jQuery.extend({ 
min : function(a,b){ 
return a<b?a:b; 
}, 
max : function(a,b){ 
return a<b?b:a; 
} 
}) 
$(function(){ 
$("input").click(function(){ 
var a = prompt("请输入一个数:"); 
var b = prompt("再输入一个数:"); 
var c = jQuery.min(a,b); 
var d = jQuery.max(a,b); 
alert("最大值是:" + d + "\n最小值是:" + c); 
}); 
})
<input type="button" value="jQuery扩展测试" />
jQuery.extend({ 
min : function(a,b){ 
return a<b?a:b; 
}, 
max : function(a,b){ 
return a<b?b:a; 
} 
}) 
$(function(){ 
$("input").click(function(){ 
var a = prompt("请输入一个数:"); 
var b = prompt("再输入一个数:"); 
var c = jQuery.min(a,b); 
var d = jQuery.max(a,b); 
alert("最大值是:" + d + "\n最小值是:" + c); 
}); 
})
<input type="button" value="jQuery扩展测试" />

jQuery.extend()方法除了可以创建插件外,还可以用来扩展jQuery对象。

例如,调用jQuery.extend()方法把对象a和对象b合并为一个新的对象,并返回合并对象将其赋值给变量c:

var a = {name : "aaa",pass : 777}; 
var b = {name : "bbb",pass : 888,age : 9}; 
var c = jQuery.extend(a,b); 
$(function(){ 
for(var name in c){ 
$("div").html($("div").html() + "<br />"+ name + ":" + c[name]); 
} 
})

如果要向jQuery命名空间上添加一个函数,只需要将这个新函数制定为jQuery对象的一个属性即可。其中jQuery对象名也可以简写为$,jQuery.smalluv==$.smalluv。

例如,创建jQuery全局函数:

jQuery.smalluv = { 
min : function(a,b){ 
return a<b?a:b; 
}, 
max : function(a,b){ 
return a<b?b:a; 
} 
} 
$(function(){ 
$("input").click(function(){ 
var a = prompt("请输入一个数:"); 
var b = prompt("再输入一个数:"); 
var c = jQuery.smalluv.min(a,b); 
var d = jQuery.smalluv.max(a,b); 
alert("最大值是:" + d + "\n最小值是:" + c); 
}); 
})

2. jQuery.fn.extend()方法

这种方法能够创建jQuery对象方法。

举一个最简单的jQuery对象方法例子:

jQuery.fn.test = function(){ 
alert("jQuery对象方法"); 
} 
$(function(){ 
$("div").click(function(){ 
$(this).test(); 
}); 
})


三、初步总结

在jQuery匿名函数中,采用jQuery.extend();方法创建jQuery插件
在jQuery匿名函数中,采用对象.属性=函数的方式创建jQuery插件

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>最简单的jquery插件</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <script type="text/javascript" src="../res/jquery/jquery-1.4.4.min.js"></script>
    <script type="text/javascript">
      (function($) {
        jQuery.extend({//写法1
          a: function(h){
            $("#ad").html(h);
          },
          b:function(h){
            alert(h);
          }
        })
      })(jQuery);
 
      (function($) {//写法2
        jQuery.a=function(h){
          $("#ad").html(h);
        }
        jQuery.b=function(h){
          alert(h);
        }
      })(jQuery);
 
      $(document).ready(function(){
        $.a("abc");
        $.b("xyz");
      });
 
    </script>
 
  </head>
  <body>
    <h3>最简单的jQuery插件</h3>
    <div id="ad"></div>
  </body>
</html>

四、编写实例

写法一

插件主体:

(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);

调用

var MNDialog = $("#header").MNDialog({
  sId : "#footer",    //覆盖默认值
  fCloseCallback : dialog,//回调函数
  oContentHtml : function(_aoCallback){
      _aoCallback(_oEditGrpDlgView.el);
    }
  }
});
// 调用提供的函数
MNDialog.close;
function dialog(){
 
}

点评

1. 自调用匿名函数

(function($, window) {
 // jquery code
})(jQuery, window);

用处:通过定义一个匿名函数,创建了一个“私有”的命名空间,该命名空间的变量和方法,不会破坏全局的命名空间。这点非常有用也是一个JS框架必须支持的功能,jQuery被应用在成千上万的JavaScript程序中,必须确保jQuery创建的变量不能和导入他的程序所使用的变量发生冲突。

2. 匿名函数为什么要传入window

通过传入window变量,使得window由全局变量变为局部变量,当在jQuery代码块中访问window时,不需要将作用域链回退到顶层作用域,这样可以更快的访问window;这还不是关键所在,更重要的是,将window作为参数传入,可以在压缩代码时进行优化,看看jquery.min.js:

(function(a,b){})(jQuery, window); // jQuery被优化为a, window 被优化为 b

3. 全局变量this定义

var _oSelf = this,
$this = $(this);

使得在插件的函数内可以使用指向插件的this

4. 插件配置

this.oConfig = $.extend(defaults, _aoConfig);

设置默认参数,同时也可以再插件定义时传入参数覆盖默认值

5. 初始化函数

一般的插件会有init初始化函数并在插件的尾部初始化

6. 私有函数、公有函数

私有函数:插件内使用,函数名使用”_”作为前缀标识

共有函数:可在插件外使用,函数名使用”this.”作为前缀标识,作为插件的一个方法供外部使用

7. return this

最后返回jQuery对象,便于jQuery的链式操作

写法二

主体结构

(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,后面的均为参数

语法:

call([thisObj[,arg1[, arg2[, [,.argN]]]]])
定义:调用一个对象的一个方法,以另一个对象替换当前对象。

说明:call 方法可以用来代替另一个对象调用一个方法。call 方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。如果没有提供 thisObj 参数,那么 Global 对象被用作 thisObj。

4. 关于”this”

在插件的方法中,可能有用到指向插件的this、和指向事件触发的this,所以事件触发的this用event来获取:event.cuerrntTarget

event.currentTarget:指向事件所绑定的元素,按照事件冒泡的方式,向上找到元素
event.target:始终指向事件发生时的元素
如:

html代码

<div id="wrapper"> 
  <a href="#" id="inner">click here!</a> 
</div>

 js代码

$('#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); 
});

结果输出

#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>​

写法三(原生写法)

主体结构

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) {}

使用

var oColleagueCard = new MemberCard({ nHideTime: 200 });
oColleagueCard.hide();

点评:

1. 关于属性覆盖(对象深拷贝)

原生函数实现方法

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:

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的实现方法:

jQuery.extend([deep], target, object1, [objectN]);

用一个或多个其他对象来扩展一个对象,返回被扩展的对象。

如果不指定target,则给jQuery命名空间本身进行扩展。这有助于插件作者为jQuery增加新方法。 如果第一个参数设置为true,则jQuery返回一个深层次的副本,递归地复制找到的任何对象。否则的话,副本会与原对象共享结构。 未定义的属性将不会被复制,然而从对象的原型继承的属性将会被复制。

demo:

var options = {id: "nav", class: "header"}
var config = $.extend({id: "navi"}, options); //config={id: "nav", class: "header"}

 2. 关于this

这个对象的所有方法的this都指向这个对象,所以就不需要重新指定

写法四

主体结构

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();

总结

写法四和写法三其实都差不多,但是你们有没有看出其中的不一样呢?

两种都是利用原型链给对象添加方法:

写法三:

MemberCard.prototype._timedHide
MemberCard.prototype.hide

写法四:

EditorUtil.prototype = {
  consturctor: EditorUtil,
  noteBookmark: function(){},  
  htmlReplace: function(){}
}

细看写法四利用“对象直接量”的写法给EditorUtil对象添加方法,和写法三的区别在于写法四这样写会造成consturctor属性的改变

constructor属性:始终指向创建当前对象的构造函数

每个函数都有一个默认的属性prototype,而这个prototype的constructor默认指向这个函数。如下例所示:

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

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属性的行为就有点奇怪了,如下示例:

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时,等价于进行如下代码操作:

Person.prototype = new Object({ 
  getName: function() { 
    return this.name; 
  } 
});

Person.prototype = new Object({ 
  getName: function() { 
    return this.name; 
  } 
});

而constructor属性始终指向创建自身的构造函数,所以此时Person.prototype.constructor === Object,即是:

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

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即可:

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

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

Javascript 相关文章推荐
javascript编程起步(第七课)
Feb 27 Javascript
二叉树的非递归后序遍历算法实例详解
Feb 07 Javascript
Javascript的严格模式strict mode详细介绍
Jun 06 Javascript
JavaScript及jquey实现多个数组的合并操作
Sep 06 Javascript
关于JS中二维数组的声明方法
Sep 24 Javascript
详解微信小程序 页面跳转 传递参数
Dec 08 Javascript
vue单页应用中如何使用jquery的方法示例
Jul 27 jQuery
JavaScript 中Date对象的格式化代码方法汇总
Sep 06 Javascript
原生js实现仿window10系统日历效果的实例
Oct 31 Javascript
webuploader实现上传图片到服务器功能
Aug 16 Javascript
Vue 3.0双向绑定原理的实现方法
Oct 23 Javascript
JavaScript将数组转换为链表的方法
Feb 16 Javascript
总结jQuery插件开发中的一些要点
May 16 #Javascript
JavaScript编写Chrome扩展实现与浏览器的交互及时间通知
May 16 #Javascript
动态的9*9乘法表效果的实现代码
May 16 #Javascript
Svg.js实例教程及使用手册详解(一)
May 16 #Javascript
限制只能输入数字的实现代码
May 16 #Javascript
JavaScript开发Chrome浏览器扩展程序UI的教程
May 16 #Javascript
基于javascript实现最简单的选项卡切换效果
May 16 #Javascript
You might like
PHP的数组中提高元素查找与元素去重的效率的技巧解析
2016/03/03 PHP
完美解决php 导出excle的.csv格式的数据时乱码问题
2017/02/18 PHP
ThinkPHP框架表单验证操作方法
2017/07/19 PHP
PHP高并发和大流量解决方案整理
2019/12/24 PHP
IE FF OPERA都可用的弹出层实现代码
2009/09/29 Javascript
JS注释所产生的bug 即使注释也会执行
2013/11/19 Javascript
使用js判断TextBox控件值改变然后出发事件
2014/03/07 Javascript
jquery获取radio值(单选组radio)
2014/10/16 Javascript
jQuery animate和CSS3相结合实现缓动追逐效果附源码下载
2016/04/18 Javascript
jQuery解决input元素的blur事件和其他非表单元素的click事件冲突问题
2016/08/15 Javascript
jQuery表单插件ajaxForm实例详解
2017/01/17 Javascript
jQuery validata插件实现方法
2017/06/25 jQuery
Vue-Router进阶之滚动行为详解
2017/09/13 Javascript
Bootstrap-table自定义可编辑每页显示记录数
2018/09/07 Javascript
Vue指令v-for遍历输出JavaScript数组及json对象的常见方式小结
2019/02/11 Javascript
vue模式history下在iis中配置流程
2019/04/17 Javascript
JavaScript如何把两个数组对象合并过程解析
2019/10/10 Javascript
vue项目中常见问题及解决方案(推荐)
2019/10/21 Javascript
解决vue项目刷新后,导航菜单高亮显示的位置不对问题
2019/11/01 Javascript
vue cli4.0项目引入typescript的方法
2020/07/17 Javascript
VSCode 添加自定义注释的方法(附带红色警戒经典注释风格)
2020/08/27 Javascript
Python实时获取cmd的输出
2015/12/13 Python
pycham查看程序执行的时间方法
2018/11/29 Python
python绘制已知点的坐标的直线实例
2019/07/04 Python
妙用itchat! python实现久坐提醒功能
2019/11/25 Python
python绘制彩虹图
2019/12/16 Python
pytorch 准备、训练和测试自己的图片数据的方法
2020/01/10 Python
VSCODE配置Markdown及Markdown基础语法详解
2021/01/19 Python
法国床上用品商店:La Compagnie du lit
2019/12/26 全球购物
办公室驾驶员岗位职责
2013/11/15 职场文书
家长寄语大全
2014/04/02 职场文书
乡镇务虚会发言材料
2014/10/20 职场文书
裁员通知
2015/04/25 职场文书
推销搭讪开场白
2015/05/28 职场文书
go xorm框架的使用
2021/05/22 Golang
Python激活Anaconda环境变量的详细步骤
2021/06/08 Python