第八章 8.5 面向对象的 JavaScript (Object-Oriented JavaScript)

虽然 JavaScript 支持我们所谓的对象(object),但他没有正式的“类”(class) 这个提法。这使得他和传统的面向对象语言比如 C++ 以及 Java 有些区别。面向对象的语言一个共有的概念就是强类型,支持基于类的继承。而 JavaScript 不具备。
另一方面,JavaScript 大量使用对象,有基于原型的继承方法, JavaScript 又是一个真正的面向对象语言。
此处省去若干字regular_smile.gif

在 Java 中,有些普遍的命名约定是值得我们写 JavaScript 程序时沿用的。比如:类以大写字母开头,而变量或对象实例以小写字母开头。

在 Java 中,类的成员大致分为四种类型:实例属性(instance properties), 实例方法(instance methods), 类属性(class properties), 类方法(class methods). 下面我们来逐一探讨。

8.5.1 Instance Properties

JavaScript 中任何 object property 都是 instance property. 比如那前面圆的例子来说:

None.gif c.r

8.5.2 Instance Methods

例子:
None.gif =  c.area();

8.5.3 Class Properties

如:
None.gif Number.MAX_VALUE

我们可以通过给 constructor 添加属性,就可以简单的实现 class property :
None.gif Circle.PI  =   3.14 ;


因为在 JavaScript 中 function 也是 object. 因此可以附加属性。

8.5.4 Class Methods.

和 Class Property 一样,也是全局的。例子如 Date.parse() 方法。因为是通过类名称而不是对象实例被调用的,Class Method 里是不能用 this 的。实现方法就是类似的向 constructor 添加属性方法就可以了。

8.5.5 例子:

None.gif function  Circle(radius) {    //  The constructor defines the class itself.
None.gif
     //  r is an instance property, defined and initialized in the constructor.
None.gif
     this .r  =  radius;
None.gif}
None.gif
None.gif
None.gif
//  Circle.PI is a class property--it is a property of the constructor function.
None.gif
Circle.PI  =   3.14159 ;
None.gif
None.gif
None.gif
//  Here is a function that computes a circle's area.
None.gif
function  Circle_area(  ) {  return  Circle.PI  *   this .r  *   this .r; }
None.gif
None.gif
None.gif
//  Here we make the function into an instance method by assigning it
None.gif//
 to the prototype object of the constructor.
None.gif//
 Note: with JavaScript 1.2, we can use a function literal to
None.gif//
 define the function without naming it Circle_area.
None.gif
Circle.prototype.area  =  Circle_area;
None.gif
None.gif
None.gif
//  Here's another function. It takes two Circle objects as arguments and
None.gif//
 returns the one that is larger (i.e., has the larger radius).
None.gif
function  Circle_max(a,b) {
None.gif    
if  (a.r  >  b.r)  return  a;
None.gif    
else   return  b;
None.gif}
None.gif
None.gif
None.gif
//  Since this function compares two Circle objects, it doesn't make sense as
None.gif//
 an instance method operating on a single Circle object. But we don't want
None.gif//
 it to be a standalone function either, so we make it into a class method
None.gif//
 by assigning it to the constructor function:
None.gif
Circle.max  =  Circle_max;
None.gif
None.gif
None.gif
//  Here is some code that uses each of these fields:
None.gif
var  c  =   new  Circle( 1.0 );       //  Create an instance of the Circle class
None.gif
c.r  =   2.2 ;                     //  Set the r instance property
None.gif
var  a  =  c.area();              //  Invoke the area(  ) instance method
None.gif
var  x  =  Math.exp(Circle.PI);   //  Use the PI class property in our own computation
None.gif
var  d  =   new  Circle( 1.2 );       //  Create another Circle instance
None.gif
var  bigger  =  Circle.max(c,d);  //  Use the max(  ) class method

8.5.6 例子:复数类

下面的例子用了 JavaScript 1.2 及更高版本才有的 function literal 特性。因此不需要为了兼容 1.1 而在调用 prototype 之前调用一次 new 操作。

None.gif /*
None.gif * Complex.js:
None.gif * This file defines a Complex class to represent complex numbers.
None.gif * Recall that a complex number is the sum of a real number and an
None.gif * imaginary number and that the imaginary number i is the
None.gif * square root of -1.
None.gif 
*/
None.gif
None.gif
None.gif
/*
None.gif * The first step in defining a class is defining the constructor
None.gif * function of the class. This constructor should initialize any
None.gif * instance properties of the object. These are the essential
None.gif * "state variables" that make each instance of the class different.
None.gif 
*/
None.gif
function  Complex(real, imaginary) {
None.gif    
this .x  =  real;        //  The real part of the number
None.gif
     this .y  =  imaginary;   //  The imaginary part of the number
None.gif
}
None.gif
None.gif
None.gif
/*
None.gif * The second step in defining a class is defining its instance
None.gif * methods (and possibly other properties) in the prototype object
None.gif * of the constructor. Any properties defined in this object will
None.gif * be inherited by all instances of the class. Note that instance
None.gif * methods operate implicitly on the this keyword. For many methods,
None.gif * no other arguments are needed.
None.gif 
*/
None.gif
None.gif
None.gif
//  Return the magnitude of a complex number. This is defined
None.gif//
 as its distance from the origin (0,0) of the complex plane.
None.gif
Complex.prototype.magnitude  =   function (  ) {
None.gif    
return  Math.sqrt( this .x * this .x  +   this .y * this .y);
None.gif};
None.gif
None.gif
None.gif
//  Return a complex number that is the negative of this one.
None.gif
Complex.prototype.negative  =   function (  ) {
None.gif    
return   new  Complex( - this .x,  - this .y);
None.gif};
None.gif
None.gif
None.gif
//   Convert a Complex object to a string in a useful way.
None.gif//
  This is invoked when a Complex object is used as a string.
None.gif
Complex.prototype.toString  =   function (  ) {
None.gif    
return   " { "   +   this .x  +   " , "   +   this .y  +   " } " ;
None.gif};
None.gif
None.gif
None.gif
//  Return the real portion of a complex number. This function
None.gif//
 is invoked when a Complex object is treated as a primitive value.
None.gif
Complex.prototype.valueOf  =   function (  ) {  return   this .x; }
None.gif
None.gif
None.gif
/*
None.gif * The third step in defining a class is to define class methods,
None.gif * constants, and any needed class properties as properties of the
None.gif * constructor function itself (instead of as properties of the
None.gif * prototype object of the constructor). Note that class methods
None.gif * do not use the this keyword: they operate only on their arguments.
None.gif 
*/
None.gif
None.gif
None.gif
//  Add two complex numbers and return the result.
None.gif
Complex.add  =   function  (a, b) {
None.gif    
return   new  Complex(a.x  +  b.x, a.y  +  b.y);
None.gif};
None.gif
None.gif
None.gif
//  Subtract one complex number from another.
None.gif
Complex.subtract  =   function  (a, b) {
None.gif    
return   new  Complex(a.x  -  b.x, a.y  -  b.y);
None.gif};
None.gif
None.gif
None.gif
//  Multiply two complex numbers and return the product.
None.gif
Complex.multiply  =   function (a, b) {
None.gif    
return   new  Complex(a.x  *  b.x  -  a.y  *  b.y,
None.gif                       a.x 
*  b.y  +  a.y  *  b.x);
None.gif};
None.gif
None.gif
None.gif
//  Here are some useful predefined complex numbers.
None.gif//
 They are defined as class properties, where they can be used as
None.gif//
 "constants." (Note, though, that they are not actually read-only.)
None.gif
Complex.zero  =   new  Complex( 0 , 0 );
None.gifComplex.one 
=   new  Complex( 1 , 0 );
None.gifComplex.i 
=   new  Complex( 0 , 1 );
None.gif

8.5.7 父类和子类(Superclasses and Subclasses)

在 Java, C++ 等 OO 语言中,都有严格的类层次结构。在 JavaScript 中,使用基于原型扩展的继承方法代替了基于类的继承。类似的继承体系也是可以实现的。比如,Object 类是最普通的,也就是最基础的类。而其他的都是他的一些特殊的版本或者说子类。可以说 Object 是所有内建的子类的父类。

我们已经知道了对象通过其 constructor 的原型来继承属性。那么他们如何继承的 Object 类的属性呢?别忘了,prototype 对象本身也是一个 Object, 是通过 Object() 这个 constructor 创建的。这说明 prototype 对象自身从 Object.prototype 继承属性!
所以,上面例子里提到的 Complex 类,会从 Complex.prototype 继承属性;而 Complex.prototype 本身又从 Object.prototype 继承属性。因此实际上 Complex 类是从两者都继承属性。当你要查找一个 Complex 对象的属性时,首先被查找的是对象本身,如果找不到,然后是 Complex.prototype, 如果还找不到,最后是 Object.prototype.

需要注意的是,因为 Complex.prototype 在 Object.prototype 之前被搜索,所以前者的属性会覆盖或者隐藏后者的同名属性。比如 toString() 就会覆盖掉。

以上展示的都是典型的 JavaScript 编程方法。通常来说,不太可能需要创建一个复杂的类库体系。当需要的时候,我们也可以创建这样的子类。具体方法就是将子类的 prototype 设置为父类的一个实例就可以了。例子如下:

None.gif //  This is the constructor for the subclass.
None.gif
function  MoreComplex(real, imaginary) {
None.gif    
this .x  =  real;
None.gif    
this .y  =  imaginary;
None.gif}
None.gif
None.gif
None.gif
//  We force its prototype to be a Complex object. This means that
None.gif//
 instances of our new class inherit from MoreComplex.prototype,
None.gif//
 which inherits from Complex.prototype, which inherits from
None.gif//
 Object.prototype.
None.gif
MoreComplex.prototype  =   new  Complex( 0 , 0 );
None.gif
None.gif
None.gif
//  Now add a new method or other new features to this subclass.
None.gif
MoreComplex.prototype.swap  =   function (  ) {
None.gif    
var  tmp  =   this .x;
None.gif    
this .x  =   this .y;
None.gif    
this .y  =  tmp;
None.gif
None.gif

这样做有一个缺点:
因为明确指定了 MoreComplex.prototype 的原型对象,我们覆盖了 JavaScript 提供的默认的那个原型对象,同时丢弃了那个 constructor 属性。这个时候 MoreComplex 类的 constructor 属性是从父类继承来的,而不是他自己应该有的那个。一个解决办法是明确的去指定该属性:

None.gif MoreComplex.prototype.constructor  =  MoreComplex; 

注意:在 JavaScript 1.1 中 constructor 属性是只读的,不能像上面这样设置。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值