浅析JS中的面向对象
这几天在学习JS中的面向对象编程(OOP),经常会被各种创建对象的方法搞混淆:工厂模式、构造函数模式、原型模式、组合模式、动态原型模式、寄生构造函数模式、稳妥构造函数模式等等,今天重点梳理一下构造函数模式、原型模式、组合模式,并浅析这三种常见的创建对象方法之间的关系。
作为一个前端小白,我经常用创建函数的方式来实现各种功能,例如验证注册客户的姓名、邮箱、密码等,我是这样写的:
1 function checkName(){ 2 //验证姓名 3 } 4 function checkEmail(){ 5 //验证邮箱 6 } 7 function checkPassword(){ 8 //验证密码 9 }
这样写会导致什么问题呢?首先,我们都知道函数实际上是对象,函数名指针。既然如此,函数名是不是也可以理解为一个变量呢?重写以上代码如下:
1 var checkName=function(){ 2 //验证姓名 3 } 4 var checkEmail=function(){ 5 //验证邮箱 6 } 7 var checkPassword=function(){ 8 //验证密码 9 }
这样改写后相信大家对函数看得更加清楚了。但是,我用每实现一个功能就在全局环境中创建一个函数的做法是十分不明智的,除了定义多个全局变量会导致混乱外,实际上在全局作用域下的函数实际上只能被某个对象调用。因此应该尽量减少全局变量的个数。
使用对象来封装变量是一个不错的办法。通过将实现某个操作的众多方法封装在一个对象中,既能极大减少全局变量的个数,又能清楚知道某个方法所在的位置。下面就创建一个检测对象CheckObject,将上面三种方法放在里面:
1 var CheckObject = { 2 checkName:function(){ 3 //验证姓名 4 }, 5 checkEmail:function(){ 6 //验证邮箱 7 }, 8 checkPassword:function(){ 9 //验证密码 10 } 11 }
虽然可以通过CheckObject.checkName()的方式来使用对象内的方法,但是这样创建的对象别人是不能复用的,即新创建的对象不能继承上述方法。可以使用构造函数来创建特定类型的对象,解决重用和继承的问题。上述对象进一步改造如下:
1 var CheckObject = function() { 2 this.checkName=function(){ 3 //验证姓名 4 }; 5 this.checkEmail=function(){ 6 //验证姓名 7 }; 8 this.checkPassword=function(){ 9 //验证姓名 10 } 11 };
基此,我们每一次通过关键字new构造新对象的时候,每个新对象都有一套自己的方法。简言之,应用构造函数创建实例时,每个方法都要在实例上重新创建一遍。有时候这种做法会造成极大的浪费,毕竟所有实例的方法本质上都是相同的。为了进一步解决这个问题,我们使用了原型对象。
1 var CheckObject = function() {}; 2 CheckObject.prototype = { 3 checkName:function(){ 4 //验证姓名 5 }, 6 checkEmail:function(){ 7 //验证邮编 8 }, 9 checkPassword:function(){ 10 //验证密码 11 } 12 } 13 14 /* 或者这样: 15 CheckObject.prototype.checkName=function(){ 16 //验证姓名 17 }; 18 CheckObject.prototype.checkEmail=function(){ 19 //验证邮箱 20 }; 21 CheckObject.prototype.checkPassword=function(){ 22 //验证密码 23 }; 24 */
综上,我们用原型对象解决了构造函数产生的共享问题,但是也正因为如此,所有使用原型对象创建的实例都共享属性和方法。方法共享固然是好的,但是每个实例应该有自己属性,而使用原型对象后,修改一个实例的属性必然会影响到其他实例。因此,综合构造函数模式和原型模式各自的特点,采取混合模式来创建对象最为常见。下面举一个简单的例子来说明:
1 function Person(name, age, job){ 2 this.name = name; 3 this.age = age; 4 this.job = job; 5 this.friends = ["Shelby", "Court"]; 6 } 7 8 Person.prototype = { 9 constructor: Person, 10 sayName : function () { 11 alert(this.name); 12 } 13 }; 14 15 var person1 = new Person("Nicholas", 29, "Software Engineer"); 16 var person2 = new Person("Greg", 27, "Doctor"); 17 18 person1.friends.push("Van"); 19 20 alert(person1.friends); //"Shelby,Court,Van" 21 alert(person2.friends); //"Shelby,Court" 22 alert(person1.friends === person2.friends); //false 23 alert(person1.sayName === person2.sayName); //true
混合模式最大的优点在于:用构造函数来定义实例属性,用原型模式来定义方法和共享的属性。
(全文完,欢迎各位不吝指正。)