JavaScript: The Definitive Guide, Sixth Editio javaScript权威指南(第6版) pdf 文字版-文字版, javascript电子书, 和javascript 有关的电子书:

9.3 Java-Style Classes in JavaScript

9.3 Java-Style Classes in JavaScript


If you have programmed in Java or a similar strongly-typed object-oriented language, you may be accustomed to thinking about four kinds of class members:

Instance fields

These are the per-instance properties or variables that hold the state of individual objects.

Instance methods

These are methods that are shared by all instances of the class that are invoked through individual instances.

Class fields

These are properties or variables associated with the class rather than the instances of the class.

Class methods

These are methods that are associated with the class rather than with instances.

One way JavaScript differs from Java is that its functions are values, and there is no hard distinction between methods and fields. If the value of a property is a function, that property defines a method; otherwise, it is just an ordinary property or “field.” Despite this difference, we can simulate each of Java’s four categories of class members in JavaScript. In JavaScript, there are three different objects involved in any class definition (see Figure 9-1 ), and the properties of these three objects act like different kinds of class members:

Constructor object

As we’ve noted, the constructor function (an object) defines a name for a JavaScript class. Properties you add to this constructor object serve as class fields and class methods (depending on whether the property values are functions or not).

Prototype object

The properties of this object are inherited by all instances of the class, and properties whose values are functions behave like instance methods of the class.

Instance object

Each instance of a class is an object in its own right, and properties defined directly on an instance are not shared by any other instances. Nonfunction properties defined on instances behave as the instance fields of the class.

We can reduce the process of class definition in JavaScript to a three-step algorithm. First, write a constructor function that sets instance properties on new objects. Second, define instance methods on the prototype object of the constructor. Third, define class fields and class properties on the constructor itself. We can even implement this algorithm as a simple defineClass() function. (It uses the extend() function of Exam ple 6-2 as patched in Example 8-3 ):

// A simple function for defining simple classes function defineClass(constructor, // A function that sets instance properties

9.3 Java-Style Classes in JavaScript | 205

methods, // Instance methods: copied to prototype
statics) // Class properties: copied to constructor
{

if (methods) extend(constructor.prototype, methods); if (statics) extend(constructor, statics); return constructor;

}

// This is a simple variant of our Range class var SimpleRange = defineClass(function(f,t) { this.f = f; this.t = t; },

{ includes: function(x) { return this.f <= x && x <= this.t;}, toString: function() { return this.f + "..." + this.t; }

}, { upto: function(t) { return new SimpleRange(0, t); } });

Example 9-3 is a longer class definition. It creates a class that represents complex numbers and demonstrates how to simulate Java-style class members using JavaScript. It does this “manually”—without relying on the defineClass() function above.

Example 9-3. Complex.js: A complex number class

/*

*Complex.js:*This file defines a Complex class to represent complex numbers.*Recall that a complex number is the sum of a real number and an*imaginary number and that the imaginary number i is the square root of -1. */

/*

*This constructor function defines the instance fields r and i on every*instance it creates. These fields hold the real and imaginary parts of*the complex number: they are the state of the object. */ function Complex(real, imaginary) { if (isNaN(real) || isNaN(imaginary)) // Ensure that both args are numbers.

throw new TypeError(); // Throw an error if they are not. this.r = real; // The real part of the complex number. this.i = imaginary; // The imaginary part of the number.

}

/*

*The instance methods of a class are defined as function-valued properties * of the prototype object. The methods defined here are inherited by all*instances and provide the shared behavior of the class. Note that JavaScript*instance methods must use the this keyword to access the instance fields. */

// Add a complex number to this one and return the sum in a new object. Complex.prototype.add = function(that) { return new Complex(this.r + that.r, this.i + that.i); };

// Multiply this complex number by another and return the product. Complex.prototype.mul = function(that) {

return new Complex(this.r * that.r - this.i * that.i, this.r * that.i + this.i * that.r); };

// Return the real magnitude of a complex number. This is defined // as its distance from the origin (0,0) of the complex plane. Complex.prototype.mag = function() {

return Math.sqrt(this.r*this.r + this.i*this.i); };

// Return a complex number that is the negative of this one. Complex.prototype.neg = function() { return new Complex(-this.r, -this.i); };

// Convert a Complex object to a string in a useful way. Complex.prototype.toString = function() { return "{" + this.r + "," + this.i + "}"; };

// Test whether this Complex object has the same value as another. Complex.prototype.equals = function(that) {

return that != null && // must be defined and non-null that.constructor === Complex && // and an instance of Complex this.r === that.r && this.i === that.i; // and have the same values.

};

/*

*Class fields (such as constants) and class methods are defined as*properties of the constructor. Note that class methods do not*generally use the this keyword: they operate only on their arguments. */

// Here are some class fields that hold useful predefined complex numbers. // Their names are uppercase to indicate that they are constants. // (In ECMAScript 5, we could actually make these properties read-only.) Complex.ZERO = new Complex(0,0); Complex.ONE = new Complex(1,0); Complex.I = new Complex(0,1);

// This class method parses a string in the format returned by the toString // instance method and returns a Complex object or throws a TypeError. Complex.parse = function(s) {

try { // Assume that the parsing will succeed var m = Complex._format.exec(s); // Regular expression magic return new Complex(parseFloat(m[1]), parseFloat(m[2]));

} catch (x) { // And throw an exception if it fails throw new TypeError("Can't parse '" + s + "' as a complex number."); } };

// A "private" class field used in Complex.parse() above. // The underscore in its name indicates that it is intended for internal // use and should not be considered part of the public API of this class. Complex._format = /^\{([^,]+),([^}]+)\}$/;

9.3 Java-Style Classes in JavaScript | 207

With the Complex class of Example 9-3 defined, we can use the constructor, instance fields, instance methods, class fields, and class methods with code like this:

var c = new Complex(2,3); // Create a new object with the constructor

var d = new Complex(c.i,c.r); // Use instance properties of c

c.add(d).toString(); // => "{5,5}": use instance methods

// A more complex expression that uses a class method and field

Complex.parse(c.toString()). // Convert c to a string and back again,

add(c.neg()). // add its negative to it,

equals(Complex.ZERO) // and it will always equal zero

Although JavaScript classes can emulate Java-style class members, there are a number of significant Java features that JavaScript classes do not support. First, in the instance methods of Java classes, instance fields can be used as if they were local variables— there is no need to prefix them with this. JavaScript does not do this, but you could achieve a similar effect using a with statement (this is not recommended, however):

Complex.prototype.toString = function() { with(this) { return "{" + r + "," + i + "}"; } };

Java allows fields to be declared final to indicate that they are constants, and it allows fields and methods to be declared private to specify that they are private to the class implementation and should not be visible to users of the class. JavaScript does not have these keywords, and Example 9-3 uses typographical conventions to provide hints that some properties (whose names are in capital letters) should not be changed and that others (whose names begin with an underscore) should not be used outside of the class. We’ll return to both of these topics later in the chapter: private properties can be emulated using the local variables of a closure (see §9.6.6 ) and constant properties are possible in ECMAScript 5 (see §9.8.2 ).

欢迎转载,转载请注明来自一手册:http://yishouce.com/book/1/27674.html
友情链接It题库(ittiku.com)| 版权归yishouce.com所有| 友链等可联系 admin#yishouce.com|粤ICP备16001685号-1