本文共--字 阅读约--分钟 | 浏览: -- Last Updated: 2021-12-15
链模式(Chain of Responsibility):通过在对象方法中将当前对象返回,实现同一个对象多个方法的链式调用,从而简化该对象的多个方法的多次调用时,对该对象的多次引用。
在这个方面最佳的实践就是JQuery库。让我们探索它是如何实现的?
// 根据原型链访问其原型对象,以实现链调用
var $ = function (selector) {
return $.prototype.init(selector);
};
$.prototype = {
init(selector) {
this[0] = document.getElementById(selector);
this.length = 1;
// 不能直接返回元素,因为为了链式调用只能返回this,所以将获取到的元素存储在属性0上
return this;
},
size() {
return this.length;
}
};
$('.className').size(); // 1
但是,此时存在一个问题,就是当再次调用 $('.className2')
的时候,之前存储在this[0]
上的dom对象就会被替换。因为每次调用$()
返回的都是$.prototype
,并且它们的size方法也是共用的,所以可以直接使用new关键字去使得返回一个新的实例对象。
var $ = function (selector) {
return new $.prototype.init(selector);
};
$.prototype = {
// 强化构造器
constructor: $,
// 如果使用简写的对象方法 init(){} 那么将无法被new调用
init: function (selector) {
console.log(this);
this[0] = document.getElementById(selector);
this.length = 1;
return this;
},
size: function() {
return this.length;
}
};
// 使用new调用之后,将返回的实例对象的原型链指回
$.prototype.init.prototype = $.prototype;
var a = $('abc').size();
而关于链式调用,则是通过extend扩展多个方法到原型对象上并返回this来实现。
var $ = function (selector) {
return new $.prototype.init(selector);
};
$.prototype = {
constructor: $,
// 如果使用简写的对象方法 init(){} 那么将无法被new调用
init: function (selector) {
if (selector.startsWith('#')) {
this[0] = document.getElementById(selector.slice(1));
this.length = 1;
} else {
var doms = selector.startsWith('.') ? document.getElementsByClassName(selector.slice(1)) : document.getElementsByTagName(selector);
for (var i = 0; i < doms.length; i++) {
this[i] = doms[i];
}
this.length = doms.length;
}
return this;
},
size: function() {
return this.length;
}
};
$.prototype.init.prototype = $.prototype;
// 方法扩展,可以扩展在$上,$.xx 也可以扩展到原型对象上 $('#id').xx
$.extend = $.prototype.extend = function() {
// 如果只有一个参数,就当做是扩展$和$.prototype
// 如果有多个参数,当做将后面的参数对象,扩展到第一个参数对象上
var i = 1;
var len = arguments.length;
var target = arguments[0];
if (i === len) {
target = this;
i--;
}
for (; i < len; i++) {
for(var key in arguments[i]) {
target[key] = arguments[i][key]
}
}
return target;
}
var a = $.extend({ first: 1}, { second: 2})
console.log(a); // {first: 1, second: 2}
// 扩展到实例上 $.prototype
$.extend($.prototype, { version: '1.0.0'})
console.log($('demo').version); // 1.0.0
$.prototype.extend({ getVersion: function() { return this.version } }) // 只有1个参数
console.log($('demo').getVersion()) // 1.0.0
// 扩展到$上
$.extend($, { author: 'jQuery team' }); // jQuery team
console.log($.author);
$.extend({ nickname: '$' }) // 只有一个参数
console.log($.nickname); // $
// 添加方法以测试链式调用
$.prototype.extend({
on: function(type, fn) {
var i = this.length - 1;
// 遍历所有元素添加事件
for (; i >= 0; i--) {
this[i].addEventListener(type, fn, false)
}
return this;
},
css: function() {
var len = arguments.length;
if (this.length < 1) { // 没有获取过元素
return this;
}
if (len === 1) { // 只有一个参数
if (typeof arguments[0] === 'string') {
// 如果为字符串则作用为 获取第一个元素的css样式 $('#id').css('fontSize')
return getComputedStyle(this[0], false)[arguments[0]]
}
if (typeof arguments[0] === 'object') {
// 如果为对象,则设置第一个元素的多个样式 $('#id').css({ color: 'red', height: '30px' })
for (var key in arguments[0]) {
for (var j = this.length - 1; j >= 0; j--) { // 如果获取到的是一组元素就设置多个元素的多个样式
this[j].style[key] = arguments[0][key]
}
}
}
} else if (len === 2) { // 两个参数则设置一个样式, $('#id').css('color', 'green')
for (var j = this.length - 1; j >= 0; j--) {
this[j].style[arguments[0]] = arguments[1];
}
}
return this;
}
})
console.log($('.my-div'))
$('.my-div').css('color', 'red').css({ height: '18px'}).on('click', function() {
var h = $('.my-div').css('height');
console.log(h);
})