行为委托

[[Prototype]] 机制就是指对象中的一个内部链接引用 另一个对象。JavaScript 中这个机制的本质就是对象之间的关联关系

面向委托的设计模式

为了更好地学习如何更直观地使用 [[Prototype]],我们必须认识到它代表的是一种不同
于类(参见第 4 章)的设计模式。

类理论

类设计模式鼓励你在继承时使用方法重写(和多态)

委托理论

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Task = {
setID: function(ID) {
this.id = ID
},
outputID: function() {
console.log(this.id)
}
}

//让XYZ委托Task
XYZ = Object.create(Task)
XYZ.prepareTask = function(ID, Label) {
this.setID(ID)
this.lable = Label
}
XYZ.outputTaskDetails = function() {
this.outputID()
console.log(this.lable)
}

XYZ.prepareTask(1222, 'hh')
XYZ.outputTaskDetails()

/**
* 1222
* hh
*/

将XYZ委托给Task, 当XYZ对象执行setID方式时,该对象本身并没有该方法,会沿着原型链查找到Task对象上,

this.id = ID其实是隐式绑定。当前的this绑定这XYZ并不是Task

打印XYZ与Task对象的属性,如下:

1
2
3
4
5
6
7
{ prepareTask: [Function],
outputTaskDetails: [Function],
id: 1222,
lable: 'hh' }


{ setID: [Function: setID], outputID: [Function: outputID] }

委托行为意味着某些对象(XYZ)在找不到属性或者方法引用时会把这个请求委托给另一
个对象(Task)。

这种设计模式需要注意的几个问题:

  • 委托中最好把状态保存在委托者(XYZ、ABC)而不是委托目标(Task)上。如上例子中,id与label都属于对象XYZ

  • 进行避免在不同级别中使用相同的方法或属性命名,提倡更有描述性的方法名,尤其是要写清相应对象行为的类型。

  • 使用this的隐式绑定,将需要的属性绑定到委托者身上,尤其是对于一些私有的数据属性,这并不需要保存在委托目标上。

互相委托(禁止)

你无法在两个或两个以上互相(双向)委托的对象之间创建循环委托。如果你把 B 关联到 A 然后试着把 A 关联到 B,就会出错。

之所以要禁止互相委托,是因为引擎的开发者们发现在设置时检查(并禁止!)一次无限 循环引用要更加高效,否则每次从对象中查找属性时都需要进行检查。

比较思维模型

对比JavaScript中面向对象与对象关联两种设计模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
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 两个实例。b1 委托了 Bar.prototype,后者
委托了 Foo.prototype。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Foo = {
init: function(who) {
this.me = who; },
identify: function() {
return "I am " + this.me;
} };

Bar = Object.create( Foo );
Bar.speak = function() {
alert( "Hello, " + this.identify() + "." );
};

var b1 = Object.create( Bar );
b1.init( "b1" );
var b2 = Object.create( Bar );

b2.init( "b2" );
b1.speak();
b2.speak();

对象关联风格代码的思维模型

对象之间的关联关系。

小结

行为委托认为对象之间是兄弟关系,互相委托,而不是父类和子类的关系。JavaScript 的 [[Prototype]] 机制本质上就是行为委托机制。也就是说,我们可以选择在 JavaScript 中努 力实现类机制(参见第 4 和第 5 章),也可以拥抱更自然的 [[Prototype]] 委托机制。

当你只用对象来设计代码时,不仅可以让语法更加简洁,而且可以让代码结构更加清晰。

对象关联(对象之前互相关联)是一种编码风格,它倡导的是直接创建和关联对象,不把
它们抽象成类。对象关联可以用基于 [[Prototype]] 的行为委托非常自然地实现。