面向对象众生相

编程语言层出不穷,面向对象的样式各有特色。各种意料之外的编程模型,惊呼”哦,面象对象还可以这样玩”。其中比较容易理解的是Java和C++的对象模型,两者之间区分度也很小: Java的成员函数只能在类内定义, 而C++的成员函数可以类内声明,类外定义。(当然区分并不止这么点)。

后起之秀Go语言对面向对象有了新的定义: 面向对象就是将要处理的数据跟函数进行绑定的方法。

定义数据类型:

1
2
3
type Dog struct{
name string
}

绑定函数:

1
2
3
func (dog Dog) move() {
fmt.Println(dog.name)
}

调用函数:

1
2
3
4
func main(){
  var aDog Dog
   aDog.move();
}

只需要为函数指定接收者,即完成了数据与函数的绑定,而且接收者可以是基础类型,比如int等。可以做到一切皆对象。自由绑定成员函数,使项目功能扩展极其方便。

起初笔者并不喜欢Javascript面向对象的方法,go的出现再次重申了Javascript面向对象的终极地位。且看Javascript几种创建对象的神技:

方法一:通过Object对象

1
2
3
4
5
var box = new Object();
box.name = 'Lee';
box.getName = function () {
return this.name
};

这种方式与go倒有几分相似,不过Javascript不但能绑定函数,还可以增加数据成员。go语言并无出其右。

方法二: 构造函数式:

1
2
3
4
5
6
7
function Box(name){
this.name = name;
this.getName = function () {
return this.name;
}
}
var box1 = new Box('Lee')

方法三:原型模式

1
2
3
4
5
6
function Box() {
}
Box.prototype.name = 'Lee';
Box.prototype.getName = function () {
return this.name;
};

方法四,五…, 招式只局限于你的想象力。为什么Javascript要搞这么复杂, 搞这么复杂…?

其实是Javascript玩了个七十二变的戏法, 让我们产生了错觉。下面来拆解Javascript这种怪异的OOP玩法?

对于方法一, Object也是一个函数(typeof Object == function), 这个函数是Javascript内置的,不需要程序员重新定义。那它和方法二之间的相同基因就不难理解了。

方法一中Javascript可以为对象本身增加属性和绑定方法。方法二也是一样, 而this就是这个对象, 函数为this对象增加属性和绑定方法。再看方法三的模式, 同方法一、二并无不同, 只不过Box.prototype才是这个对象。

其实Javascript中每一个函数都有一个本尊(原型)对象,函数用这个对象作为创建新对象的基础对象, 我们把这个对象看成是具有变化能力的齐天大圣, 新对象都是大圣变化而来。这个本尊对象就是funcation_name.prototype。

而函数就是这位本尊的变身大法,也就是克隆出新对象的构造器, 启动变身大法(new 函数)本尊就会变成想要的模样。这样方法二就容易理解了。

对于方法三, Box.prototype就是Box函数的本尊,改变Box.prototype:Box.prototype.name = ‘Lee’。相当于改变了函数的本尊(原型)对象(由原来的孙悟空变成猪悟能), 构造器的当然就是在这个新本尊的基础上构建新对象。

更有意思的,方法二可以直接作为普通函数调用: Box(‘Lee’),也可以作为构造函数调用 var box1 = new Box(‘Lee’)。函数里的this代表的对象什么不同呢?作为构造函数时,前面已经说过,这里不再重述。但作为普通函数,this却是当前Window对象,这里相当于window.Box(“Lee”),即它是作为Window的成员函数而存在的。Javascript中所有的全局函数和对象都属于Window对象的属性和方法。

网上有很多Javascript面向对象的方法变体,用此方法,即可解疑惑。