2016-12-13 JavaScript 中几种不同的基于 prototype 继承方式的区别

JavaScript 中几种不同的基于 prototype 继承方式的区别

普通属性的继承

第一种方式

1
function Employee1 (name, dept) {
2
console.log('Employee constructor')
3
this.name = name || "";
4
this.dept = dept || "general";
5
this.work = function() {console.log("Employee.work")}
6
this.workAsEmployee = function() { this.work() }
7
}
8
9
function WorkerBee1 (projs) {
10
console.log('WorkerBee constructor')
11
this.projects = projs || [];
12
this.work = function() {console.log("WorkerBee.work")}
13
this.workAsBee = function() { this.work() }
14
}
15
WorkerBee1.prototype = new Employee1;
16
17
function Engineer1 (mach) {
18
console.log('Engineer constructor')
19
this.dept = "engineering";
20
this.machine = mach || "";
21
this.work = function() {console.log("Engineer.work")}
22
this.workAsEngineer = function() { this.work() }
23
}
24
25
Engineer1.prototype = new WorkerBee1;
26
27
e1 = new Engineer1()
28
e1.work()
29
console.log(Employee1.prototype.isPrototypeOf(e1))
30
console.log(WorkerBee1.prototype.isPrototypeOf(e1))
31
console.log(e1)
Copied!

第二种方式

来自于 YUI 的实现,利用中间对象传递 prototype
1
function extend(Child, Parent) {
2
var F = function () { };
3
F.prototype = Parent.prototype;
4
Child.prototype = new F();
5
Child.prototype.constructor = Child;
6
Object.defineProperty(Child, "super", { "value": Parent.prototype })
7
}
8
9
function Employee2 (name, dept) {
10
console.log('Employee constructor')
11
this.name = name || "";
12
this.dept = dept || "general";
13
this.work = function() {console.log("Employee.work")}
14
this.workAsEmployee = function() { this.work() }
15
}
16
17
function WorkerBee2 (projs) {
18
console.log('WorkerBee constructor')
19
this.projects = projs || [];
20
this.work = function() {console.log("WorkerBee.work")}
21
this.workAsBee = function() { this.work() }
22
}
23
extend(WorkerBee2, Employee2);
24
25
function Engineer2 (mach) {
26
console.log('Engineer constructor')
27
this.dept = "engineering";
28
this.machine = mach || "";
29
this.work = function() {console.log("Engineer.work")}
30
this.workAsEngineer = function() { this.work() }
31
}
32
extend(Engineer2, WorkerBee2);
33
34
e2 = new Engineer2()
35
e2.work()
36
37
console.log(Employee2.prototype.isPrototypeOf(e2))
38
console.log(WorkerBee2.prototype.isPrototypeOf(e2))
39
console.log(e2)
Copied!
可以看到,区别主要在于,直接 Child.prototype = new Parent() 会把定义在 Parent 里面的方法也带到 prototype 里面去。另外,这种方式并没有执行父类的构造函数。

对于定义在 prototype 里面的方法呢

下面对上面的方法定义进行一点改进,把方法定义在 prototype 里,类似正常的 OO 编程中在类里面定义方法。

第一种方式改进

1
function Employee3 (name, dept) {
2
console.log('Employee constructor')
3
this.name = name || "";
4
this.dept = dept || "general";
5
}
6
Employee3.prototype.work = function() {console.log("Employee.work")}
7
Employee3.prototype.workAsEmployee = function() { this.work() }
8
9
function WorkerBee3 (projs) {
10
console.log('WorkerBee constructor')
11
this.projects = projs || [];
12
}
13
WorkerBee3.prototype = new Employee3;
14
WorkerBee3.prototype.work = function() {console.log("WorkerBee.work")}
15
WorkerBee3.prototype.workAsBee = function() { this.work() }
16
17
function Engineer3 (mach) {
18
console.log('Engineer constructor')
19
this.dept = "engineering";
20
this.machine = mach || "";
21
}
22
Engineer3.prototype = new WorkerBee3;
23
Engineer3.prototype.work = function() {console.log("Engineer.work")}
24
Engineer3.prototype.workAsEngineer = function() { this.work() }
25
26
e3 = new Engineer3()
27
e3.work()
28
29
console.log(Employee3.prototype.isPrototypeOf(e3))
30
console.log(WorkerBee3.prototype.isPrototypeOf(e3))
31
console.log(e3)
Copied!

第二种方式的改进

1
function extend(Child, Parent) {
2
var F = function () { };
3
F.prototype = Parent.prototype;
4
Child.prototype = new F();
5
Child.prototype.constructor = Child;
6
Object.defineProperty(Child, "super", { "value": Parent.prototype })
7
}
8
9
function Employee4 (name, dept) {
10
console.log('Employee constructor')
11
this.name = name || "";
12
this.dept = dept || "general";
13
}
14
Employee4.prototype.work = function() {console.log("Employee.work")}
15
Employee4.prototype.workAsEmployee = function() { this.work() }
16
17
function WorkerBee4 (projs) {
18
console.log('WorkerBee constructor')
19
this.projects = projs || [];
20
21
}
22
extend(WorkerBee4, Employee4);
23
WorkerBee4.prototype.work = function() {console.log("WorkerBee.work")}
24
WorkerBee4.prototype.workAsBee = function() { this.work() }
25
26
function Engineer4 (mach) {
27
console.log('Engineer constructor')
28
this.dept = "engineering";
29
this.machine = mach || "";
30
}
31
extend(Engineer4, WorkerBee4);
32
Engineer4.prototype.work = function() {console.log("Engineer.work")}
33
Engineer4.prototype.workAsEngineer = function() { this.work() }
34
35
e4 = new Engineer4()
36
e4.work()
37
38
console.log(Employee4.prototype.isPrototypeOf(e4))
39
console.log(WorkerBee4.prototype.isPrototypeOf(e4))
40
console.log(e4)
Copied!
注意观察 constructor__proto__ 属性。

要执行所有构造函数

上述第二种方法,都没有执行父类的构造函数,也就没有真正的继承父类的初始化数据。为了弥补这一点,如下两种写法都可以达到目的。

利用 super 变量

1
function extend(Child, Parent) {
2
var F = function () { };
3
F.prototype = Parent.prototype;
4
Child.prototype = new F();
5
Child.prototype.constructor = Child;
6
}
7
8
function Employee5 (name, dept) {
9
console.log('Employee constructor')
10
this.name = name || "";
11
this.dept = dept || "general";
12
}
13
Employee5.prototype.work = function() {console.log("Employee.work")}
14
Employee5.prototype.workAsEmployee = function() { this.work() }
15
16
function WorkerBee5 (projs) {
17
console.log('WorkerBee constructor')
18
19
this.super = Employee5;
20
this.super();
21
22
this.projects = projs || [];
23
24
}
25
extend(WorkerBee5, Employee5);
26
WorkerBee5.prototype.work = function() {console.log("WorkerBee.work")}
27
WorkerBee5.prototype.workAsBee = function() { this.work() }
28
29
function Engineer5 (mach) {
30
console.log('Engineer constructor')
31
32
this.super = WorkerBee5;
33
this.super();
34
35
this.dept = "engineering";
36
this.machine = mach || "";
37
}
38
extend(Engineer5, WorkerBee5);
39
Engineer5.prototype.work = function() {console.log("Engineer.work")}
40
Engineer5.prototype.workAsEngineer = function() { this.work() }
41
42
e5 = new Engineer5()
43
e5.work()
44
45
console.log(Employee5.prototype.isPrototypeOf(e5))
46
console.log(WorkerBee5.prototype.isPrototypeOf(e5))
47
console.log(e5)
Copied!

类似,但是用 Parent.apply 方法

1
function extend(Child, Parent) {
2
var F = function () { };
3
F.prototype = Parent.prototype;
4
Child.prototype = new F();
5
Child.prototype.constructor = Child;
6
}
7
8
function Employee6 (name, dept) {
9
console.log('Employee constructor')
10
this.name = name || "";
11
this.dept = dept || "general";
12
}
13
Employee6.prototype.work = function() {console.log("Employee.work")}
14
Employee6.prototype.workAsEmployee = function() { this.work() }
15
16
function WorkerBee6 (projs) {
17
console.log('WorkerBee constructor')
18
Employee6.apply(this)
19
this.projects = projs || [];
20
}
21
extend(WorkerBee6, Employee6);
22
WorkerBee6.prototype.work = function() {console.log("WorkerBee.work")}
23
WorkerBee6.prototype.workAsBee = function() { this.work() }
24
25
function Engineer6 (mach) {
26
console.log('Engineer constructor')
27
WorkerBee6.apply(this)
28
this.dept = "engineering";
29
this.machine = mach || "";
30
}
31
extend(Engineer6, WorkerBee6);
32
Engineer6.prototype.work = function() {console.log("Engineer.work")}
33
Engineer6.prototype.workAsEngineer = function() { this.work() }
34
35
e6 = new Engineer6()
36
e6.work()
37
38
console.log(Employee6.prototype.isPrototypeOf(e6))
39
console.log(WorkerBee6.prototype.isPrototypeOf(e6))
40
console.log(e6)
Copied!
最近更新 1yr ago