《JavaScript高级程序设计》学习笔记(继承)

来源:互联网 发布:网上虚拟充值淘宝原理 编辑:程序博客网 时间:2024/05/29 11:07

《JavaScript高级程序设计》学习笔记(继承) 

继承

要用ECMAScript实现继承机制,首先从基类入手。
所有开发者定义的类都可作为基类。出于安全原因,本地类和宿主类不能作为基类。

选定基类后,就可以创建它的子类了。是否使用基类完全由你决定。
抽象类:只用于给子类提供通用函数的不能直接使用的基类。

创建的子类将继承超类的所有属性和方法,包括构造函数及方法的实现。
注意:所有属性和方法都是公用的,因此子类可直接访问这些方法。
子类还可添加超类中没有的新属性和方法,也可以覆盖超类中的属性和方法。

 

继承的方式

ECMAScript中有多种继承方式,因为JavaScript中的继承机制并不是明确规定的,

而是通过模仿实现的。这意味着所有的继承细节并非由解释程序处理。作为开发者,你有权决定最适用的继承方式。


1. 对象冒充

原理:构造函数使用this关键字给所有属性和方法赋值(即采用类声明的构造函数方式)。
因为构造函数只是一个函数,所以可使ClassA的构造函数成为ClassB的方法,然后调用它。
例如:
function ClassA(col){
 this.color = col;
 this.show = function(){alert(this.color);};
}

function ClassB(col,n){
 this.newMethod = ClassA;
 this.newMethod(col);
 delete this.newMethod;

 this.n = n;
 this.show2 = function(){alert(this.n);}; 
}

var obja = new ClassA('red');
var objb = new ClassB('blue',5);
obja.show();//'red'
objb.show();//'blue'
objb.show2();//'5'

所有的新属性和新方法都必须在删除了新方法的代码行后定义。否则,可能会覆盖超类的相关属性和方法。

对象冒充可以支持多重继承,即一个类可以继承多个超类。
例如:
已经有类ClassX和ClassY
function ClassZ(){
 this.newMethod = ClassX;
 this.newMethod();
 delete this.newMethod;

 this.newMethod = ClassY;
 this.newMethod();
 delete this.newMethod;
}
这里存在一个弊端,如果ClassX和ClassY具有同名的属性或方法,ClassY具有高优先级,因为继承的是最后的类。


2. call()方法

call()方法是与经典的对象冒充方法最相似的方法。
它的第一个参数用作this的对象。其他参数都直接传递给函数自身。
例如:
function ClassA(col){
 this.color = col;
 this.show = function(){alert(this.color);};
}
var obj = new Object;
ClassA.call(obj,"red")
obj.show();

结合对象冒充方法,只需将前三行的赋值、调用和删除代码替换即可
例如:
function ClassA(col){
 this.color = col;
 this.show = function(){alert(this.color);};
}

function ClassB(col,n){
 //this.newMethod = ClassA;
 //this.newMethod(col);
 //delete this.newMethod;
 ClassA.call(this,col)

 this.n = n;
 this.show2 = function(){alert(this.n);}; 
}


3. apply()方法

apply()方法有两个参数,用作this的对象和要传递给函数的参数的数组。
例如:
function ClassA(col1,col2){
 this.show = function(){alert(col1+' '+col2)};
}
var obj = new Object;
ClassA.apply(obj,new Array("red","blue"));
obj.show();

结合对象冒充方法,只需将前三行的赋值、调用和删除代码替换即可
例如:
function ClassA(col1,col2){
 this.show = function(){alert(col1+' '+col2)};
}

function ClassB(col1,col2,n){
 //this.newMethod = ClassA;
 //this.newMethod(col);
 //delete this.newMethod;
 ClassA.apply(obj,new Array(col1,col2));

 this.n = n;
 this.show2 = function(){alert(this.n);}; 
}

同样的,第一个参数仍是this。第二个参数是只有一个值color的数组。
可以把ClassB的整个arguments对象作为第二个参数传递给apply()方法
例如:
function ClassA(col1,col2){
 this.show = function(){alert(col1+' '+col2)};
}

function ClassB(col1,col2,n){
 //this.newMethod = ClassA;
 //this.newMethod(col);
 //delete this.newMethod;
 ClassA.apply(obj,arguments);

 this.n = n;
 this.show2 = function(){alert(this.n);}; 
}

只有超类中的参数顺序与子类中的参数顺序完全一致时才可以用arguments传递参数对象。
如果不是,就必须创建一个单独的数组,按照正确的顺序放置参数。此外,还可使用call()方法。


4. 原型链

prototype对象的任何属性和方法都被传递给那个类的所有实例。
原型链利用这种功能来实现继承机制。
例如:
function ClassA(){}
ClassA.prototype.color = "red";
ClassA.prototype.show = function(){alert(this.color);};

function ClassB(){}
ClassB.prototype = new ClassA();//把ClassB的prototype属性设置成ClassA的实例

var obj = new ClassB();
obj.show();

注意:调用ClassA的构造函数时,没有给它传递参数。这在原型链中是标准做法。要确保构造函数没有任何参数。

注意:子类的所有属性和方法都必须出现在prototype属性被赋值后,因为prototype属性被替换成新对象后,原始对象将被销毁。
例如:
function ClassA(){}
ClassA.prototype.color = "red";
ClassA.prototype.show = function(){alert(this.color);};

function ClassB(){}
ClassB.prototype = new ClassA();
ClassB.prototype.show2 = function(){alert('2');};

var obj = new ClassB();
obj.show();
obj.show2();

在原型链中,对ClassB的所有实例,instanceof为ClassA和ClassB都返回true。
例如:
alert(obj instanceof ClassA);
alert(obj instanceof ClassB);

在ECMAScript的弱类型世界中,这是极其有用的工具,不过使用对象冒充时不能使用它。
原型链的弊端是不支持多重继承。记住,原型链会用另一类型的对象重写类的prototype属性。


5. 混合方式

用对象冒充继承构造函数的属性,用原型链继承prototype对象的方法。
例如:
function ClassA(col){
 this.color = col;
}
ClassA.prototype.show = function(){alert(this.color);};

function ClassB(col){
 ClassA.call(this,col)
}
ClassB.prototype = new ClassA();

var obj = new ClassB('red');
obj.show();
alert(obj instanceof ClassA);
alert(obj instanceof ClassB);

用对象冒充继承ClassA类的color属性,用原型链继承ClassA类的方法。
由于这种混合方式使用了原型链,所以instanceof运算符都返回true。

原创粉丝点击