本回内容介绍
上一回聊到JS的类的模拟,继承,分析了nodejs,extjs,jquery,underscore的继承源码。
介一回,偶们来聊一下在JS中模拟接口,掺元类,装饰者模式,有些盆友可能用过ES6或者TypeScript的,知道在ES6里已经有interface,在es5中呢,只能靠模拟了,来吧,开始咯~
1. 接口模式
接口模式,主要是模拟java接口检测函数,确保接口的实现类必须实现接口中的方法:
/**
* 参数1:接口的名字,类型:string
* 参数2:方法名称的集合,类型:array
*/
var Interface = function(name,methods){
// 判断接口参数的个数
if(arguments.length!=2){
throw new Error('Interface exactly 2 arguments are expected"!');
}
this.name = name;
this.methods = [];
for(var i=0,len=methods.length;i<len;i++){
if(typeof methods[i]!=='string'){
throw new Error('Interface method name is expected to be passed in as a string!');
}
this.methods.push(methods[i]);
}
};
Interface.ensureImplements = function(o){
var i=1,len=arguments.length;
// 检测参数
if(len<2){
throw new Error('Interface ensureImplements at least 2 arguments are expected!');
}
// 获得实例
for(;i<len;i++){
var j=0,instanceInterface = arguments[i],mLen=instanceInterface.methods.length;
// 判断参数是否是接口类型
if(instanceInterface.constructor!==Interface){
throw new Error('instances of Interface are expected');
}
// 循环接口实例对象里的每一个方法
for(;j<mLen;j++){
var mName = instanceInterface.methods[j];
if(!o[mName]||typeof o[mName]!=='function'){
throw new Error("the method name" + mName + "() is not found!");
}
}
}
};
var PersonInterface = new Interface('PersonInterface',['eat','drink']); // 定义一个人的接口,有吃,喝的方法
var ProgrammerInterface = new Interface('ProgrammerInterface',['coding']); // 定义一个程序猿的接口,有写代码的方法
var DavidImpl = function(){ // 定义一个叫大卫的哥们儿
Interface.ensureImplements(this,PersonInterface,ProgrammerInterface); // 大卫必须实现的接口
};
DavidImpl.prototype.eat = function(o){
var str = '大卫是个大胖子,一天只晓得吃';
return str;
}
DavidImpl.prototype.drink = function(o){
var str = '大卫除了吃就是喝';
return str;
}
DavidImpl.prototype.coding = function(o){
var str = '写代码吧,大卫';
return str;
}
var david = new DavidImpl();
david.eat(); // 返回eat
这就是一个接口的模拟了,参考JavaScript设计模式上的例子,做了些修改,把一些不好理解的地方都详细的分析了,应该感觉还好吧。如果感觉吃力的话,可能是对接口的概念不是很了解,没关系,后面聊其他设计模式的时候,会常用到接口。这里咱再升华一下,在接口模式的基础上再写点内容,聊聊装饰者模式。
(注:这里投抛异常的英文句子都是我自己写的,英文不好,如果有单词,语法错误,请见谅!)
2. 装饰者模式
装饰者模式,给对象动态添加职责的方式称为装饰者(decorator)模式。
这里我们接着接口的例子来,上面的代码就不复制了,这里接着写,继续把大卫给玩儿坏。
var M20 = function(o){ // 这里定义一个装饰类
var str = '20多岁的时候,';
// o是传入的对象,调用传入对象的方法,加以装饰
this.eat = function(){
return str + o.eat()+",肥得很!";
};
this.drink = function(){
return str + o.drink()+",就是个水桶!";
};
this.coding = function(){
return str + o.coding()+",代码又写得撇!";
};
}
alert(new M20(david).eat()); // 20多岁的时候,大卫是个大胖子,一天只晓得吃,肥得很!
alert(new M20(david).drink()); // 20多岁的时候,大卫除了吃就是喝,就是个水桶!
alert(new M20(david).coding()); // 20多岁的时候,写代码吧,大卫,代码又写得撇!
这个例子应该很好理解吧,(注:这个代码是接着上边儿接口例子的代码噢),后面我们还会用到装饰者模式做一些复杂的例子。
先装个逼,再继续,最近没啥钟汉良的新戏,只好看他的老戏《天涯明月刀》,以前吴岱融版本的《边城浪子》也好看,都是经典的傅红雪。
这一回讲的内容不多,就一个接口模式,一个装饰者模式的入门级示例,
下面的内容就是写一个掺元类,算是对上一回的一个复习吧。
3. 掺元类
掺元类:也是继承的一种,这里就简单的模拟一下c++的多亲继承,帅狐show time,just do it:
// 定义一个掺元类,o是传入的子类对象
function Mixin(o) {
// copy聊柯里化的时候写过的代码,眼熟吧
var _slice = Array.prototype.slice;
var args = _slice.call(arguments,1);
// 跟接口有点类似,从第二个参数开始遍历
for (var i=0; i<args.length; i++) {
var sup = args[i];
// 把父类的原型对象付给sub
var sub = sup.prototype;
// 再枚举循环sub的属性方法
for (var method in sub) {
// 判断传入的子类对象是否存在sub的属性
if (!o.prototype[method]) {
o.prototype[method] = sub[method];
}
}
}
}
function Person() {} // 定义一个Person类
Person.prototype.eat = function(){
alert("吃");
}
Person.prototype.drink = function(){
alert("喝");
}
function Programmer() {} // 定义一个程序猿类
Programmer.prototype.coding = function(){
alert("写代码,写的撇!");
}
function David() {} // 定义一个叫大卫的哥们儿
Mixin(David,Person,Programmer) // 调用,拷贝
var david = new David()
david.coding(); // 返回写代码,写的撇!
难度不大吧,当然这里只是简单的实现,代码写得不严谨。
在网上看到有JSer说:"Backbone的源码广泛使用掺元类",以前玩过这个框架,但在工作项目中没有用过Backbone(这是一个依赖jquery,underscore.js的MVC框架),看到这哥们儿说的话后,就好奇想看看源码,我们就简单的分析下backbone的extend。
4. backbone之extend源码浅析
backbone的extend浅析源码(官网源码地址:http://backbonejs.org/backbone.js)
首先backbone是依赖与underscore.js的,而underscore.js的_.extend在上一回我们已经聊过了,来吧,直接看backbone的extend源码:
// 定义extend类,没什么好说的
var extend = function(protoProps, staticProps) {
var parent = this; // 父类
var child; // 子类
// _开头的是underscore.js的方法,这里是判断传入的对象是否存在
if (protoProps && _.has(protoProps, 'constructor')) {
// protoProps.constructor是传入对象的构造器
child = protoProps.constructor;
} else {
// 这一步是对象冒充继承,但是要注意对象冒充只能继承构造里的信息,不能继承原型.
child = function(){ return parent.apply(this, arguments); };
}
// 这个_.extend是上一回聊过的underscore.js的继承,这里是继承父类,staticProps(这是静态属性).
_.extend(child, parent, staticProps);
// this.constructor = child;这里是还原子类构造器
var Surrogate = function(){ this.constructor = child; };
// 这一步是原型继承父类
Surrogate.prototype = parent.prototype;
// 这里就是实现子类的原型继承,这手法是不是有点像上一回模拟Extjs继承的手法
child.prototype = new Surrogate;
if (protoProps) _.extend(child.prototype, protoProps);
// 定义一个属性保存父类的原型,跟上一回的模拟ExtJS继承像吧.
child.__super__ = parent.prototype;
return child;
};
// 给model, collection, router, view, history都加上继承.
Model.extend = Collection.extend = Router.extend = View.extend = History.extend = extend;
这个就是backbone的继承实现了,难解的地方我都写了注释,如果有分析错的地方,请指正。如果感觉晕菜的话,可以跳过,。
这一回,主要聊了接口的模拟,浅谈装饰者模式,模拟了掺元类实现多亲继承,分析了下backbone的extend,如果上一回的underscore和extjs感觉还好的话,这回backbone的extend应该没啥难度~~
下一回,咱主要聊一聊单例模式。
话说最近钟汉良,蒋劲夫领衔的《我要上学啦》真人秀第一季over了,从第一期追剧追到最后,真是好玩吖,主题曲也励志:如果能,重返17岁,插上翅膀你敢不敢高飞...
注:此系飞狐原创,转载请注明出处
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。