JavaScript高级编程学习9——继承机制

      同声明对象一样,实现JavaScript继承机制主要是通过模仿实现的。主要有以下这几种机制的实现。

      1.对象冒充:其主要实现的原理就是让子类调用父类的构造方法。这个主要是利用this的原理实现的,在采用构造函数的方式声明对象时,this关键字指的是刚new出来对象,但是我们不要忘记了,如果将构造函数作为一个函数来调用的话,构造函数里面的this就是这个调用方了。于是我们在子类的构造函数里面来调用父类的构造函数。从而实现将父类的属性和方法都传递给子类。

       首先回顾下前面的代码:

View Code
1 function Person(name, age) {
2 this._name = name;
3 this._age = age;
4 this.ShowInfo = function () {
5 document.write(this._name + this._age);
6 }
7 }
8 var person = new Person('wuxq', 24);//这时构造函数里面的this是刚new出来的对象person
9 person.ShowInfo();

      而使用对象冒充的代码是:

View Code
1 //接下来实现继承
2 function ITPerson(age, name) {
3 this._newMethod = Person;
4 this._newMethod(age, name);//注意这时Person函数里面的this指的是调用Person的对象,即ITPerson
5 delete this._newMethod; //在定义子类的新属性和新方法之前必须先删除_newMethod,否则可能会覆盖父类的属性和方法
6 this.ShowProgrammer = function () {
7 document.write('i love javascript');
8 }
9 }
10 var itPerson = new ITPerson('wuxiaoqian', 24);
11 itPerson.ShowInfo();
12 itPerson.ShowProgrammer();

     上面实现的就是通过对象冒充来实现ECMAScript的下继承,通过对象冒充我们还可以实现多重继承,但是多重继承要注意一点,就是如果多个类具有相同的属性或者相同方法,则最后一个继承的类是优先级最高的。如下代码:

View Code
1 function ClassA(name) {
2 this._name = name;
3 this.ShowInfo = function () {
4 document.write('It is ClassA');
5 }
6
7 }
8 function ClassB(name) {
9 this._name = name;
10 this.ShowInfo = function () {
11 document.write('It is ClassB');
12 }
13 }
14 function ClassC() {
15 this._newMethod = ClassA;
16 this._newMethod('ClassA');
17 delete this._newMethod;
18 this._newMethod = ClassB;
19 this._newMethod('ClassB');
20 delete this._newMethod;
21 this.ShowInfo2 = function () {
22 document.write(this._name);
23 }
24 }
25 var classC = new ClassC('ClassC');
26 classC.ShowInfo(); //输出ClassB
27 classC.ShowInfo2(); //输出ClassB

      在ECMAScript的第三版,对每个函数对象引入了两个新的方法call和apply。首先来介绍call方法,call方法和经典的对象冒充是一致的,因为他的第一个参数代表函数中的this对象,而接下来的参数就是直接传递给函数对象。而apply是采用两个参数,第一个参数是用this,而第二个参数是用于传递给函数的数组。以下是实现的代码。

View Code
1 //用call方法来实现上面的代码
2 function ITPerson(age, name) {
3 Person.call(this, age, name);
4 }
5 var itPerson = new ITPerson('wuxiaoqian', 24);
6 document.write("-----调用call方法-----");
7 itPerson.ShowInfo();
8 //用apply方法实现上面的代码
9 function ITPerson(name, age) {
10 Person.apply(this,new Array(name,age));
11 }
12 var itPerson = new ITPerson('wuxiaoqian', 24);
13 document.write("-----调用apply方法-----");
14 itPerson.ShowInfo();

      2.原型链:原型链就是将父类的实例赋值给子类的prototype,函数的默认原型是object对象的,而object对象的原型是为null。使用原型链要注意,构造函数不能带参数,而且子类的新属性或者新的方法要在重新赋值之后再定义,因为  ITPerson.prototype = new Person();是将ITPerson的原型的引用指向了另外一个地址,这也是原型链的实质。

View Code
1 //用原型链实现
2 function Person() {
3 }
4 Person.prototype.name = 'wuxq';
5 Person.prototype.age = '24';
6 Person.prototype.ShowInfo = function () {
7 document.write(this.name+this.age);
8 }
9 function ITPerson() {
10
11 }
12 ITPerson.prototype = new Person();
13 ITPerson.prototype.ShowLanguage = function () {
14 document.write('C#');
15 }
16 var itPerson = new ITPerson();
17 itPerson.ShowInfo();
18 itPerson.ShowLanguage();

     3.混合方式:混合方式就是利用对象冒充继承构造函数的属性,用原型链继承prototype对象的方法。实现代码如下。

View Code
1 //用混合方式实现
2 function Person(name, age) {
3 this._name = name;
4 this._age = age;
5 }
6 Person.prototype.ShowInfo = function () {
7 document.write(name+age);
8 }
9 function ITPerson(name, age) {
10 Person.call(this, name, age); //这里的this指的是ITPerson
11 }
12 ITPerson.prototype = new Person();

      4.关于动态原型方式的探讨:在前面我们讲过可以使用动态原型的方式,实现的prototype的声明放入到类中,我们在实现继承的时候也可以这样实现吗?我们是否可以编写下面的代码。

View Code
1 //用动态原型实现
2 function ITPerson(name, age) {
3 Person.call(this, name, age);
4 if (typeof ITPerson._initialized == 'undefine') {
5 ITPerson.prototype = new Person();//这里有错误
6 ITPerson.prototype.ShowLanguage = function () {
7 document.write('C#');
8 }
9 }
10 ITPerson._initialized = true;
11 }

       答案是否定的,因为 ITPerson.prototype = new Person();这一句是将Person的原型引用指向另外一个地址,而我们在创建第一个实例的时候,var itPerson=new ITPerson('wuxq',24)这时在上面这句代码运行前对象已经实例化了。虽然在这个对象实例化之后我们可以实现将ITPerson的prototype的引用修改,但是这时的改变已经不能第一个对象。因此第一个对象是错误的。因此我们可以得出结论,在继承机制下,是不能利用动态原型的。

posted @ 2011-03-16 22:55  雁北飞  阅读(162)  评论(0编辑  收藏  举报