如何编写jQuery插件

 要说jQuery 最成功的地方,我认为是它的可扩展性吸引了众多开发者为其开发插件,从而创建起了一个生态系统。这比如大公司们争相作平台同样,得平台者得天下。苹果,微软,谷歌等巨头,都有各自的平台及生态圈。javascript

学会使用jQuery并不难,由于它简单易学,而且相信你接触jQuery后确定也使用或熟悉了很多其插件。若是要将能力上升一个台阶,编写一个属于本身的插件是个不错的选择。css

本教程可能不是最精品的,但必定是最细致的。html

jQuery插件开发模式

软件开发过程当中是须要必定的设计模式来指导开发的,有了模式,咱们就能更好地组织咱们的代码,而且从这些前人总结出来的模式中学到不少好的实践。java

根据《jQuery高级编程》的描述,jQuery插件开发方式主要有三种:jquery

  1. 经过$.extend()方法来扩展jQuery;
  2. 经过$.fn来扩展jQuery;
  3. 经过$.widget()应用jQuery UI的部件工厂方式建立;

一般咱们使用第二种方法来进行简单插件开发,说简单是相对于第三种方式。第三种方式是用来开发更高级jQuery部件的,该模式开发出来的部件带有不少jQuery内建的特性,好比插件的状态信息自动保存,各类关于插件的经常使用方法等,很是贴心,这里不细说。web

而第一种方式又太简单,仅仅是在jQuery命名空间或者理解成jQuery构造函数身上添加了一个静态方法而以。因此咱们调用经过$.extend()添加的函数时直接经过$符号调用($.myfunction())而不须要选中DOM元素($('#example').myfunction())。请看下面的例子:面试

$.extend({
    sayHello: function(name) {
        console.log('Hello,' + (name ? name : 'Dude') + '!');
    }
})
$.sayHello(); //调用
$.sayHello('Wayou'); //带参调用

运行结果:编程

上面代码中,经过$.extend()向jQuery添加了一个sayHello函数,而后经过$直接调用。到此你能够认为咱们已经完成了一个简单的jQuery插件了。设计模式

但如你所见,这种方式用来定义一些辅助方法是比较方便的。好比一个自定义的console,输出特定格式的信息,定义一次后能够经过jQuery在程序中任何须要的地方调用它。安全

$.extend({
    log: function(message) {
        var now = new Date(),
            y = now.getFullYear(),
            m = now.getMonth() + 1, //!JavaScript中月分是从0开始的
            d = now.getDate(),
            h = now.getHours(),
            min = now.getMinutes(),
            s = now.getSeconds(),
            time = y + '/' + m + '/' + d + ' ' + h + ':' + min + ':' + s;
        console.log(time + ' 信息内容: ' + message);
    }
})
$.log('initializing...'); //调用

但这种方式没法利用jQuery强大的选择器带来的便利,要处理DOM元素以及将插件更好地运用于所选择的元素身上,仍是须要使用第二种开发方式。你所见到或使用的插件也大可能是经过此种方式开发。

使用$.fn进行插件开发

下面咱们就来看第二种方式的jQuery插件开发方式。

基本方式

先看一下它的基本格式:

$.fn.pluginName = function() {
    //your code goes here
}

基本上就是往$.fn上面添加一个方法,名字是咱们的插件名称。而后咱们的插件代码在这个方法里面展开。

好比咱们将页面上全部连接颜色转成红色,则能够这样写这个插件:

$.fn.myPlugin = function() {
    //在这里面,this指的是用jQuery选中的元素
    //example :$('a'),则this=$('a')
    this.css('color', 'red');
}

在插件名字定义的这个函数内部,this指代的是咱们在调用该插件时,用jQuery选择器选中的元素,通常是一个jQuery类型的集合。好比$('a')返回的是页面上全部a标签的集合,且这个集合已是jQuery包装类型了,也就是说,在对其进行操做的时候能够直接调用jQuery的其余方法而不须要再用美圆符号来包装一下。

因此在上面插件代码中,咱们在this身上调用jQuery的css()方法,也就至关于在调用 $('a').css()。

理解this在这个地方的含义很重要。这样你才知道为何能够直接用jQuery方法同时在其余地方this指代不一样时咱们又须要用jQuery从新包装才能调用,下面会讲到。初学容易被this的值整晕,但理解了就不难。

如今就能够去页面试试咱们的代码了,在页面上放几个连接,调用插件后连接字体变成红色:

<ul>
    <li>
        <a href="http://www.webo.com/liuwayong">个人微博</a>
    </li>
    <li>
        <a href="http://http://www.cnblogs.com/Wayou/">个人博客</a>
    </li>
    <li>
        <a href="http://wayouliu.duapp.com/">个人小站</a>
    </li>
</ul>
<p>这是p标签不是a标签,我不会受影响</p>
<script src="jquery-1.11.0.min.js"></script>
<script src="jquery.myplugin.js"></script>
<script type="text/javascript">
    $(function(){
        $('a').myPlugin();
    })
</script>

运行结果:

下面进一步,在插件代码里处理每一个具体的元素,而不是对一个集合进行处理,这样咱们就能够针对每一个元素进行相应操做。

咱们已经知道this指代jQuery选择器返回的集合,那么经过调用jQuery的.each()方法就能够处理合集中的每一个元素了,但此刻要注意的是,在each方法内部,this指带的是普通的DOM元素了,若是须要调用jQuery的方法那就须要用$来从新包装一下。

好比如今咱们要在每一个连接显示连接的真实地址,首先经过each遍历全部a标签,而后获取href属性的值再加到连接文本后面。

更改后咱们的插件代码为:

$.fn.myPlugin = function() {
    //在这里面,this指的是用jQuery选中的元素
    this.css('color', 'red');
    this.each(function() {
        //对每一个元素进行操做
        $(this).append(' ' + $(this).attr('href'));
    }))
}

调用代码仍是同样的,咱们经过选中页面全部的a标签来调用这个插件

运行结果:

到此,你已经能够编写功能简单的jQuery插件了。是否是也没那么难。

下面开始jQuery插件编写中一个重要的部分,参数的接收。

支持链式调用

咱们都知道jQuery一个时常优雅的特性是支持链式调用,选择好DOM元素后能够不断地调用其余方法。

要让插件不打破这种链式调用,只需return一下便可。

$.fn.myPlugin = function() {
    //在这里面,this指的是用jQuery选中的元素
    this.css('color', 'red');
    return this.each(function() {
        //对每一个元素进行操做
        $(this).append(' ' + $(this).attr('href'));
    }))
}

让插件接收参数

一个强劲的插件是可让使用者随意定制的,这要求咱们提供在编写插件时就要考虑得全面些,尽可能提供合适的参数。

好比如今咱们不想让连接只变成红色,咱们让插件的使用者本身定义显示什么颜色,要作到这一点很方便,只须要使用者在调用的时候传入一个参数便可。同时咱们在插件的代码里面接收。另外一方面,为了灵活,使用者能够不传递参数,插件里面会给出参数的默认值。

在处理插件参数的接收上,一般使用jQuery的extend方法,上面也提到过,但那是给extend方法传递单个对象的状况下,这个对象会合并到jQuery身上,因此咱们就能够在jQuery身上调用新合并对象里包含的方法了,像上面的例子。当给extend方法传递一个以上的参数时,它会将全部参数对象合并到第一个里。同时,若是对象中有同名属性时,合并的时候后面的会覆盖前面的。

利用这一点,咱们能够在插件里定义一个保存插件参数默认值的对象,同时将接收来的参数对象合并到默认对象上,最后就实现了用户指定了值的参数使用指定的值,未指定的参数使用插件默认值。

为了演示方便,再指定一个参数fontSize,容许调用插件的时候设置字体大小。

$.fn.myPlugin = function(options) {
    var defaults = {
        'color': 'red',
        'fontSize': '12px'
    };
    var settings = $.extend(defaults, options);
    return this.css({
        'color': settings.color,
        'fontSize': settings.fontSize
    });
}

如今,咱们调用的时候指定颜色,字体大小未指定,会运用插件里的默认值12px。

$('a').myPlugin({
    'color': '#2C9929'
});

运行结果:

同时制定文字颜色与大小:

$('a').myPlugin({
    'color': '#2C9929',
    'fontSize': '20px'
});

 

保护好默认参数,不容许修改

注意到上面代码调用extend时会将defaults的值改变,这样很差,由于它做为插件因有的一些东西应该维持原样,另外就是若是你在后续代码中还要使用这些默认值的话,当你再次访问它时它已经被用户传进来的参数更改了。

一个好的作法是将一个新的空对象作为$.extend的第一个参数,defaults和用户传递的参数对象紧随其后,这样作的好处是全部值被合并到这个空对象上,保护了插件里面的默认值。

$.fn.myPlugin = function(options) {
    var defaults = {
        'color': 'red',
        'fontSize': '12px'
    };
    var settings = $.extend({},defaults, options);//将一个空对象作为第一个参数
    return this.css({
        'color': settings.color,
        'fontSize': settings.fontSize
    });
}

到此,插件能够接收和处理参数后,就能够编写出更健壮而灵活的插件了。若要编写一个复杂的插件,代码量会很大,如何组织代码就成了一个须要面临的问题,没有一个好的方式来组织这些代码,总体感受会杂乱无章,同时也很差维护,因此将插件的全部方法属性包装到一个对象上,用面向对象的思惟来进行开发,无疑会使工做轻松不少。

面向对象的插件开发

为何要有面向对象的思惟,由于若是不这样,你可能须要一个方法的时候就去定义一个function,当须要另一个方法的时候,再去随便定义一个function,一样,须要一个变量的时候,毫无规则地定义一些散落在代码各处的变量。

仍是老问题,不方便维护,也不够清晰。固然,这些问题在代码规模较小时是体现不出来的。

若是将须要的重要变量定义到对象的属性上,函数变成对象的方法,当咱们须要的时候经过对象来获取,一来方便管理,二来不会影响外部命名空间,由于全部这些变量名还有方法名都是在对象内部。

接着上面的例子,咱们能够把这个插件抽象成一个美化页面的对象,由于他的功能是设置颜色啊字体啊什么的,固然咱们还能够加入其余功能好比设置下划线啊什么的。

因此咱们新建一个对象命名为Beautifier,而后咱们在插件里使用这个对象来编码。

//定义Beautifier的构造函数
var Beautifier = function(ele, opt) {
    this.$element = ele,
    this.defaults = {
        'color': 'red',
        'fontSize': '12px',
        'textDecoration':'none'
    },
    this.options = $.extend({}, this.defaults, opt)
}
//定义Beautifier的方法
Beautifier.prototype = {
    beautify: function() {
        return this.$element.css({
            'color': this.options.color,
            'fontSize': this.options.fontSize,
            'textDecoration': this.options.textDecoration
        });
    }
}
//在插件中使用Beautifier对象
$.fn.myPlugin = function(options) {
    //建立Beautifier的实体
    var beautifier = new Beautifier(this, options);
    //调用其方法
    return beautifier.beautify();
}

经过上面这样一改造,咱们的代码变得更面向对象了,也更好维护和理解,之后要加新功能新方法,只需向对象添加新变量及方法便可,而后在插件里实例化后便可调用新添加的东西。

插件的调用仍是同样的,咱们对代码的改动并不影响插件其余地方,只是将代码的组织结构改动了而以。

$(function() {
    $('a').myPlugin({
        'color': '#2C9929',
        'fontSize': '20px'
    });
})

指定文字带下划线(咱们在Beautifier对象中新加的功能,默认不带下划线,如上面的例子)的调用:

$(function() {
    $('a').myPlugin({
        'color': '#2C9929',
        'fontSize': '20px',
        'textDecoration': 'underline'
    });
})

到这里,你能够更好地编写复杂的插件同时很好地组织代码了。当咱们回头去看上面的代码时,其实也仍是有改进空间的。也就是下面介绍的关于命名空间及变量各什么的,一些杂项。

关于命名空间

不只仅是jQuery插件的开发,咱们在写任何JS代码时都应该注意的一点是不要污染全局命名空间。由于随着你代码的增多,若是有意无心在全局范围内定义一些变量的话,最后很难维护,也容易跟别人写的代码有冲突。

好比你在代码中向全局window对象添加了一个变量status用于存放状态,同时页面中引用了另外一个别人写的库,也向全局添加了这样一个同名变量,最后的结果确定不是你想要的。因此不到万不得已,通常咱们不会将变量定义成全局的。

一个好的作法是始终用自调用匿名函数包裹你的代码,这样就能够彻底放心,安全地将它用于任何地方了,绝对没有冲突。

用自调用匿名函数包裹你的代码

咱们知道JavaScript中没法用花括号方便地建立做用域,但函数却能够造成一个做用域,域内的代码是没法被外界访问的。若是咱们将本身的代码放入一个函数中,那么就不会污染全局命名空间,同时不会和别的代码冲突。

如上面咱们定义了一个Beautifier全局变量,它会被附到全局的window对象上,为了防止这种事情发生,你或许会说,把全部代码放到jQuery的插件定义代码里面去啊,也就是放到$.fn.myPlugin里面。这样作倒也是种选择。但会让咱们实际跟插件定义有关的代码变得臃肿,而在$.fn.myPlugin里面咱们其实应该更专一于插件的调用,以及如何与jQuery互动。

因此保持原来的代码不变,咱们将全部代码用自调用匿名函数包裹。

(function() {
    //定义Beautifier的构造函数
    var Beautifier = function(ele, opt) {
        this.$element = ele,
        this.defaults = {
            'color': 'red',
            'fontSize': '12px',
            'textDecoration': 'none'
        },
        this.options = $.extend({}, this.defaults, opt)
    }
    //定义Beautifier的方法
    Beautifier.prototype = {
        beautify: function() {
            return this.$element.css({
                'color': this.options.color,
                'fontSize': this.options.fontSize,
                'textDecoration': this.options.textDecoration
            });
        }
    }
    //在插件中使用Beautifier对象
    $.fn.myPlugin = function(options) {
        //建立Beautifier的实体
        var beautifier = new Beautifier(this, options);
        //调用其方法
        return beautifier.beautify();
    }
})();

将系统变量以变量形式传递到插件内部

来看下面的代码,你猜他会出现什么结果?

var foo=function(){
    //别人的代码
}//注意这里没有用分号结尾

//开始咱们的代码。。。
(function(){
    //咱们的代码。。
    alert('Hello!');
})();

原本别人的代码也正常工做,只是最后定义的那个函数没有用分号结尾而以,而后当页面中引入咱们的插件时,报错了,咱们的代码没法正常执行。

缘由是咱们用来充当自调用匿名函数的第一对括号与上面别人定义的函数相连,由于中间没有分号嘛,总之咱们的代码没法正常解析了,因此报错。

因此好的作法是咱们在代码开头加一个分号,这在任什么时候候都是一个好的习惯。

var foo=function(){
    //别人的代码
}//注意这里没有用分号结尾

//开始咱们的代码。。。前面加一个分号是个好习惯
;(function(){
    //咱们的代码。。
    alert('Hello!');
})();

同时,将系统变量以参数形式传递到插件内部也是个不错的实践。

当咱们这样作以后,window等系统变量在插件内部就有了一个局部的引用,能够提升访问速度,会有些许性能的提高

最后咱们获得一个很是安全结构良好的代码:

;(function($,window,document,undefined){
    //咱们的代码。。
    //blah blah blah...
})(jQuery,window,document);

一个安全,结构良好,组织有序的插件编写完成。

关于变量定义与命名

如今谈谈关于变量及方法等的命名,没有硬性规定,但为了规范,遵循一些约定仍是颇有必要的。

变量定义:好的作法是把将要使用的变量名用一个var关键字一并定义在代码开头,变量名间用逗号隔开。缘由有二:

  • 一是便于理解,知道下面的代码会用到哪些变量,同时代码显得整洁且有规律,也方便管理,变量定义与逻辑代码分开;
  • 二是由于JavaScript中全部变量及函数名会自动提高,也称之为JavaScript的Hoist特性,即便你将变量的定义穿插在逻辑代码中,在代码解析运行期间,这些变量的声明仍是被提高到了当前做用域最顶端的,因此咱们将变量定义在一个做用域的开头是更符合逻辑的一种作法。固然,再次说明这只是一种约定,不是必需的。

变量及函数命名 通常使用驼峰命名法(CamelCase),即首个单词的首字母小写,后面单词首字母大写,好比resultArray,requestAnimationFrame。对于常量,全部字母采用大写,多个单词用下划线隔开,好比WIDTH=100,BRUSH_COLOR='#00ff00'。当变量是jQuery类型时,建议以$开头,开始会不习惯,但常常用了以后会感受很方便,由于能够很方便地将它与普通变量区别开来,一看到以$开头咱们就知道它是jQuery类型能够直接在其身上调用jQuery相关的方法,好比var $element=$('a'); 以后就能够在后面的代码中很方便地使用它,而且与其余变量容易区分开来。

引号的使用:既然都扯了这些与插件主题无关的了,这里再多说一句,通常HTML代码里面使用双引号,而在JavaScript中多用单引号,好比下面代码所示:

var name = 'Wayou';
document.getElementById(‘example’).innerHTML = '< a href="http: //wayouliu.duapp.com/">'+name+'</a>'; //href=".." HTML中保持双引号,JavaScript中保持单引号

 一方面,HTML代码中原本就使用的是双引号,另外一方面,在JavaScript中引号中还须要引号的时候,要求咱们单双引号间隔着写才是合法的语句,除非你使用转意符那也是能够的。再者,坚持这样的统一能够保持代码风格的一致,不会出现这里字符串用双引号包着,另外的地方就在用单引号。

 

原文:http://www.cnblogs.com/Wayou/p/jquery_plugin_tutorial.html

相关文章
相关标签/搜索