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();

总结:

这里结合两人对话的方式对代码进行分析展现,要多理解体会,其中对象绑定方法的两种方式不能昏混用,这里有疑惑,带查资料解决。

 

posted on 2017-07-13 15:17  jayafs  阅读(128)  评论(0)    收藏  举报

导航