You Don't Know JS上卷Part2 Chapter4.混合对象“类”

4.1 混入

  • 混入用来模拟类的复制行为。
4.1.1 显式混入
  • 混入处理的已经不再是类了,因为在JS中不存在类,VehicleCar都是对象,进行复制和粘贴。
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
28
29
30
31
32
33
34
// 非常简单的mixin(..)例子:
function mixin( sourceObj, targetObj ) {
for(var key in sourceObj) {
// 只会在不存在的情况下复制
if(!(key in targetObj)) {
targetObj[key] = sourceObj[key];
}
}
return targetObj;
}

var Vehicle = {
engines: 1,

ignition: function() {
console.log( "Turning on my engine." );
},

drive: function() {
this.ignition();
console.log( "Steering and moving forward!" );
}
};

var Car = mixin( Vehicle, {
wheels: 4,

drive: function() {
Vehicle.drive.call( this ); // 显示多态(伪多态)调用,因为Vehicle和Car中都有drive
console.log(
"Rolling on all" + this.wheels + "wheels!"
);
}
} );
  • 另一种混入模式,先复制然后对Car进行特殊化,可以跳过存在性检查。不如第一种方法常用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 可能有重写风险
function mixin( sourceObj, targetObj ) {
for(var key in sourceObj) {
targetObj[key] = sourceObj[key];
}
return targetObj;
}

var Vehicle = {
// ...
};

// 首先创建一个空对象并把Vehicle的内容复制进去
var Car = mixin( Vehicle, { });

// 然后把新内容复制到Car中
mixin( {
wheels: 4,

drive: function() {
// ...
}
}, Car );
  • 寄生继承,既是显式的又是隐式的
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
28
29
30
31
32
33
34
35
36
37
38
// “传统的JS类” Vehicle
function Vehicle() {
this.engines = 1;
}
Vehicle.prototype.ignition = function() {
console.log( "Turning on my engine." );
};
Vehicle.prototype.drive = function() {
this.ignition();
console.log( "Steering and moving forward!" );
}

// "寄生类"Car
function Car() {
// 首先,car是一个Vehicle
var car = new Vehicle();

// 接着我们对car进行定制
car.wheels = 4;

// 保存到Vehicle::drive()的特殊引用
var vehDrive = car.drive();

// 重写Vehicle::drive()
car.drive = function() {
vehDrive.call( this );
console.log(
"Rolling on all " + this.wheels + " wheels!"
);
}
return car;
}

var myCar = new Car();
myCar.drive();
// Turning on my engine.
// Steering and moving forward!
// Rolling on all 4 wheels!

调用new Car()时会创建一个新对象并绑定到Car的this上,上述代码没有使用这个新对象而是返回了我们自己的car对象,最初创建的这个对象会被丢弃。因此可以不使用new关键字调用Car()。结果是一致的,但是可以避免创建并丢弃多余的对象。

4.1.2 隐式混入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var Something = {
cool: function() {
this.greeting = "Hello World";
this.count = this.count ? this.count + 1 : 1;
}
};

Something.cool();
Something.greeting; // "Hello World"
Something.count; // 1

var Another = {
cool: function() {
// 隐式把Something混入Another
Something.cool.call( this );
}
};

Another.cool();
Another.greeting; // "Hello World"
Another.count; // 1 (count不是共享状态)