jQuery插件扩展
一、jQuery的扩展性
jQuery,除了对JavaScript进行封装让写法、功能实现更简单,特别是选择器抓取DOM元素(虽然有了vue等插件会减少操作dom的需求)和ajax调用;而且,它的可扩展性也是受众多开发者青睐的,从而可以建立起自己公司的生态系统。
二、扩展方式
1、通过$.extend()
扩展
仅仅是在jQuery命名空间或者说是jQuery身上添加了一个静态方法而已。
通过调用$.extend()
添加的函数直接通过$
符号调用($.func()
),不需要抓取DOM元素($('#id').func()
)。
<html>
<head>
<meta charset="utf-8">
<title></title>
<script typet="text/javascript" src="http://code.jquery.com/jquery-latest.js"></script>
</head>
<body>
<script>
$.extend({
sayHello(name){
console.log(`Hello ${name}`);
}
});
$.sayHello('qiurx'); //Hello qiurx
</script>
</body>
</html>
这种方法可以简单的添加一个jQuery插件,通常用来定义一些辅助方法,例如console.log打印自定义日志。用另一种写法:
<html>
<head>
<meta charset="utf-8">
<title></title>
<script typet="text/javascript" src="http://code.jquery.com/jquery-latest.js"></script>
</head>
<body>
<script>
$.logDate = function(msg){
var now = new Date();
var y = now.getFullYear();
var m = now.getMonth()*1+1;
var d = now.getDate();
var hh = now.getHours();
var mm = now.getMinutes();
var ss = now.getSeconds();
console.log(`${y}-${m}-${d} ${hh}:${mm}:${ss}————${msg}`);
}
$.logDate('异步调用开始'); //2019-4-8 19:46:8————异步调用开始
</script>
</body>
</html>
但这种方法无法利用jQuery强大的选择器带来的便利。
2、通过$.fn
扩展
jQuery扩展最常用的一种方法。
$.fn
的插件执行函数中,this指向jQuery选择器选中的元素,是一个jQuery类型的集合。集合已经是jQuery包装类型了,可以直接调用jQuery的其他方法,也不需要用$
重新包装了。
<html>
<head>
<meta charset="utf-8">
<title></title>
<script typet="text/javascript" src="http://code.jquery.com/jquery-latest.js"></script>
</head>
<body>
<a href="http://baidu.com">百度</a>
<script>
$.fn.colorRed = function(){
this.css('color','red');
}
$('a').colorRed(); //此插件内部的this即为$('a')
</script>
</body>
</html>
$.fn
添加的插件内部this指向jQuery选择器返回的集合,可以通过$.each()
操作集合中的每个元素。注意,$.each()
方法内部,this指向集合中的每个元素,是普通的DOM元素,这时候要调用jQuery的方法需要用$
重新包装下。
<html>
<head>
<meta charset="utf-8">
<title></title>
<script typet="text/javascript" src="http://code.jquery.com/jquery-latest.js"></script>
</head>
<body>
<li><a href="http://baidu.com">百度</a></li>
<li><a href="https://www.json.cn/">JSON解析</a></li>
<script>
$.fn.showHref = function(){
this.each(function(){
//此处this指向每个普通的DOM元素,要重新$获取
$(this).append($(this).attr('href'));
});
}
$('a').showHref();
</script>
</body>
</html>
3、通过
$.widget()
应用jQuery UI的部件工厂方式创建扩展略。
三、
$.fn
的链式调用jQuery一个很好的地方就是支持链式调用,选择器抓取DOM元素后可以不断的调用其他方法。要让插件支持这种调用,只需要return一下。
<html>
<head>
<meta charset="utf-8">
<title></title>
<script typet="text/javascript" src="http://code.jquery.com/jquery-latest.js"></script>
</head>
<body>
<li><a href="http://baidu.com">百度</a></li>
<li><a href="https://www.json.cn/">JSON解析</a></li>
<script>
$.fn.colorRed = function(opt) {
var defaults = {
'color' : 'red',
'fontSize' : '12px'
}
$.extend(defaults,opt); //让插件接收参数,有则合并取参数内容,没有则用默认值
return this.css(defaults);
}
$.fn.showHref = function() {
this.each(function() {
$(this).append($(this).attr('href'));
});
return this;
}
$('a').colorRed({'color':'blue'}).showHref();
</script>
</body>
</html>
四、面向对象思维开发插件
首先为什么要有面向对象思维。需要一个方法,就去定义一个function;需要一个变量,就随意在代码各处定义全局变量。这种做法不方便维护,也不清晰,虽然在代码规模小的时候体现不明显。
- 如果将重要属性和方法都定义在对象上,需要时就通过对象来调用,这样方便管理,而且也减少影响外部的命名空间可能性(随着代码增多,全局属性和方法多了在全局中可能会出现重名覆盖,和别人冲突,难维护)。
<html>
<head>
<meta charset="utf-8">
<title></title>
<script typet="text/javascript" src="http://code.jquery.com/jquery-latest.js"></script>
</head>
<body>
<li><a href="http://baidu.com">百度</a></li>
<li><a href="https://www.json.cn/">JSON解析</a></li>
<script>
var Beautiful = function(ele, opt) { //定义一个专门用来美化的类(构造函数)
this.$ele = ele;
var defaults = {
'color': 'red',
'fontSize': '12px',
'textDecoration': 'none'
}
this.options = $.extend({}, defaults, opt);
}
Beautiful.prototype = { //定义原型方法
beautify(){
return this.$ele.css({
'color': this.options.color,
'fontSize': this.options.fontSize,
'textDecoration': this.options.textDecoration
});
}
}
$.fn.myPlugin = function(opt) { //定义一个插件
var beautifier = new Beautiful(this,opt); //调用构造函数创建对象
return beautifier.beautify(); //调用对象方法
}
$('a').myPlugin({
'color': 'yellow',
'fontSize': '20px',
});
</script>
</body>
</html>
像这样改造,以后需要新属性方法时就直往构造函数添加,然后在插件里实例化后调用。
五、自调用匿名函数包裹代码
为了避免污染全局命名空间,不到万不得已尽量不把变量定义成全局的;还有一个方法,用自调用匿名函数包裹代码,这样可以安全的用于任何地方,绝对不会冲突。
- js无法用花括号快捷的创建创建作用域,但是函数可以函数内无法被外部访问(除非闭包返回出去)。将代码写在一个函数中,就不会污染全局命名空间了,然后再自调用实现内部代码(也可以直接把代码写到jQuery的插件定义代码中去
$.fn...
,但这样让插件定义显得臃肿,插件定义代码应该更注重调用和如何与jQuery实现互动)。
修改为自调用匿名函数形式:
<html>
<head>
<meta charset="utf-8">
<title></title>
<script typet="text/javascript" src="http://code.jquery.com/jquery-latest.js"></script>
</head>
<body>
<li><a href="http://baidu.com">百度</a></li>
<li><a href="https://www.json.cn/">JSON解析</a></li>
<script>
//避免前面的代码没有加(;)结尾,与自调用匿名函数相连报错
;(function (){ //定义一个匿名函数
var Beautiful = function(ele, opt) { //定义一个专门用来美化的类(构造函数)
this.$ele = ele;
var defaults = {
'color': 'red',
'fontSize': '12px',
'textDecoration': 'none'
}
this.options = $.extend({}, defaults, opt);
}
Beautiful.prototype = { //定义原型方法
beautify(){
return this.$ele.css({
'color': this.options.color,
'fontSize': this.options.fontSize,
'textDecoration': this.options.textDecoration
});
}
}
$.fn.myPlugin = function(opt) { //定义一个插件
var beautifier = new Beautiful(this,opt); //调用构造函数创建对象
return beautifier.beautify(); //调用对象方法
}
}
)(); //定义函数后自调用
$('a').myPlugin({
'color': 'yellow',
'fontSize': '20px',
});
</script>
</body>
</html>
自调用匿名函数中的代码会在第一时间执行。
六、良好习惯
1、习惯性在代码开头加上分号(;),避免前面的代码没以分号结尾导致代码运行报错。
<html>
<head>
<meta charset="utf-8">
<title></title>
<script typet="text/javascript" src="http://code.jquery.com/jquery-latest.js"></script>
</head>
<body>
<script>
var foo = function() {
//别人的代码
} //注意这里没有用分号结尾
//开始我们的代码。。。
(function() {
alert('Hello!');
})();
</script>
</body>
</html>
匿名函数的第一个括号与前面别人的函数相连导致了报错,所以习惯性在我们的代码前面加个分号。
;(function(){
//我们的代码。。
alert('Hello!');
})();
2、将系统变量以参数形式传递到插件内部也是个不错的实践。
window等系统变量在插件内部就有了一个局部的引用,可以提高访问速度,会有些许性能的提升。
;(function($,window,document,undefined){
//我们的代码。。
//blah blah blah...
})(jQuery,window,document);
至于这个undefined参数,为了得到没有被修改的undefined,我们并没有传递这个参数,但却在接收时接收了它,因为实际并没有传,所以‘undefined’那个位置接收到的就是真实的'undefined'了。