JavaScript学习笔记9-行为委托

  |  

前言

继续看《你不知道的JavaScript》


45



面向委托的设计

在之前我们讨论原型的时候,我们一直在讨论如何利用[[Prototype]]来模拟类的实现和继承。但是为了更好的学习如何直观的使用[[Prototype]],我们必须要知道它代表的是一种不同于类的设计模式。我们必须要把思路从类和继承的设计模式转换到委托行为的模式。

类理论

当我们需要在软件中建模一些类似的任务。如果使用类的方式,那设计方法可能是这样的:定义一个父类,可以将其命名为Task,在Task类中定义所有任务都有的行为。接着定义子类XYZ和ABC,它们都继承自Task并且会添加一些特殊的行为来处理对应的任务。

非常重要的是,类世界模式鼓励在继承时使用方法重写(和堕胎)。

委托理论

现在我们尝试用委托行为的方式来思考同样的问题。

首先我们会定义一个名为Task的对象,他会包含所有任务都可以使用的具体行为。接着,对于每个任务,我们在定义一个对象了存储对应的数据和行为。然后把特定的任务对象关联到Task功能对象上,让它们在需要的时候可以进行委托。下面是推荐形式的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
Task = {
setID: function(ID) { this.id = ID; },
outputID: function() { console.log(this.id); }
};
XYZ = Object.create(Task); //让XYZ委托Task
XYZ.prepareTask = function(ID, label) {
this.setID(ID);
this.label = label
};
XYZ.outputTaskDatails = function() {
this.outpustID();
console.log(this.label);
};

在这段代码中,Task和XYZ并不是类(或者函数),它们是对象。XYZ通过Object.create()创建,它的[[Prototype]]委托给Task对象。

相比于面向类(或者说面向对象),这种编码风格称为“对象关联”。

在JavaScript中,[[Prototype]]机制会把对象关联到其它对象上。对象关联风格的代码跟面向类的风格有些不同:

  • 通常来说,在[[Prototye]]委托中最好把状态保存在委托者(及XYZ)而不是委托目标(及Task)上
  • 在委托行为中,我们会尽量避免在[[Prototype]]链的不同级别中使用相同的命名
  • 在API接口设计中,委托最好在内部实现,不要直接暴露出去。就像我们在XYZ.prepareTask()中委托Task.setID()

需要注意的是,互相委托是被禁止的。及如果你把B关联到A然后试着把A关联到B,就会出错。

比较两种思维模型

在前面我们已经明白了“类”和“委托”这两种设计模式的理论区别。下面我们来看看它们在思维模型上的区别

我们先来看一下典型的面向对象的风格

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function Foo(who) {
this.me = who;
}
Foo.prototype.identify = function() {
return "I am " + this.me;
};
function Bar(who) {
Foo.call(this, who);
}
Bar.prototype = Object.create(Foo.prototype);
Bar.prototype.speak = function() {
alert("Hello, " + this.identify() + "." );
};
var b1 = new Bar("b1");
var b2 = new Bar("b2");
b1.speak();
b2.speak();

子类Bar继承了父类Foo,然后生成了b1和b2两个实例。

下面我们来看看如何使用对象关联风格来编写功能完全相同的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Foo = {
init: function(who) {
this.me = who;
},
identify: function() {
return "I am " + this,me;
}
};
Bar = Object.create(Foo);
Bar.speak = function() {
alert("Hello, " + this.indentify() + ".");
};
var b1 = Object.create(Bar);
b1.init("b1");
var b2 = Object.create(Bar);
b2.init("b2");
b1.speak();
b2.speak();

这段代码中我们同样利用 [[Prototype]] 把 b1 委托给 Bar 并把 Bar 委托给 Foo,和上一段
代码一模一样。我们仍然实现了三个对象之间的关联。

类风格的思维模型强调实体以及实体间的关系

类

这是一个很复杂的关系网络,但是通过箭头走向,我们还是可以看出JavaScript机制有很强的内部连贯关系。

而我们再来看看对象关联风格代码的思维模型

对象关联

通过比较就可以看出,对象关联风格的代码显然更简洁,因为这种代码只关注一件事:对象之间的关联关系。

文章目录
  1. 1. 面向委托的设计
    1. 1.1. 类理论
    2. 1.2. 委托理论
  2. 2. 比较两种思维模型