关闭

Simulating class in JavaScript - 5

138人阅读 评论(0) 收藏 举报
9.3.7 Private Members

 像Java和C++这些传统的面向对象语言的一个普遍的特点就是类的属性是可以被申明成私有的为的就是他们仅仅对于类的方法是可用的,且不能被外部的代码执行。数据封装这种普遍的编程技巧使得属性私有且允许仅仅通过特别的存取方法才能访问他们。JavaScript可以用闭包模拟这种。但是这样做,存取方法必须是存储在每个对象实例;他们不能够继承于prototype对象。

      接下来的代码说明这是怎么做到的。它执行一个不可改变的Rectangle对象,它的宽和高是永远不会被改变且仅对于存取方法是有用的。


      

function ImmutableRectangle(w, h) {

    //This constructor does not store the width and height properties
    //in the object it initializes. Instead, it simply defines
    //accessor methods in the object. These methods are closures and
    //the width and height values are captured in their scope chain.
    this.getWidth = function () { return w; }
    this.getHeight = function () { return h; }

}

//Note that the class can have regular methods in prototype object.
ImmutableRectangle.prototype.area = function () {
    return this.getWidth() * this.getHeight();
};

上述代码可以看到对于w,h的存取只能通过getWidth和getHeight方法,且这两种方法是在ImmutableRectangle的实例中使用。


9.4 Common Object Methods

当定义一个新的JavaScript类,有很多的方法是你常要考虑要定义的。这些方法将在下面的子部分中详细介绍。


9.4.1  The toString() Method


toString()的想法就是让每个类的对象都有它自己唯一的string表现方式。所以它应该定义一个适合的toString方法去转化对象为string形式。当你定义一个类,你应该定义一个toString()方法为的是类的实例可以被转化为有意义的字符串。这个字符串应该包含关于对象被转化的信息,因为这样对调试是很有帮助的。如果认真去选择string表达方式,它在程序本身将起着重要的作用。另外,你应该考虑到添加一个静态的parse()方法到你的类里去分析一个从toString()输出来的字符串结果,返回到object形式。

之前的complex类就包含了这样的方法,且接下来你可以添加一个toString方法到Circle类里;

      

Circle.prototype.toString = function () {
    return "[Circle of radius "+ this.r +", centered at ("+ this.x +", "+ this.y +")]";
}

有了toString()这个方法,一个Circle对象就会被转化成string"[Circle of radius 1, centered at (0,0)]".这样的形式的。


9.4.2 The valueOf() Method

valueOf() 方法十分像toString()方法,但当JavaScript需要转化一个对象到一些原始类型(primitive type)而不是字符串时,它才被调用。典型的,一个数字。在可能的情况下,函数应该返回一个用某种方式表现被this关键值引用的对象的原始值。

在定义下,对象并不是原始值,所以大多数的对象没有原始性的等价。因此,那个默认的被Object类定义的valueOf()方法表现成没有变换且简单的返回被调用的对象。像Number和Boolean这些有着明显的原始性等价。所以他们重载valueOf方法去返回适当的原始值。这就是为什么Number和Boolean对象可以表现十分像他们的等价的原始值。

偶尔地,你可以定义一个有着合理的原始性等价的类。在这种情况下,你可能想为这个类去定义一个习惯的valueOf()方法,在Complex class的例子里,上一章提到的,你将会看到一个valueOf()的方法返回复数的实数部分。因此,当一个Complex对象被使用在数字的上下文里,没有了虚数部分它表现似乎它是一个实数,举个例子,考虑下列代码:


  var a = new Complex(5, 4);
    var b = new Complex(2, 1);
    var c = Complex.sum(a, b); //c is the complex number{7,5}
    var d = a + b; // d is the number 7

一个要注意的是关于定义一个valueOf()方法:在一些环境里,当转化一个对象到string时。valueOf()方法的优先权是在toString()方法之上的。因此,当你为类定义一个valueOf()方法时,你也许需要更明确地去调用toString的方法当你想去强制让对象转化成字符串。继续刚刚的例子:


    alert("c = " + c); //uses valueOf(); display "c = 7"
    alert("c = " + c.toString()); // Displays "c = {7,5}"


9.4.3 Comparison Methods

JavaScript相等的操作符是比较对象的引用的,并不是值。那就是,给定的两个对象引用,看看他们两者的引用是不是指向同一个对象。他们并不是检查看是否两个不同的对象有着相同的属性名字和值。能够去比较两个对象的相等或者甚至是为了相对的顺序(<, >这样的比较),这是很有用处的。如果你定义一个类且想去能够比较类的实例,你应该定义合适的方法去表现那些比较。

Java程序语言用方法作为对象的比较且采用Java的想法在JavaScript里是普遍和有用的。为了能够检测你的类的实例的相等性。定义一个实例方法叫equals().它应该有一个单独的参数和返回true值如果那个参数相等于被调用那个对象。当然它可以由你来决定equal在你的类的上下文里是意味着什么。典型地,你简单的比较两个对象的实例的属性可以去确认他们是否有相同的值。之前的Complex类里有equals()这个方法。

有时候根据一些顺序来比较对象是挺有用的。那就是,对于一些类,它可能可以说成一个实例是比另一个实例小或者大这样。你也许可以排序Complex数字基于它们的magnitude()方法。另一方面,对于Circle的对象,你去比较他们的半径,X,Y坐标还是合并这些呢?

如果你试着把对象和JavaScript的关系符,例如<和<=一起使用,JavaScript首先会呼叫对象的valueOf()方法,如果这个方法返回一个原始值,就去比较那些值。自从我们的Complex类有了一个valueOf()方法是返回 复数的实数部分,Complex实例可以去比较似乎他们就是实数而没有虚数。但这并不是你想要的,去根据明显的你定好的顺序来比较对象,你可以定义一个方法叫compareTo().

compareTo()方法应该接受一个单独的参数和需要比较的被这个方法所调用的对象。如果这个对象比参数对象少,compareTo()方法返回一个比0小的值。如果这个对象比参数对象大,则返回比0大的值,最后就是相等的话,就返回0。这样的返回规矩则是相对重要的了。他们允许你为了关系性和相等的操作去替换接下来的表达式。


Replace this                                                 With this

a < b                                                               a.compareTo(b)   < 0

a <= b                                                             a.compareTo(b)   <= 0

a > b                                                               a.compareTo(b)   > 0

a >= b                                                             a.compareTo(b)   >= 0

a == b                                                             a.compareTo(b)   == 0

a != b                                                               a.compareTo(b)   != 0


这就是Complex类的compareTo()方法来比较复数的长度的:

Complex.prototype.compareTo = function (that) {
    //If we aren't given an argument, or are passed a value that
    //does not have a magnitude() method, throw an exception
    //An alternative would be return -1 ro 1 in this case to say
    //that all Complex object are always less than or greater than
    //any other values.
    if (!that || !that.magnitude || typeof that.magnitude != "function")
        throw new Error("bad argument to Complex.compareTo()");

    //This substraction trick returns a value less than, equal to, or 
    //greater than zero. It is useful in many compareTo() methods.
    return this.magnitude() - that.magnitude();
}

一个原因去比较类的实例是为了那些实例的数组可以被排序。Array.sort()方法接受额外的参数一个比较的函数用着享用的返回值就好象compareTo()那样。我们可以这样做:

complexNumbers.sort(new function(a,b)
{return a.compareTo(b)});

排序是有着足够的重要让你应该去考虑添加一个静态的compare()方法在任何你定义了compareTo()实例方法的类

Complex.compare = function (a, b) { return a.compareTo(b); };

这样,排序的方法就变得简单:

complexNumbers.sort(Complex.compare);


注意到compareTo()和compare()方法他们并不包含在之前的那个Complex类。那是因为他们和equals方法并不是相关联的。equals方法是表示两个Complex类对象的相等仅仅是如果两者的实数和虚数部分都相等。但compareTo()方法是返回0给任何的两个复数有着相同的长度。例如复数1 +0i和0+1i都有着相同的长度,且他们两个值是根据compareTo()来判断相等而不是根据equals()。如果你写equals()方法和compareTo() 方法给相同的类,最好的办法就是让他们一致起来。不一致的相等的概念可以成为致命性的bugs.这就是与已经定义的equals()方法一致的compareTo()方法:

//Compare complex numbers first by their real part. If their real
//parts are equal, compare them by complex part
Complex.prototype.compareTo = function (that) {
    var result = this.x - that.y; //compare real using substraction
    if (result == 0)
        result = this.y - that.y; //then compare imaginary parts
    //Now our result is 0 if and only if this.equals(that)
    return result;
} 



0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:4310次
    • 积分:159
    • 等级:
    • 排名:千里之外
    • 原创:13篇
    • 转载:0篇
    • 译文:0篇
    • 评论:0条