js设计模式之路--面向对象
A: 我现在需要几个需求,你给我实现下:
- 验证用户名
- 邮箱
- 密码
B: 第一次看到这个需求,这么简单,随手便写下:
1 function checkName() { 2 // 验证姓名 3 } 4 function checkEmail() { 5 // 验证邮箱 6 } 7 function checkPassword() { 8 // 验证密码 9 }
此时我准备提交代码了,但是被打住了。
A:你创建了很多全局变量哦。
B:变量?不是只写了几个函数而已。
A:函数不是变量?
B:心想:“函数椒变量???”
A:我这么实现你的功能你看是否可以?
var checkName = function () {
// 验证姓名
}
var checkEmail = function () {
// 验证邮箱
}
var checkPassword = function () {
// 验证密码
}
B:一样的只不过。。。。
A:对,只不过这个在用的时候提前生命,但是这么看你会发现你创建了3个函数,并存在变量里面。这个和你之前写的只是变量名放置位置不一样罢了。所以说你声明了3个变量,明白了。
B:明白。
B:这样有什么问题那?
A:从功能来讲当然没问题,但是你在实际开发中不能只考虑你自己,也要考虑会不会影响到他人,如果别人也定义了和你一样的方法,那么大家的代码之间就会参生覆盖,不管是你的代码功能覆盖别人的还是别人覆盖你的,都不是开发中想要的,而是要杜绝的。而且如果方法很多,这种相互之间的覆盖我饿恩提是很不容易察觉的。
B:那我应该怎样避免那?
A:你可以将他放在一个变量里面这样可以减少覆盖或者被覆盖的风险,但是,如果变了名也出现了覆盖的情况的话,那么对应的里面的所有功能都会失效,但是这种现象也很明显,你自然也会很容易察觉到。
B:可是我该如何做那?
A:对象你知道吧?对象有属性和方法,而我们访问对象的属性和方法的时候,可以通过点语法来遍历查询得到,英雌我们可以创建一个检测对象,然后把我们的方法放在里面:
var checkObject = {
checkName: function () {
// 验证姓名
},
checkEmail: function () {
// 验证邮箱
},
checkPassword: function () {
// 验证密码
}
}
A:此时我们将所有的函数作为checkObject对象的方法,这样我们就只有一个对象,而我们要使用它们也很简单,比如检测姓名 checkObject.checkName(),只是我们在使用的时候前面多了个对象名称而已。
B:checkObject是我们直接利用“{}”创建的对象,那我们可否使用 function 对象来创建checkobject对象,然后再将方法添加给对象。
A:可以的,但是首先你得创建一个对象,然后再给他添加方法。
var checkObject = function() {};
checkObject.checkName = function () {
// 验证姓名
}
checkObject.checkEmail = function () {
// 验证邮箱
}
checkObject.checkPassword = function () {
// 验证密码
}
A:使用和前面一样,通过点语法 chekcObject.checkName(),虽然现在能满足你得需求,但是当别人想用你写的对象的时候就有写麻烦,因为这个对象不能复制一份。或者说这样说,你通过new关键字创建checkObject的新对象是无法集成这些方法的。
B:上面你说的别人用我的代码,不能复制是怎么回事,能复制有能怎样?
A:这样和你举个例子吧,你买了一本书,回家后你的同事看见后觉得书很好,也想要怎么办?书就这么一本。但是如果你买的是一台打印机,那么不管你有多少小伙伴都能给他们没人打印一本。
这里你可能会想,再买一本,但是你要知道,你再买一本,就相当于别人重新写一份代码,和你的就没什么关系,没关系也就没有讨论这个问题的必要。
因此上面的问题是基于,你买的书(代码)很好,别人想直接拿来用。
如果但对复制这个功能的话,你可以这样写你的代码:
var checkObject = function() {
return {
checkName: function () {
// 验证姓名
},
checkEmail: function () {
// 验证邮箱
},
checkPassword: function () {
// 验证密码
}
}
};
B:嗯,你这样写,每次别人调用这个函数的时候就会返回出一个对象,而这个对象里面包含对应的所有方法。这样每个人想使用的话,就执行下这个方法,从表面上看,这个是checkObject对象,但是实际上调用这个函数后返回的新的对象。这样每个人使用就互不影响。我们使用上面代码验证姓名:
var a = checkObject(); a.checkName();
A:虽然是通过创建新对象来完成我们的需求,但是他不是一个真正意义上类的创建方式。并且创建的对象a和对象checkObject对象无关,因此这里我们得向面向对象改造:
var checkObject = function() {
this.checkName = function() {
// 验证姓名
}
this.checkEmail = function() {
// 验证邮箱
}
this.checkPassword = function() {
// 验证密码
}
}
A:像上面这样的对象就可以看成类了。
B:类?我们还像之前那样创建吗?
A:不,既然是一个类,你就要使用关键字 new 来创建。
var a = new checkObject(); a.checkName();
A:这样就可以利用 checkObject 类创建出对象,如果别人想用的话我们只需要通过checkObject类来创建对象。每个新创建的对象都有一套属于自己的方法。
那你知道为什么他们通过新创建对象都有属于自己的方法,而互相不影响吗?
B:不知道。
A:你看我们吧所有的方法放在函数内部了,通过this定义,所以每次通过new关键字创建新对象时候。新创建的对象会对类的this上的属性和方法,进行复制,相当于,每个人只要创建一份,都是复制自己的一份走,所以他们会才会互相不影响。但是换个角度看,我们的方法都是一样的,每次创建新对象的时候,都复制都一份,这样感觉很浪费资源。相同的东西多份肯定浪费了。
B:我们可以将这些不变的方法都提出来,不用每次创建的时候都复制一份走,而是直接去某个地方拿。
A:没错。所以你可以这样写:
var checkObject = function() {};
checkObject.prototype.checkName = function() {
// 验证姓名
}
checkObject.prototype.checkEmail = function() {
// 验证邮箱
}
checkObject.prototype.checkPassword = function() {
// 验证密码
}
A:这样创建对象的时候,创建出来的对象所拥有的方法就都是一个了。因为他们依赖prototype原型依次寻找。而找到的方法都是同一个。他们都绑定在checkObject对象类的原型上。
但是上面这样写有一个问题,就是要将prototype写很多便,因此我们也可以这样做:
var checkObject = function() {};
checkObject.prototype ={
checkName: function () {
// 验证姓名
},
checkEmail: function () {
// 验证邮箱
},
checkPassword: function () {
// 验证密码
}
}
但是要记住。着两种方式不能混着用,否则一旦混用,如果后面为对象的原型对象赋值新对象时,那么它将会覆盖掉之前对prototype对象赋值的方法。
这里我的理解是,以上面的例子为列,使用“{}”为原型对象赋值,结果赋值的是一个对象,这里可以看成后面赋值的对象是一个整体,所以当你再赋值的时候,这个对象就会被新赋值的方法给覆盖。----疑问点??
B:所以上面的调用:
var a = new checkObject() a.checkName(); a.checkEmail(); a.checkPassword();
A:没错。但是你发现一个问题没?你调用3个方法,对象a就写了3次。这是可以避免的,如果要改变这种情况,就要再你声明每个方法的时候,在末尾将当前对象返回。在方法中this指向当前对象,看个小例子:
var checkObject ={
checkName: function () {
// 验证姓名
return this;
},
checkEmail: function () {
// 验证邮箱
return this;
},
checkPassword: function () {
// 验证密码
return this;
}
}
此时我们想使用它就可以这样:
checkObject.checkName().checkEmail().checkPassword();
当然同样的方式我们也可以用在类的原型对象中:
var checkObject = function() {};
checkObject.prototype ={
checkName: function () {
// 验证姓名
return this;
},
checkEmail: function () {
// 验证邮箱
return this;
},
checkPassword: function () {
// 验证密码
return this;
}
}
但是这里的使用就不是像前面的那样直接使用了,这里必须通过关键字 new。
var a = new checkObject(); a.checkName().checkEmail().checkPassword();
如果你看过prototype.js的代码,你会想到下面的书写方式。
B:prototype.js是什么?
A:一款javascript框架里面封装了很多方法,最大特点就是对原生对象(Array、Function、Object等等)的拓展。比如你想给每个函数加上一个检测邮箱的方法就可以这么做:
1 Function.prototype.checkEmail = function() { 2 // 验证邮箱 3 }
当然如果你习惯函数形式,那么你可以这么写:
1 var f = function() {}; 2 f.checkEmail();
如果你习惯类的形式你也可以这么做:
1 var f = new Function(); 2 f.checkEmail();
但是你这么做我们一般不建议。因为你污染了原生对象Function,当别人创建函数的时候,因为你是直接对Function原型上绑定的,所以会造成对被人创建方法的时候,也会继承这个方法,这样在别人不知道的情况下,既然默默的多个方法。造成不必要的开销。
但是你可以抽象出一个统一添加功能的方法:
Function.prototype.addMethod = function(name, fn) { this[name] = fn; }
这样如果你想添加邮箱验证和姓名验证的话,你可以这样做:
1 var methods = function() {}; 2 // 或者 3 var methods = new Function(); 4 methods.addMethod('checkName', function() { 5 6 }); 7 8 methods.addMethod('checkEmail', function() { 9 10 }); 11 12 // 调用 13 methods.checkName(); 14 methods.checkEmail();
B:能不能想前面一样使用链式调方法那?
A:当然可以,和前面一样通过this将当前对象返回出来:
1 Function.prototype.addMethod = function(name, fn) { 2 this[name] = fn; 3 return this; 4 }
使用:
1 var methods = function() {}; 2 // 或者 3 var methods = new Function(); 4 methods.addMethod('checkName', function() { 5 6 }).addMethod('checkEmail', function() { 7 8 });
A:那我问你,如果想链式调用改如何实现那?
B:和链式增加方法一样,将对象通过 this 将当前对象返回出来。
1 var methods = function() {}; 2 // 或者 3 var methods = new Function(); 4 methods.addMethod('checkName', function() { 5 6 return this; 7 }).addMethod('checkEmail', function() { 8 9 return this; 10 });
使用:
1 // 调用 2 methods.checkName().checkEmail();
A:上面的代码使用的是函数式的调用,对习惯对象方式的人来说:
1 Function.prototype.addMethod = function(name, fn) { 2 this.prototype[name] = fn; 3 return this; 4 }
此时我们再调用这个这样添加的方法的时候,不能通过想前面调用一样,直接通过点调用,而是要先通过关键字 new 实例化对象后再通过对象调用方法:
1 var m = new Methods(); 2 m.checkEmail();
总结:
这里结合两人对话的方式对代码进行分析展现,要多理解体会,其中对象绑定方法的两种方式不能昏混用,这里有疑惑,带查资料解决。
浙公网安备 33010602011771号