🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
根据《jQuery高级编程》的描述,jQuery插件开发方式主要有三种: 1. 通过$.extend()来扩展jQuery 2. 通过$.fn 向jQuery添加新的方法 3. 通过$.widget()应用jQuery UI的部件工厂方式创建 通常我们使用第二种方法来进行简单插件开发,说简单是相对于第三种方式。第三种方式是用来开发更高级jQuery部件的,该模式开发出来的部件带有很多jQuery内建的特性,比如插件的状态信息自动保存,各种关于插件的常用方法等,非常贴心,这里不细说。 而第一种方式又太简单,仅仅是在jQuery命名空间或者理解成jQuery身上添加了一个静态方法而以。所以我们调用通过$.extend()添加的函数时直接通过$符号调用($.myfunction())而不需要选中DOM元素($('#example').myfunction())  这种方式无法利用jQuery强大的选择器带来的便利 重载原型: ## **jQuery.extend(\[deep\], target, object1, \[objectN\])**         用一个或多个其他对象来扩展一个对象,返回被扩展的对象 意思就是合并其他对象到目标对象来扩展对象。 **参数:第一个参数为布尔类型  其他都是对象类型**    deep:       可选。如果设为true,则递归合并(不支持false)。  boolean    target:     待修改(目标)对象。    object1:   待合并到第一个对象的对象。           objectN:   可选。待合并到第一个对象的对象。 * 如果第一个参数设置为true,则jQuery返回一个深层次的副本,递归地复制找到的任何对象(取target和object属性的并集)。否则的话,object会完全的将属性名相同的属性的值替换掉。 * 未定义的属性将不会被复制,然而从对象的原型继承的属性将会被复制。 举例有第一个参数的区别: 第一个参数未设置:  ~~~ <div id="log"></div> <script> var target = { 苹果: 0, 香蕉: { 重量: 52, 价格: 100 }, 樱桃: 97 }; var object1 = { 香蕉: { 价格: 200 }, 榴莲: 100 }; // 合并object1到target $.extend( target, object1 ); $( "#log" ).append( JSON.stringify( target) ); </script> 结果: ~~~ ~~~ { “苹果”:0, “香蕉”:{ “价格”:200}, “樱桃”:97 “榴莲”:100} ~~~ 第一个参数设置为true ~~~ <div id="log"></div> <script> ~~~ ~~~ var target = { 苹果: 0, 香蕉: { 重量: 52, 价格: 100 }, 樱桃: 97 }; var object1 = { 香蕉: { 价格: 200 }, 榴莲: 100 }; // 合并object1到target ~~~ ~~~ $.extend( true, target, object1 ); // $( "#log" ).append( JSON.stringify( object1 ) ); </script> 结果: ~~~ ~~~ { “苹果”:0, “香蕉”:{ “重量”:52, “价格”:200}, “樱桃”:97 “榴莲”:100} ~~~ 合并默认值和选项,而不修改默认值。这是一个常见的插件开发模式    即target参数搞成 {} 就 不会改变target的属性 ~~~ <div id="log"></div> <script> var defaults = { validate: false, limit: 5, name: "foo" }; var options = { validate: true, name: "bar" }; // 合并 默认值 和 options,无需修改默认值 var settings = $.extend( {}, defaults, options ); $( "#log" ).append( "<div><b>defaults -- </b>" + JSON.stringify( defaults ) + "</div>" ); $( "#log" ).append( "<div><b>options -- </b>" + JSON.stringify( options ) + "</div>" ); $( "#log" ).append( "<div><b>settings -- </b>" + JSON.stringify( settings ) + "</div>" ); </script> 结果: ~~~ **defaults --** {“validate”:false,“limit”:5,“name”:“foo”} **options --** {“validate”:true,“name”:“bar”} **settings --** {“validate”:true,“limit”:5,“name”:“bar”} #### jQuery.extend( target \[, object1 \] \[, objectN \] )    target:     待修改(目标)对象。    object1:   可选。待合并到第一个对象的对象。           objectN:   可选。待合并到第一个对象的对象。 1、如果不指定target,则给jQuery命名空间本身进行扩展。这有助于插件作者为jQuery增加新方法。除了目标对象如果还传入其他对象 那么将接收新属性,如果它是唯一参数,则会扩展jQuery名称空间 当$.extend()提供了两个或多个对象参数时,所有对象的属性都将添加到目标对象target。如果参数为`null`或`undefined`将被忽略 如果$.extend()只提供一个参数,这意味着目标参数被省略。在这种情况下,jQuery对象本身被假定为目标。通过这样做,可以将新函数添加到jQuery名称空间。这对希望向JQuery添加新方法的插件作者非常有用 请记住,目标对象(第一个参数)将被修改,并且也将从中返回`$.extend()`。但是,如果要保留两个原始对象,可以通过将空对象作为目标来传递(如果想要得到合并的结果却又不想修改target目标对象的结构),那么: ~~~ var newObj=$.extend({},object1,object2,object3...)//也就是将"{}"作为target参数。 ~~~ 例子: ~~~ var result=$.extend({},{name:"Tom",age:21},{name:"Jerry",sex:"Boy"}) //那么合并后的结果 可以看到,相同的参数时,后面的将被覆盖掉。如:Jerry覆盖掉了Tom //result={name:"Jerry",age:21,sex:"Boy"} ~~~ ## jQuery.fn.extend( object ) 例子:js控制check默认选中状态 ~~~ <label><input type="checkbox" name="foo"> Foo</label> <label><input type="checkbox" name="bar"> Bar</label> <script> jQuery.fn.extend({ //扩展选中方法 check: function() { return this.each(function() {//这里的this 就是 jQuery对象。这里return 为了支持链式调用 this.checked = true; }); }, //扩展未选中方法 uncheck: function() { return this.each(function() { this.checked = false; }); } }); // 调用选中的方法 $( "input[type='checkbox']" ).check(); </script> ~~~ ** jQuery.extend和jQuery.fn.extend的区别:**  $.extend是直接把函数挂载在jQuery函数体上 这在代码中直接反映便是$.函数名不需要实例化便可访问即使实例化反而访问不到(类级别),而$.fn.extend函数 是把函数挂载在jQuery对象上 这在代码中的直接表现就是$(dom).函数名 也就是说必须实例化jQuery对象之后才能调用(对象级别) ~~~ 1、类级别 类级别你可以理解为拓展jquery类,最明显的例子是$.ajax(...),为jQuery类添加添加类方法,相当于静态方法。 开发扩展其方法时使用$.extend方法,即jQuery.extend(object);  $.extend({   add:function(a,b){return a+b;} }); //$.add(3,4); 插件扩展中一般用他来设置选项值 如: ~~~ ~~~ var defaults = { validate: false, limit: 5, name: "foo" }; var options = { validate: true, name: "bar" }; // 合并 默认值 和 options,无需修改默认属性值 var settings = $.extend( {}, defaults, options ); ~~~ ~~~ 2、对象级别 对象级别则可以理解为基于对象的拓展,如$("#table").changeColor(...); 这里这个changeColor呢,就是基于对象的拓展了。 开发扩展其方法时使用$.fn.extend方法,即jQuery.fn.extend(object);  $.fn.extend({ add:function(){return a+b;} }) //$('xxx').add(); 插件开发中一般用他来定义扩展的方法 ~~~ ~~~ $.fn.extend({ check:function(){ return this.each({ this.checked=true; }); }, uncheck:function(){ return this.each({ this.checked=false; }); } }); 页面中调用: $('input[type=checkbox]').check(); $('input[type=checkbox]').uncheck(); ~~~ 不是某个框架的插件我们一般传入window  如jquery最开始的封装: ~~~ (function( window, undefined ) { //window.jQuery = window.$ = jQuery; })(window); ~~~ 那么继承与jquery的插件则为: ~~~ (function($){ //..... })(jQuery); ~~~ 往下走  为插件新添加一个设置文本字体大小和颜色的函数: ~~~ (function($){ $.fn.extend({ customfunc:function(options){ //定义默认的值 var defaults = {color:'red', size:'16px'}; //组装参数,如果传入参数则接受并覆盖掉默认的参数,否则接受默认的参数 opts = $.extend({},defaults,options); // return $(this).each(function(){ //设置元素的颜色 $(this).css({'color':opts.color}); //设置元素的字体大小 $(this).css({'font-size':opts.size}); }); } }); })(jQuery) //用法 $('.xxx').customfunc({color:'blue',size:'30px'}); ~~~ ~~~ (function($){ $.fn.customfnc=function(options){ $.fn.customfnc.defaults={color:'red',size:'16px'} return this.each(function() { var opts = $.extend({},$.fn.customfnc.defaults,options); //设置元素的颜色 $(this).css({'color':opts.color}); //设置元素的字体大小 $(this).css({'font-size':opts.size}); } } })(jQuery); ~~~  demo3 ~~~ ;(function($, window, document,undefined) { //定义Beautifier的构造函数 这里传入$是需要jquery的强大的选择器 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(); } })(jQuery, window, document); ~~~ demo3调用     https://www.cnblogs.com/ajianbeyourself/p/5815689.html#\_label0 ~~~ <script type="text/javascript"> ;(function($,window,document,undefined) { $('a').myPlugin({ 'color': '#2C9929', 'fontSize': '20px' }); })(jQuery,window,document) </script> ~~~ 匿名函数:(function(){ }) 匿名函数自执行(自动执行) ~~~ (function () { alert(11111) var a=10; })(); //a is not defined。 alert(a);//但是里面的变量和函数我们在外部是访问不了的 ~~~ 一个插件只需要对外暴露一个接口就行即只需要一个类 其他的属性方法在这个插件类里面链式声明即可,最后我们可以将这个类以window.Object=Object的方式对外开发 ~~~ (function () { var a=10; function abc(){ alert(a); } //将abc方法作为window的方法,就可以在匿名函数自执行外面进行访问了 window.abc=abc; })(); abc(); ~~~ 要对外提供接口,我们才能找到使用的方法和属性 jquery一个特别重要的函数,就是平时用的$() jQuery()对外的接口 ~~~ //声明构造函数 var jQuery = function( selector, context ) { //在这个函数执行完了就是一个new构造函数的过程,返回的就是一个jQuery对象~~既然返回的是对象,当然可以调用方法喽~~ ~~~ ~~~ return new jQuery.fn.init( selector, context, rootjQuery ); }, ~~~ 但是现在这个jQuery还是以局部变量的形式存在,要提供对外的接口,才能使用 所以在jquery最后一行有如下代码 ~~~ window.jQuery = window.$ = jQuery; ~~~ 给jQuery对象添加一些方法和属性  prototype(原型)是面向对象 他主要用于解决这个类的对象属性和方法不能相互共享的问题 ~~~ jQuery.fn = jQuery.prototype ~~~  extend:是jQuery当中的一个继承方法,希望后续添加的方法都能挂在jQuery对象上,很方便扩展 ~~~ //通过使用对象调用的方法,是实例方法。 $().text(); $().html(); //$是一个函数,在函数下面来扩展方法的话,就是扩展一些静态方法 //在jQuery当中,给面向对象扩展静态属性和静态方法叫做扩展工具方法 //工具方法和实例方法区别就在于,它既可以给jQuery对象来用,也可以给源生的JS来用,实例方法只能给jQuery对象调用 $.trim(); $.proxy(); ~~~  总览: ~~~ (function( window, undefined ) { var document = window.document; var userAgent = navigator.userAgent; var toString = Object.prototype.toString; var push = Array.prototype.push; //声明jQuery类的构造函数 并且实例化对象 var jQuery =function(selector){ return new jQuery.fn.init(selector); }; //prototype属性的作用解决构造函数的对象实例之间无法共享属性的缺点 jQuery.fn = jQuery.prototype = { init: function( selector, context ) { var match, elem, ret, doc; // Handle $(""), $(null), or $(undefined)时直接返回该对象 if ( !selector ) { return this; } // Handle $(DOMElement) if ( selector.nodeType ) { this.context = this[0] = selector; this.length = 1; return this; } // The body element only exists once, optimize finding it if ( selector === "body" && !context ) { this.context = document; this[0] = document.body; this.selector = "body"; this.length = 1; return this; } // Handle HTML strings if ( typeof selector === "string" ) { // Are we dealing with HTML string or an ID? match = quickExpr.exec( selector ); // Verify a match, and that no context was specified for #id if ( match && (match[1] || !context) ) { // HANDLE: $(html) -> $(array) if ( match[1] ) { doc = (context ? context.ownerDocument || context : document); // If a single string is passed in and it's a single tag // just do a createElement and skip the rest ret = rsingleTag.exec( selector ); if ( ret ) { if ( jQuery.isPlainObject( context ) ) { selector = [ document.createElement( ret[1] ) ]; jQuery.fn.attr.call( selector, context, true ); } else { selector = [ doc.createElement( ret[1] ) ]; } } else { ret = buildFragment( [ match[1] ], [ doc ] ); selector = (ret.cacheable ? ret.fragment.cloneNode(true) : ret.fragment).childNodes; } return jQuery.merge( this, selector ); // HANDLE: $("#id") } else { elem = document.getElementById( match[2] ); if ( elem ) { // Handle the case where IE and Opera return items // by name instead of ID if ( elem.id !== match[2] ) { return rootjQuery.find( selector ); } // Otherwise, we inject the element directly into the jQuery object this.length = 1; this[0] = elem; } this.context = document; this.selector = selector; return this; } // HANDLE: $("TAG") } else if ( !context && /^\w+$/.test( selector ) ) { this.selector = selector; this.context = document; selector = document.getElementsByTagName( selector ); return jQuery.merge( this, selector ); // HANDLE: $(expr, $(...)) } else if ( !context || context.jquery ) { return (context || rootjQuery).find( selector ); // HANDLE: $(expr, context) // (which is just equivalent to: $(context).find(expr) } else { return jQuery( context ).find( selector ); } // HANDLE: $(function) // Shortcut for document ready } else if ( jQuery.isFunction( selector ) ) { return rootjQuery.ready( selector ); } if (selector.selector !== undefined) { this.selector = selector.selector; this.context = selector.context; } return jQuery.makeArray( selector, this ); }, // Start with an empty selector selector: "", // The current version of jQuery being used jquery: "1.4.2", // The default length of a jQuery object is 0 length: 0, // The number of elements contained in the matched element set size: function() { return this.length; }, toArray: function() { return slice.call( this, 0 ); }, // Get the Nth element in the matched element set OR // Get the whole matched element set as a clean array get: function( num ) { return num == null ? // Return a 'clean' array this.toArray() : // Return just the object ( num < 0 ? this.slice(num)[ 0 ] : this[ num ] ); }, // Take an array of elements and push it onto the stack // (returning the new matched element set) pushStack: function( elems, name, selector ) { // Build a new jQuery matched element set var ret = jQuery(); if ( jQuery.isArray( elems ) ) { push.apply( ret, elems ); } else { jQuery.merge( ret, elems ); } // Add the old object onto the stack (as a reference) ret.prevObject = this; ret.context = this.context; if ( name === "find" ) { ret.selector = this.selector + (this.selector ? " " : "") + selector; } else if ( name ) { ret.selector = this.selector + "." + name + "(" + selector + ")"; } // Return the newly-formed element set return ret; }, // Execute a callback for every element in the matched set. // (You can seed the arguments with an array of args, but this is // only used internally.) each: function( callback, args ) { return jQuery.each( this, callback, args ); }, ready: function( fn ) { // Attach the listeners jQuery.bindReady(); // If the DOM is already ready if ( jQuery.isReady ) { // Execute the function immediately fn.call( document, jQuery ); // Otherwise, remember the function for later } else if ( readyList ) { // Add the function to the wait list readyList.push( fn ); } return this; }, eq: function( i ) { return i === -1 ? this.slice( i ) : this.slice( i, +i + 1 ); }, first: function() { return this.eq( 0 ); }, last: function() { return this.eq( -1 ); }, slice: function() { return this.pushStack( slice.apply( this, arguments ), "slice", slice.call(arguments).join(",") ); }, map: function( callback ) { return this.pushStack( jQuery.map(this, function( elem, i ) { return callback.call( elem, i, elem ); })); }, end: function() { return this.prevObject || jQuery(null); }, // 仅供内部使用 push: push, }; /*很重要的一步 jQuery.fn.init是一个对象*/ //Object.prototype.name=value; 为Object对象添加name属性 jQuery.fn.init.prototype = jQuery.fn; console.log(jQuery('aa')); jQuery.extend = jQuery.fn.extend = function() { var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options, name, src, copy; // Handle a deep copy situation if ( typeof target === "boolean" ) { deep = target; target = arguments[1] || {}; // skip the boolean and the target i = 2; } // Handle case when target is a string or something (possible in deep copy) if ( typeof target !== "object" && !jQuery.isFunction(target) ) { target = {}; } // extend jQuery itself if only one argument is passed if ( length === i ) { target = this; --i; } for ( ; i < length; i++ ) { // Only deal with non-null/undefined values if ( (options = arguments[ i ]) != null ) { // Extend the base object for ( name in options ) { src = target[ name ]; copy = options[ name ]; // Prevent never-ending loop if ( target === copy ) { continue; } // Recurse if we're merging object literal values or arrays if ( deep && copy && ( jQuery.isPlainObject(copy) || jQuery.isArray(copy) ) ) { var clone = src && ( jQuery.isPlainObject(src) || jQuery.isArray(src) ) ? src : jQuery.isArray(copy) ? [] : {}; // Never move original objects, clone them target[ name ] = jQuery.extend( deep, clone, copy ); // Don't bring in undefined values } else if ( copy !== undefined ) { target[ name ] = copy; } } } } // Return the modified object return target; }; jQuery.fn.extend({ data: function( key, value ) {}, attr: function( name, value ) {}, removeAttr: function( name, fn ) {} }); jQuery.extend({ each: function( object, callback, args ) {}, isReady: false, uaMatch: function( ua ) {console.log(ua)} }); browserMatch = jQuery.uaMatch( userAgent ); window.jQuery = window.$ = jQuery; })(window); ~~~