深入学习jquery源码之插件机制
jQuery最着名的一个方面是其广泛的插件生态系统。从表格排序到表单验证再到自动完成 - 如果需要它,很可能有人为它编写了一个插件。
jQuery插件的质量差异很大。许多插件都经过了广泛的测试和维护良好,但其他插件被匆忙创建然后被忽略。不止一些人未能遵循最佳做法。一些插件,主要是jQuery UI,由jQuery团队维护。这些插件的质量与jQuery本身一样好。
扩展 jQuery 元素集来提供新的方法(通常用来制作插件)。
有时您希望在整个代码中提供一些功能。 例如,您可能想要一个可以在jQuery选择上调用的方法,该选择对选择执行一系列操作。 在这种情况下,您可以通过编写插件实现。
jQuery如何工作:jQuery对象方法
在我们编写自己的插件之前,我们必须先了解一下jQuery的工作原理。 看看这段代码:
$( "a" ).css( "color", "red" );
这是一些非常基本的jQuery代码,但你知道幕后发生了什么吗? 每当使用$函数选择元素时,它都会返回一个jQuery对象。 此对象包含您一直使用的所有方法(.css(),. click()等)以及适合您的选择器的所有元素。 jQuery对象从$ .fn对象获取这些方法。 该对象包含所有jQuery对象方法,如果我们想编写自己的方法,则还需要包含这些方法。
基本插件创作
假设我们想要创建一个插件,使一组检索到的元素中的文本变为绿色。 我们所要做的就是在$ .fn中添加一个名为greenify的函数,它就像任何其他jQuery对象方法一样可用。
$.fn.greenify = function() {
this.css( "color", "green" );
};
$( "a" ).greenify(); // Makes all the links green.
请注意,使用this.css(),而不是$(this)。 这是因为我们的greenify函数是与.css()相同的对象的一部分。
链接
我们需要做的一些事情才能使我们的插件在环境中生存。 当您将五个或六个动作链接到一个选择器时,jQuery的一个功能就是链接。 这是通过让所有jQuery对象方法再次返回原始jQuery对象来实现的(有一些例外:.width()在没有参数的情况下调用返回所选元素的宽度,并且不可链接)。 使我们的插件方法链接需要一行代码:
$.fn.greenify = function() {
this.css( "color", "green" );
return this;
}
$( "a" ).greenify().addClass( "greenified" );
$别名保护并添加范围
$变量在JavaScript库中非常流行,如果你在jQuery中使用另一个库,则必须使jQuery不使用$ with jQuery.noConflict()。 但是,这会破坏我们的插件,因为它是在假设$是jQuery函数的别名的情况下编写的。 为了与其他插件一起使用,并且仍然使用jQuery $别名,我们需要将所有代码放在里面并立即调用函数表达式中,然后传递函数jQuery,并将参数命名为$:
(function ( $ ) {
$.fn.greenify = function() {
this.css( "color", "green" );
return this;
};
}( jQuery ));
此外,立即调用函数的主要目的是允许我们拥有自己的私有变量。 假装我们想要一个不同的颜色绿色,我们想将它存储在一个变量中。
(function ( $ ) {
var shade = "#556b2f";
$.fn.greenify = function() {
this.css( "color", shade );
return this;
};
}( jQuery ));
最大限度地减少插件占用空间
编写插件只占用$ .fn中的一个插槽是一种很好的做法。 这样可以降低插件被覆盖的可能性,以及插件覆盖其他插件的可能性。
(function( $ ) {
$.fn.openPopup = function() {
// Open popup code.
};
$.fn.closePopup = function() {
// Close popup code.
};
}( jQuery ));
提炼一个公共方法会更好,并使用参数来控制执行不同的操作。
(function( $ ) {
$.fn.popup = function( action ) {
if ( action === "open") {
// Open popup code.
}
if ( action === "close" ) {
// Close popup code.
}
};
}( jQuery ));
使用each()方法
典型的jQuery对象将包含对任意数量的DOM元素的引用,这就是jQuery对象通常被称为集合的原因。 如果要对特定元素进行任何操作(例如,获取数据属性,计算特定位置),则需要使用.each()循环遍历元素。
$.fn.myNewPlugin = function() {
return this.each(function() {
// Do something to each element here.
});
};
请注意,我们返回.each()的结果而不是返回它。 因为.each()已经是可链接的,所以它返回它,然后我们再返回。 这是保持可链接性的一种比我们迄今为止所做的更好的方法。
接受选项
随着您的插件变得越来越复杂,最好通过接受选项来使您的插件可自定义。 最简单的方法是使用对象文字,特别是如果有很多选项的话。 让我们更改我们的greenify插件以接受一些选项。
(function ( $ ) {
$.fn.greenify = function( options ) {
// This is the easiest way to have default options.
var settings = $.extend({
// These are the defaults.
color: "#556b2f",
backgroundColor: "white"
}, options );
// Greenify the collection based on the settings variable.
return this.css({
color: settings.color,
backgroundColor: settings.backgroundColor
});
};
}( jQuery ));
使用
#556b2f的颜色默认值被$ .extend()覆盖为橙色。
$( "div" ).greenify({
color: "orange"
});
以下是使用我们讨论过的一些技术的小插件示例:
这个方便的插件遍历集合中的所有锚点,并在括号中附加href属性。
(function( $ ) {
$.fn.showLinkLocation = function() {
this.filter( "a" ).each(function() {
var link = $( this );
link.append( " (" + link.attr( "href" ) + ")" );
});
return this;
};
}( jQuery ));
// Usage example:
$( "a" ).showLinkLocation();
Foo
Foo (page.html)
我们的插件可以优化:
(function( $ ) {
$.fn.showLinkLocation = function() {
this.filter( "a" ).append(function() {
return " (" + this.href + ")";
});
return this;
};
}( jQuery ));
我们使用.append()方法接受回调的功能,该回调的返回值将确定附加到集合中每个元素的内容。 另请注意,我们没有使用.attr()方法来检索href属性,因为本机DOM API使我们可以使用恰当命名的href属性轻松访问。
jQuery.fn.extend(object)
增加两个插件方法。
jQuery.fn.extend({
check: function() {
return this.each(function() { this.checked = true; });
},
uncheck: function() {
return this.each(function() { this.checked = false; });
}
});
$("input[type=checkbox]").check();
$("input[type=radio]").uncheck();
你可以拓展一个对象到jQuery的 prototype里去,这样的话就是插件机制了。
(function($) {
$.fn.tooltip = function(options) {};
//等价于 var
tooltip = {
function(options) {}
};
$.fn.extend(tooltip) = $.prototype.extend(tooltip) = $.fn.tooltip
})(jQuery);
Query.fn.extend()的调用把方法扩展到了对象的prototype上,所以实例化一个jQuery对象的时候,它就具有了这些方法,在jQuery.JS中到处体现这一点
Objectj Query.extend( target, object1, [objectN])
用一个或多个其他对象来扩展一个对象,返回被扩展的对象
var settings = { validate: false, limit: 5, name: "foo" };
var options = { validate: true, name: "bar" };
jQuery.extend(settings, options);
结果:settings == { validate: true, limit: 5, name: "bar" }