全文转载自:http://www.blogjava.net/baoyaer/articles/105481.html
每当我们说到 js 的继承时,在您的脑袋的第一反应就是 prototype 原型机制来实现。但是您是否使用过其他的方法来实现继承呢,或者您是否了解其他 实现方式及各种不同的继承实现机制的优缺点呢?
好了,下面我们就来看看几种比较常见的继承实现吧。
1、 prototype方式
var
BaseClass
=
function
()2

3
{4

5
this
.name
=
"
3zfp
"
;6

7
this
.age
=
100
;8

9
this
.ToString
=
function
()
{10

11
return
this
.name
+
"
"
+
this
.age;12

13
}
14

15
}
16

17
var
Derived
=
function
()18

19
{ 20

21
this
.address
=
"
ShenZhen
"
;22

23
}
24

25
Derived.prototype
=
new
BaseClass();26

27
var
instance
=
new
Derived();28

29
instance.ToString();30
这种方式最为简单,只需要让一个类的prototype 为被继承的一个实例就 ok ,然后直接使用 BaseClass 的方法。
prototype 属性是啥意思呢? prototype 即为原型,每一个对象 ( 由 function 定义出来 ) 都有一个默认的原型属性,该属性是个对象类型。 并且该默认属性用来实现链的向上攀查。意思就是说,如果某个对象的属性不存在,那个将通过 prototype 属性对应的对象的来查找该对象的属性。 如果 prototype 查找不到呢? js 会自动地找 prototype 的 prototype 属性对应的对象来查找,这样就通过 prototype 一直往上索引攀查,直到查找到了 该属性或者 prototype 最后为空 ("undefined");
例如:上例中的 instance.ToString() 方法。 js 会先在 instance 实例中查找是否有 ToString() 方法,因为没有,所以查找 Derived.prototype 属性, 而 prototype 为 NewClass 的一个实例,该实例有 ToString() 方法,于是调用成功;同样给 instance 的 name 属性赋值时也是查找 prototype 来实现的。
注意,每一个对象得 prototype 都默认对应一个 object 对象,但是该对象不等于 Object ;如何验证呢?看如下代码:
var
foo
=
function
()
{}
;2

3
var
result
=
(foo.prototype
==
Object);
这段代码的result 所得值为 false;
以下几个需要注意:
typeof
(Object.prototype)
==
"
object
"
;2

3
4

5
typeof
(Object.prototype.prototype)
==
"
undefined
"
;6

7
8

9
var
obj
=
new
Object();10

11
typeof
(obj.prototype)
==
"
undefined
"
;12

13
14

15
var
obj
=
{}
;16

17
typeof
(obj.prototype)
==
"
undefined
"
;18

19
20

21
2 、 apply 方式
var
BaseClass
=
function
()2

3
{4

5
this
.name
=
"
3zfp
"
;6

7
this
.age
=
100
;8

9
this
.ToString
=
function
()
{10

11
return
this
.name
+
"
"
+
this
.age;12

13
}
14

15
}
16

17
var
Derived
=
function
()18

19
{ 20

21
BaseClass.apply(
this
,
new
Array());22

23
this
.address
=
"
ShenZhen
"
;24

25
}
26

27
var
instance
=
new
Derived();28

29
instance.ToString();30

31
32
在这种方式下,我们最需要理解的就是 apply 函数的作用。
该方法普遍的解释为用 A 方法去替换 B 方法。第一个参数为 B 方法的对象本身,第二个参数为一个数组,该数组内的值集合为需要传递给 A 方法对应的 参数列表,如果参数为空,即没有参数传递,则通过 new Array() 来传递, null 无效。
一般的方式为:
但是在本例当中, apply 方法执行了两步操作。
第一 :将 BaseClass 以 apply 传递的 Array 数组作为初始化参数进行实例化。
第二 :将新生成的实例对象的所有属性( name , age , ToString 方法)复制到 instance 实例对象。 这样就实现了继承。
var
foo
=
function
()2

3
{4

5
this
.fooA
=
function
()
{6

7
this
.fooB.apply(
this
,
new
Array(
"
sorry
"
));8

9
}
10

11
this
.fooB
=
function
(str)12

13
{14

15
alert(str);16

17
}
18

19
}
20

21
new
foo().fooA();22
3 、 call+prototype 方式
var
BaseClass
=
function
(name,age)2

3
{4

5
this
.name
=
name;6

7
this
.age
=
age;8

9
this
.ToString
=
function
()
{10

11
return
this
.name
+
"
"
+
this
.age;12

13
}
14

15
}
16

17
var
Derived
=
function
()18

19
{ 20

21
BaseClass.call(
this
,
"
3zfp
"
,
100
);22

23
this
.address
=
"
ShenZhen
"
;24

25
}
26

27
Derived.prototype
=
new
BaseClass();28

29
var
instance
=
new
Derived();30

31
instance.ToString();32

33
其实, call 函数和 apply 方式有很类似的作用,都是用 A 方法去替换 B 方法,但是参数传递不一样, call 方法的第一个参数为 B 方法的对象本身,和面的参数列不用 Array 对象包装,直接依次传递就可以。
为什么作用类似, call 方式的实现机制却要多一条 Derived.prototype = new BaseClass(); 语句呢?那是因为 call 方法只实现了方法的替换而没有作对象属性的复制操作。
例:
var
foo
=
function
()2

3
{4

5
this
.fooA
=
function
()
{6

7
this
.fooB.call(
this
,
"
sorry
"
);8

9
}
10

11
this
.fooB
=
function
(str)12

13
{14

15
alert(str);16

17
}
18

19
}
20

21
new
foo().fooA();22

23
则 this.fooB.call(this,"sorry") 执行了如下几个操作:
this
.temp
=
this
.fooB;2

3
this
.temp(
"
sorry
"
);4

5
delete
(
this
.temp);6
其实,google Map API 的继承就是使用这种方式。大家可以下载的参考参考 (maps.google.com) 。
4 、 prototype.js 中的实现方式
Object.extend
=
function
(destination, source)
{2

3
for
(property
in
source)
{4

5
destination[property]
=
source[property];6

7
}
8

9
return
destination;10

11
}
12

13
var
BaseClass
=
function
(name,age)
{14

15
this
.name
=
name;16

17
this
.age
=
age;18

19
this
.ToString
=
function
()
{20

21
return
this
.name
+
"
"
+
this
.age;22

23
}
24

25
}
26

27
var
Derived
=
function
()28

29
{ 30

31
BaseClass.call(
this
,
"
foo
"
,
100
);32

33
this
.address
=
"
singapore
"
;34

35
this
.ToString
=
function
()
{36

37
var
string
=
Derived.prototype.ToString.call(
this
);38

39
return
string
+
"
"
+
this
.address;40

41
}
42

43
}
44

45
Object.extend(Derived.prototype,
new
BaseClass());46

47
var
instance
=
new
Derived();48

49
document.write(instance.ToString());
该方式,实际上是显式的利用了 apply 的原理来实现继承。先 var temp = new BaseClass() ,再将 temp 的属性遍历复制到 Derived.prototype 中。 for (property in source) 表示遍历某个对象的所有属性。但是私有属性无法遍历。 例 :
var
foo
=
function
()2

3
{4

5
var
innerString
=
""
;6

7
this
.name
=
"
3zfp
"
;8

9
this
.age
=
100
;10

11
function
innerToString()12

13
{14

15
return
innerString;16

17
}
18

19
}
20

21
var
f
=
new
foo();22

23
var
eles
=
""
;24

25
for
(property
in
f)26

27
{28

29
eles
+=
"
"
+
property;30

31
}
32

33
document.write(eles); 34

35
输出为 "name age" 而没有 "innerString" 和 "innerToString()"; 具体原理,以后有机会可以解释(包括私有变量,私有函数,私有函数的变量可 访问性等等)。 上面总结了种种继承方式的实现。但是每种方法都有其优缺点。
第一种方式,如何实现如下需求,需要显示 "3zfp__100";
var
BaseClass
=
function
(name,age)2

3
{4

5
this
.name
=
name;6

7
this
.age
=
age;8

9
this
.ToString
=
function
()
{10

11
return
this
.name
+
"
"
+
this
.age;12

13
}
14

15
}
16

17
var
Derived
=
function
(name,age)18

19
{ 20

21
this
.address
=
"
ShenZhen
"
;22

23
}
24

25
Derived.prototype
=
new
BaseClass();26

27
var
instance
=
new
Derived(
"
3zfp
"
,
100
);28

29
document.write(instance.ToString());30

31
我们通过运行可以发现,实际上输出的是 "undefined__undefined" 。也就是说 name 和 age 没有被赋值。
oh,my god! 天无绝人之路。第二和第三种方法可以实现,具体如下:
var
BaseClass
=
function
(name,age)2

3
{4

5
this
.name
=
name;6

7
this
.age
=
age;8

9
this
.ToString
=
function
()
{10

11
return
this
.name
+
"
"
+
this
.age;12

13
}
14

15
}
16

17
var
Derived
=
function
(name,age)18

19
{ 20

21
BaseClass.apply(
this
,
new
Array(name,age));22

23
this
.address
=
"
ShenZhen
"
;24

25
}
26

27
var
instance
=
new
Derived(
"
3zfp
"
,
100
);28

29
document.write(instance.ToString());30

31
______________________________________________32

33
---------------------------------------------------------------------
34

35
var
BaseClass
=
function
(name,age)36

37
{38

39
this
.name
=
name;40

41
this
.age
=
age;42

43
this
.ToString
=
function
()
{44

45
return
this
.name
+
"
"
+
this
.age;46

47
}
48

49
}
50

51
var
Derived
=
function
(name,age)52

53
{ 54

55
BaseClass.call(
this
,name,age);56

57
this
.address
=
"
ShenZhen
"
;58

59
}
60

61
Derived.prototype
=
new
BaseClass();62

63
var
instance
=
new
Derived(
"
3zfp
"
,
100
);64

65
66

67
document.write(instance.ToString());68

69
70
但是用 apply 方法也还是有缺点的,为什么?在 js 中,我们有个非常重要的运算符就是 "instanceof" ,该运算符用来比较某个对向是否为某种类型。 对于继承,我们除了是属于 Derived 类型,也应该是 BaseClass 类型,但是。 apply 方式返回值为 false( (instance instanceof BaseClass) == false). 由于 prototype.js 使用了类似 apply 的方式,所以也会出现这个问题。
啊,终极方法就是 call+prototype 方式了,还是 google 牛 X 。您可以试一下是否正确 ((instance instanceof BaseClass) == true) 。
最后,就是多重继承了,由于 js 中 prototype 只能对应一个对象,因此无法实现真正意义上的多重继承。有一个 js 库模拟了多重继承, 但是该库也额外重写了 instanceOf 方法,用 _instanceOf 和 _subclassOf 函数来模拟判断。该库的名字叫 modello.js ,感兴趣的可以搜索下载。
513

被折叠的 条评论
为什么被折叠?



