【类和模块】JavaScript中Java式的类继承([classes and modules] Java class inheritance in JavaScript)

【类和模块】JavaScript中Java式的类继承

如果你有过Java或其他类似强类型面向对象语言的开发经历的话,在你的脑海中,类成员的模样可能会是这个样子:

如果你有过Java或其他类似强类型面向对象语言的开发经历的话,在你的脑海中,类成员的模样可能会是这个样子:

实例字段

它们是基于实例的属性或变量,用以保存独立对象的状态。

它们是基于实例的属性或变量,用以保存独立对象的状态。

实例方法

它们是类的所有实例所共享的方法,由每个独立的实例调用。

它们是类的所有实例所共享的方法,由每个独立的实例调用。

类字段

这些属性或变量是属于类的,而不是属于类的某个实例的。

这些属性或变量是属于类的,而不是属于类的某个实例的。

类方法

这些方法是属于类的,而不是属于类的某个实例的。
JavaScript和Java的一个不同之处在于,JavaScript中的函数都是以值的形式出现的,方法和字段之间并没有太大的区别。如果属性值是函数,那么这个属性就定义一个方法;否则,它只是一个普通的属性或“字段”。尽管存在诸多差异,我们还是可以用JavaScript模拟出Java中的这四种类成员类型。JavaScript中的类牵扯三种不同的对象(参照 图9-1),三种对象的属性的行为和下面三种类成员非常相似:

这些方法是属于类的,而不是属于类的某个实例的。

JavaScript和Java的一个不同之处在于,JavaScript中的函数都是以值的形式出现的,方法和字段之间并没有太大的区别。如果属性值是函数,那么这个属性就定义一个方法;否则,它只是一个普通的属性或“字段”。尽管存在诸多差异,我们还是可以用JavaScript模拟出Java中的这四种类成员类型。JavaScript中的类牵扯三种不同的对象(参照 图9-1),三种对象的属性的行为和下面三种类成员非常相似:

构造函数对象

之前提到,构造函数(对象)为JavaScript的类定义了名字。任何添加到这个构造 函数对象中的属性都是类字段和类方法(如果属性值是函数的话就是类方法)。

之前提到,构造函数(对象)为JavaScript的类定义了名字。任何添加到这个构造 函数对象中的属性都是类字段和类方法(如果属性值是函数的话就是类方法)。

原型对象

原型对象的属性被类的所有实例所继承,如果原型对象的属性值是函数的话,这个 函数就作为类的实例的方法来调用。

原型对象的属性被类的所有实例所继承,如果原型对象的属性值是函数的话,这个 函数就作为类的实例的方法来调用。

实例对象

类的每个实例都是一个独立的对象,直接给这个实例定义的属性是不会为所有实例 对象所共享的。定义在实例上的非函数属性,实际上是实例的字段。

类的每个实例都是一个独立的对象,直接给这个实例定义的属性是不会为所有实例 对象所共享的。定义在实例上的非函数属性,实际上是实例的字段。

在JavaScript中定义类的步骤可以缩减为一个分三步的算法。第一步,先定义一个构造函数,并设置初始化新对象的实例属性。第二步,给构造函数的prototype对象定义实例的方法。第三步,给构造函数定义类字段和类属性。我们可以将这三个步骤封装进一个简单的defineClass( )函数中:
// 一个用以定义简单类的函数
function defineClass(constructor, // 用以设置实例的属性的函数
methods, // 实例的方法,复制至原型中
statics) // 类属性,复制至构造函数中
{
if(methods) extend(constructor.prototype, methods);
if(statics) extend(constructor, statics);
return constructor;
}

// 这是Range类的另一个实现
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); } });

在JavaScript中定义类的步骤可以缩减为一个分三步的算法。第一步,先定义一个构造函数,并设置初始化新对象的实例属性。第二步,给构造函数的prototype对象定义实例的方法。第三步,给构造函数定义类字段和类属性。我们可以将这三个步骤封装进一个简单的defineClass( )函数中:

// 一个用以定义简单类的函数 
function defineClass(constructor,     // 用以设置实例的属性的函数 
                     methods,         // 实例的方法,复制至原型中 
                     statics)         // 类属性,复制至构造函数中
{ 
    if(methods) extend(constructor.prototype, methods);
    if(statics) extend(constructor, statics); 
    return constructor;
}

// 这是Range类的另一个实现 
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); } });

下例定义类的代码更长一些。这里定义了一个表示复数的类,这段代码展示了如何使用JavaScript来模拟实现Java式的类成员。下例中的代码没有用到上面的defineClass()函数,而是“手动”来实现:
例:Complex.js:表示复数的类
/* Complex.js:
* 这个文件定义了Complex类,用来描述复数
* 回忆一下,复数是实数和虚数的和,并且虚数i是-1的平方根
*/

/*
* 这个构造函数为它所创建的每个实例定义了实例字段r和i
* 这两个字段分别保存复数的实部和虚部
* 它们是对象的状态
*/
function Complex(real, imaginary) {
if (isNaN(real) || isNaN(imaginary)) // 确保两个实参都是数字
throw new TypeError(); // 如果不都是数字则抛出错误
this.r = real; // 复数的实部
this.i = imaginary; // 复数的虚部
}

/*
* 类的实例方法定义为原型对象的函数值属性
* 这里定义的方法可以被所有实例继承,并为它们提供共享的行为
* 需要注意的是,JavaScript的实例方法必须使用关键字this
* 来存取实例的字段
*/

// 当前复数对象加上另外一个复数,并返回一个新的计算和值后的复数对象
Complex.prototype.add = function (that) {
return new Complex(this.r + that.r, this.i + that.i);
};

// 当前复数乘以另外一个复数,并返回一个新的计算乘积之后的复数对象
Complex.prototype.mul = function (that) {
return new Complex(this.r * that.r – this.i * that.i, this.r * that.i + this.i * that.r);
}

// 计算复数的模,复数的模定义为原点(0,0)到复平面的距离
Complex.prototype.mag = function () {
return Math.sqrt(this.r * this.r + this.i * this.i);
};

// 复数的求负运算
Complex.prototype.neg = function () {
return new Complex(-this.r, -this.i);
};

// 将复数对象转换为一个字符串
Complex.prototype.toString = function () {
return “{” + this.r + “,” + this.i + “}”;
};

// 检测当前复数对象是否和另外一个复数值相等
Complex.prototype.equals = function (that) {
return that != null && // 必须有定义且不能是null
that.constructor === Complex && // 并且必须是Complex的实例
this.r === that.r && this.i === that.i; // 并且必须包含相同的值
};

/*
* 类字段(比如常量)和类方法直接定义为构造函数的属性
* 需要注意的是,类的方法通常不使用关键字this,
* 它们只对其参数进行操作
*/

// 这里预定义了一些对复数运算有帮助的类字段
// 它们的命名全都是大写,用以表明它们是常量
//(在ECMAScript 5中,还能设置这些类字段的属性为只读)
Complex.ZERO = new Complex(0, 0);
Complex.ONE = new Complex(1, 0);
Complex.I = new Complex(0, 1);

// 这个类方法将由实例对象的toString方法返回的字符串格式解析为一个Complex对象
// 或者抛出一个类型错误异常
Complex.parse = function (s) {
try { // 假设解析成功
var m = Complex._format.exec(s); // 利用正则表达式进行匹配
return new Complex(parseFloat(m[1]), parseFloat(m[2]));
} catch(x) { // 如果解析失败则抛出异常
throw new TypeError(“Can’t parse ‘” + s + “‘ as a complex number.”);
};

// 定义类的“私有”字段,这个字段在Complex.parse()中用到了
// 下划线前缀表明它是类内部使用的,而不属于类的公有API的部分
Complex._format = /^\{([^,]+), ([^}]+)\}$/;

下例定义类的代码更长一些。这里定义了一个表示复数的类,这段代码展示了如何使用JavaScript来模拟实现Java式的类成员。下例中的代码没有用到上面的defineClass()函数,而是“手动”来实现:

例:Complex.js:表示复数的类
/* Complex.js:
 * 这个文件定义了Complex类,用来描述复数
 * 回忆一下,复数是实数和虚数的和,并且虚数i是-1的平方根
 */

/*
 * 这个构造函数为它所创建的每个实例定义了实例字段r和i
 * 这两个字段分别保存复数的实部和虚部
 * 它们是对象的状态
*/
function Complex(real, imaginary) {
    if (isNaN(real) || isNaN(imaginary))     // 确保两个实参都是数字
        throw new TypeError();               // 如果不都是数字则抛出错误
    this.r = real;                           // 复数的实部
    this.i = imaginary;                      // 复数的虚部
}

/*
 * 类的实例方法定义为原型对象的函数值属性
 * 这里定义的方法可以被所有实例继承,并为它们提供共享的行为
 * 需要注意的是,JavaScript的实例方法必须使用关键字this
 * 来存取实例的字段
 */

// 当前复数对象加上另外一个复数,并返回一个新的计算和值后的复数对象
Complex.prototype.add = function (that) { 
    return new Complex(this.r + that.r, this.i + that.i);
};

// 当前复数乘以另外一个复数,并返回一个新的计算乘积之后的复数对象
Complex.prototype.mul = function (that) {
    return new Complex(this.r * that.r - this.i * that.i, this.r * that.i + this.i * that.r); 
}

// 计算复数的模,复数的模定义为原点(0,0)到复平面的距离 
Complex.prototype.mag = function () {
    return Math.sqrt(this.r * this.r + this.i * this.i); 
};

// 复数的求负运算
Complex.prototype.neg = function () { 
    return new Complex(-this.r, -this.i);
};

// 将复数对象转换为一个字符串
Complex.prototype.toString = function () { 
    return "{" + this.r + "," + this.i + "}";
};

// 检测当前复数对象是否和另外一个复数值相等
Complex.prototype.equals = function (that) { 
    return that != null &&                      // 必须有定义且不能是null
    that.constructor === Complex &&             // 并且必须是Complex的实例
    this.r === that.r && this.i === that.i;     // 并且必须包含相同的值
};

/*
 * 类字段(比如常量)和类方法直接定义为构造函数的属性
 * 需要注意的是,类的方法通常不使用关键字this,
 * 它们只对其参数进行操作
 */

// 这里预定义了一些对复数运算有帮助的类字段
// 它们的命名全都是大写,用以表明它们是常量
//(在ECMAScript 5中,还能设置这些类字段的属性为只读)
Complex.ZERO = new Complex(0, 0);
Complex.ONE = new Complex(1, 0);
Complex.I = new Complex(0, 1);

// 这个类方法将由实例对象的toString方法返回的字符串格式解析为一个Complex对象 
// 或者抛出一个类型错误异常
Complex.parse = function (s) {
    try { // 假设解析成功
        var m = Complex._format.exec(s); // 利用正则表达式进行匹配 
        return new Complex(parseFloat(m[1]), parseFloat(m[2]));
    } catch(x) { // 如果解析失败则抛出异常
        throw new TypeError("Can't parse '" + s + "' as a complex number.");
};

// 定义类的“私有”字段,这个字段在Complex.parse()中用到了
// 下划线前缀表明它是类内部使用的,而不属于类的公有API的部分
Complex._format = /^\{([^,]+), ([^}]+)\}$/;

从下例中所定义的Complex类可以看出,我们用到了构造函数、实例字段、实例方法、类字段和类方法,看一下这段示例代码:
var c = new Complex(2,3); // 使用构造函数创建新的对象
var d = new Complex(c.i,c.r); // 用到了c的实例属性
c.add(d).toString(); // => “{5,5}”: 使用了实例的方法
// 这个稍微复杂的表达式用到了类方法和类字段
Complex.parse(c.toString()); // 将c转换为字符串
add(c.neg()). // 加上它的负数
equals(Complex.ZERO) // 结果应当永远是”零”

从下例中所定义的Complex类可以看出,我们用到了构造函数、实例字段、实例方法、类字段和类方法,看一下这段示例代码:

var c = new Complex(2,3);         // 使用构造函数创建新的对象
var d = new Complex(c.i,c.r);     // 用到了c的实例属性
c.add(d).toString();	          // => "{5,5}": 使用了实例的方法
// 这个稍微复杂的表达式用到了类方法和类字段
Complex.parse(c.toString());      // 将c转换为字符串
 add(c.neg()).	              // 加上它的负数
 equals(Complex.ZERO)	      // 结果应当永远是"零"

尽管JavaScript可以模拟出Java式的类成员,但Java中有很多重要的特性是无法在JavaScript类中模拟的。首先,对于Java类的实例方法来说,实例字段可以用做局部变量,而不需要使用关键字this来引用它们。JavaScript是没办法模拟这个特性的,但可以使用with语句来近似地实现这个功能(但这种做法并不推荐):
Complex.prototype.toString = function() {
with(this) {
return “{” + r + “,” + i + }”;
}
};

尽管JavaScript可以模拟出Java式的类成员,但Java中有很多重要的特性是无法在JavaScript类中模拟的。首先,对于Java类的实例方法来说,实例字段可以用做局部变量,而不需要使用关键字this来引用它们。JavaScript是没办法模拟这个特性的,但可以使用with语句来近似地实现这个功能(但这种做法并不推荐):

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

在Java中可以使用final声明字段为常量,并且可以将字段和方法声明为private吗,用以表示它们是私有成员且在类的外面是不可见的。在JavaScript中没有这些关键字。下例中使用了一些命名写法上的约定来给出一些暗示,比如哪些成员是不能修改的(以大写字母命名的命名),哪些成员在类外部是不可见的(以下划线为前缀的命名)。私有属性可以使用闭包里的局部变量来模拟,常量属性可以在ECMAScript5中直接实现。

在Java中可以使用final声明字段为常量,并且可以将字段和方法声明为private吗,用以表示它们是私有成员且在类的外面是不可见的。在JavaScript中没有这些关键字。下例中使用了一些命名写法上的约定来给出一些暗示,比如哪些成员是不能修改的(以大写字母命名的命名),哪些成员在类外部是不可见的(以下划线为前缀的命名)。私有属性可以使用闭包里的局部变量来模拟,常量属性可以在ECMAScript5中直接实现。

————————

【类和模块】JavaScript中Java式的类继承

If you have experience in developing Java or other strongly typed object-oriented languages, in your mind, class members may look like this:

If you have experience in developing Java or other strongly typed object-oriented languages, in your mind, class members may look like this:

Instance field

They are instance based properties or variables that hold the state of independent objects.

They are instance based properties or variables that hold the state of independent objects.

Example method

They are methods shared by all instances of a class and called by each individual instance.

They are methods shared by all instances of a class and called by each individual instance.

Class field

These properties or variables belong to a class, not to an instance of the class.

These properties or variables belong to a class, not to an instance of the class.

Class method

These methods belong to a class, not to an instance of the class.
One difference between JavaScript and Java is that functions in JavaScript appear in the form of values, and there is not much difference between methods and fields. If the attribute value is a function, the attribute defines a method; Otherwise, it is just a common attribute or “field”. Despite many differences, we can simulate these four types of member types in java with JavaScript. Classes in JavaScript involve three different objects (see Figure 9-1). The behavior of the attributes of the three objects is very similar to the following three types of members:

These methods belong to a class, not to an instance of the class.

One difference between JavaScript and Java is that functions in JavaScript appear in the form of values, and there is not much difference between methods and fields. If the attribute value is a function, the attribute defines a method; Otherwise, it is just a common attribute or “field”. Despite many differences, we can simulate these four types of member types in java with JavaScript. Classes in JavaScript involve three different objects (see Figure 9-1). The behavior of the attributes of the three objects is very similar to the following three types of members:

Constructor object

As mentioned earlier, the constructor (object) defines the name of the JavaScript class. Any property added to this constructor object is a class field and class method (class method if the property value is a function).

As mentioned earlier, the constructor (object) defines the name of the JavaScript class. Any property added to this constructor object is a class field and class method (class method if the property value is a function).

Prototype object

The properties of the prototype object are inherited by all instances of the class. If the property value of the prototype object is a function, this function will be called as the method of the instance of the class.

The properties of the prototype object are inherited by all instances of the class. If the property value of the prototype object is a function, this function will be called as the method of the instance of the class.

Instance object

Each instance of a class is an independent object, and the properties defined directly for this instance will not be shared by all instance objects. A non functional property defined on an instance is actually a field of the instance.

Each instance of a class is an independent object, and the properties defined directly for this instance will not be shared by all instance objects. A non functional property defined on an instance is actually a field of the instance.

The step of defining classes in JavaScript can be reduced to a three-step algorithm. The first step is to define a constructor and set the instance property to initialize the new object. The second step is to define the instance method for the prototype object of the constructor. The third step is to define class fields and class properties for the constructor. We can encapsulate these three steps into a simple defineclass() function:
//A function that defines a simple class
Function defineclass (constructor, / / a function used to set the properties of an instance
Methods, / / copy the method of the instance to the prototype
Statistics) / / class attribute, copy to constructor
{
if(methods) extend(constructor.prototype, methods);
if(statics) extend(constructor, statics);
return constructor;
}
//This is another implementation of the 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); } });

The step of defining classes in JavaScript can be reduced to a three-step algorithm. The first step is to define a constructor and set the instance property to initialize the new object. The second step is to define the instance method for the prototype object of the constructor. The third step is to define class fields and class properties for the constructor. We can encapsulate these three steps into a simple defineclass() function:

// 一个用以定义简单类的函数 
function defineClass(constructor,     // 用以设置实例的属性的函数 
                     methods,         // 实例的方法,复制至原型中 
                     statics)         // 类属性,复制至构造函数中
{ 
    if(methods) extend(constructor.prototype, methods);
    if(statics) extend(constructor, statics); 
    return constructor;
}

// 这是Range类的另一个实现 
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); } });

The code for defining the class in the following example is longer. Here, a class representing a complex number is defined. This code shows how to use JavaScript to simulate and implement Java style class members. The code in the following example does not use the defineclass() function above, but is implemented “manually”:
Example: complex.js: a class representing a complex number
/* Complex.js:
*This file defines the complex class, which is used to describe complex numbers
*Recall that the complex number is the sum of the real number and the imaginary number, and the imaginary number I is the square root of – 1
*/
/*
*This constructor defines the instance fields R and I for each instance it creates
*These two fields hold the real and imaginary parts of the complex number, respectively
*They are the state of the object
*/
function Complex(real, imaginary) {
If (IsNaN (real) | IsNaN (imaginary)) / / ensure that both arguments are numbers
Throw new typeerror(); / / if they are not all numbers, an error will be thrown
This. R = real; / / real part of complex number
This. I = imaginary; / / imaginary part of complex number
}
/*
*Class is defined as the function value property of the prototype object
*The methods defined here can be inherited by all instances and provide shared behavior for them
*Note that the JavaScript instance method must use the keyword this
*To access the fields of the instance
*/
//Add another complex number to the current complex object and return a new complex object after calculation and value
Complex.prototype.add = function (that) {
return new Complex(this.r + that.r, this.i + that.i);
};
//Multiply the current complex number by another complex number and return a new complex object after calculating 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);
}
//Calculate the modulus of the complex number, which is defined as the distance from the origin (0,0) to the complex plane
Complex.prototype.mag = function () {
return Math.sqrt(this.r * this.r + this.i * this.i);
};
//Negative operation of complex number
Complex.prototype.neg = function () {
return new Complex(-this.r, -this.i);
};
//Converts a complex object to a string
Complex.prototype.toString = function () {
return “{” + this.r + “,” + this.i + “}”;
};
//Detects whether the current complex object is equal to another complex value
Complex.prototype.equals = function (that) {
Return that! = null & amp; & amp; / / must be defined and cannot be null
That. Constructor = = = complex & amp; & amp; / / and must be an instance of complex
This. R = = = that. R & amp; & amp; this. I = = = that. I; / / and must contain the same value
};
/*
*Class fields (such as constants) and class methods are directly defined as properties of the constructor
*It should be noted that class methods usually do not use the keyword this,
*They operate only on their parameters
*/
//There are predefined class fields that are helpful for complex operations
//Their names are all capitalized to indicate that they are constants
//(in ECMAScript 5, you can also set the properties of these class fields as read-only)
Complex.ZERO = new Complex(0, 0);
Complex.ONE = new Complex(1, 0);
Complex.I = new Complex(0, 1);
//This class method parses the string format returned by the toString method of the instance object into a complex object
//Or throw a type error exception
Complex.parse = function (s) {
Try {/ / hypothesis resolution succeeds
Var M = complex. _format.exec (s); / / match using regular expressions
return new Complex(parseFloat(m[1]), parseFloat(m[2]));
}Catch (x) {/ / if parsing fails, an exception is thrown
throw new TypeError(“Can’t parse ‘” + s + “‘ as a complex number.”);
};
//Define the “private” field of the class, which is used in complex. Parse()
//The underscore prefix indicates that it is used inside the class and is not part of the public API of the class
Complex._format = /^\{([^,]+), ([^}]+)\}$/;

The code for defining the class in the following example is longer. Here, a class representing a complex number is defined. This code shows how to use JavaScript to simulate and implement Java style class members. The code in the following example does not use the defineclass() function above, but is implemented “manually”:

例:Complex.js:表示复数的类
/* Complex.js:
 * 这个文件定义了Complex类,用来描述复数
 * 回忆一下,复数是实数和虚数的和,并且虚数i是-1的平方根
 */

/*
 * 这个构造函数为它所创建的每个实例定义了实例字段r和i
 * 这两个字段分别保存复数的实部和虚部
 * 它们是对象的状态
*/
function Complex(real, imaginary) {
    if (isNaN(real) || isNaN(imaginary))     // 确保两个实参都是数字
        throw new TypeError();               // 如果不都是数字则抛出错误
    this.r = real;                           // 复数的实部
    this.i = imaginary;                      // 复数的虚部
}

/*
 * 类的实例方法定义为原型对象的函数值属性
 * 这里定义的方法可以被所有实例继承,并为它们提供共享的行为
 * 需要注意的是,JavaScript的实例方法必须使用关键字this
 * 来存取实例的字段
 */

// 当前复数对象加上另外一个复数,并返回一个新的计算和值后的复数对象
Complex.prototype.add = function (that) { 
    return new Complex(this.r + that.r, this.i + that.i);
};

// 当前复数乘以另外一个复数,并返回一个新的计算乘积之后的复数对象
Complex.prototype.mul = function (that) {
    return new Complex(this.r * that.r - this.i * that.i, this.r * that.i + this.i * that.r); 
}

// 计算复数的模,复数的模定义为原点(0,0)到复平面的距离 
Complex.prototype.mag = function () {
    return Math.sqrt(this.r * this.r + this.i * this.i); 
};

// 复数的求负运算
Complex.prototype.neg = function () { 
    return new Complex(-this.r, -this.i);
};

// 将复数对象转换为一个字符串
Complex.prototype.toString = function () { 
    return "{" + this.r + "," + this.i + "}";
};

// 检测当前复数对象是否和另外一个复数值相等
Complex.prototype.equals = function (that) { 
    return that != null &&                      // 必须有定义且不能是null
    that.constructor === Complex &&             // 并且必须是Complex的实例
    this.r === that.r && this.i === that.i;     // 并且必须包含相同的值
};

/*
 * 类字段(比如常量)和类方法直接定义为构造函数的属性
 * 需要注意的是,类的方法通常不使用关键字this,
 * 它们只对其参数进行操作
 */

// 这里预定义了一些对复数运算有帮助的类字段
// 它们的命名全都是大写,用以表明它们是常量
//(在ECMAScript 5中,还能设置这些类字段的属性为只读)
Complex.ZERO = new Complex(0, 0);
Complex.ONE = new Complex(1, 0);
Complex.I = new Complex(0, 1);

// 这个类方法将由实例对象的toString方法返回的字符串格式解析为一个Complex对象 
// 或者抛出一个类型错误异常
Complex.parse = function (s) {
    try { // 假设解析成功
        var m = Complex._format.exec(s); // 利用正则表达式进行匹配 
        return new Complex(parseFloat(m[1]), parseFloat(m[2]));
    } catch(x) { // 如果解析失败则抛出异常
        throw new TypeError("Can't parse '" + s + "' as a complex number.");
};

// 定义类的“私有”字段,这个字段在Complex.parse()中用到了
// 下划线前缀表明它是类内部使用的,而不属于类的公有API的部分
Complex._format = /^\{([^,]+), ([^}]+)\}$/;

As can be seen from the complex class defined in the following example, we use constructors, instance fields, instance methods, class fields and class methods. Take a look at this example code:
var c = new Complex(2,3); // Create a new object using the constructor
var d = new Complex(c.i,c.r); // The instance property of C is used
c.add(d).toString(); // =& gt; ‘{5,5}’: method using instance
//This slightly more complex expression uses class methods and class fields
Complex.parse(c.toString()); // Convert C to string
add(c.neg()). // Add its negative number
equals(Complex.ZERO) // The result should always be “zero”

As can be seen from the complex class defined in the following example, we use constructors, instance fields, instance methods, class fields and class methods. Take a look at this example code:

var c = new Complex(2,3);         // 使用构造函数创建新的对象
var d = new Complex(c.i,c.r);     // 用到了c的实例属性
c.add(d).toString();	          // => "{5,5}": 使用了实例的方法
// 这个稍微复杂的表达式用到了类方法和类字段
Complex.parse(c.toString());      // 将c转换为字符串
 add(c.neg()).	              // 加上它的负数
 equals(Complex.ZERO)	      // 结果应当永远是"零"

Although JavaScript can simulate Java like class members, there are many important features in Java that cannot be simulated in JavaScript classes. First, for instance methods of Java classes, instance fields can be used as local variables without using the keyword this to refer to them. JavaScript cannot simulate this feature, but you can use the with statement to approximate this function (but this method is not recommended):
Complex.prototype.toString = function() {
with(this) {
return “{” + r + “,” + i + }”;
}
};

Although JavaScript can simulate Java like class members, there are many important features in Java that cannot be simulated in JavaScript classes. First, for instance methods of Java classes, instance fields can be used as local variables without using the keyword this to refer to them. JavaScript cannot simulate this feature, but you can use the with statement to approximate this function (but this method is not recommended):

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

In Java, you can use final to declare fields as constants and fields and methods as private to indicate that they are private members and invisible outside the class. These keywords are not available in JavaScript. In the following example, some naming conventions are used to give some hints, such as which members cannot be modified (named with uppercase letters) and which members are invisible outside the class (named with underscore as prefix). Private properties can be simulated by local variables in closures, and constant properties can be directly implemented in ecmascript5.

In Java, you can use final to declare fields as constants and fields and methods as private to indicate that they are private members and invisible outside the class. These keywords are not available in JavaScript. In the following example, some naming conventions are used to give some hints, such as which members cannot be modified (named with uppercase letters) and which members are invisible outside the class (named with underscore as prefix). Private properties can be simulated by local variables in closures, and constant properties can be directly implemented in ecmascript5.