在 JavaScript 中实现 Java 式的类继承
JavaScript 和 Java 等强类型面向对象语言的不同之处在于,JavaScript 中的函数都是以值的形式出现的,方法和字段之间没有太大的区别。如果属性值是函数,那么这个属性就定义了一个方法,否则它就是一个普通的属性或「字段」。JavaScript 中的类涉及到三种不同的对象:
- 构造函数对象:构造函数为 JavaScript 的类定义了名字,任何添加到构造函数的属性都是类字段和类方法(属性值为函数就是类方法)
- 原型对象:原型对象的属性被类的所有实例继承,如果原型对象的属性值是函数,这个函数就作为类的示例的方法调用
- 实例对象:类的每个实例都是独立的对象,直接给这个实例定义的属性是不会为所有实例对象所共享的
在 JavaScript 中定义类的步骤可以缩减为一个分三步的算法,第一步,先定义一个构造函数,并设置初始化新对象的实例属性;第二步,给构造函数的 prototype
对象定义实例的方法;第三步,给构造函数定义类字段和类属性。我们将这三步操作封装到一个 defineClass()
函数中:
/**
* 一个用以定义简单类的函数
* @param constructor 用于设置实例属性的函数
* @param methods 实例方法,会被复制到原型对象中
* @param statics 类属性,会被复制到构造函数中
* @returns constructor
*/
function defineClass(constructor, methods, statics) {
if (methods)
extend(constructor.prototype, methods);
if (statics)
extend(constructor, statics);
return constructor;
}
/**
* 把 p 中的可枚举属性复制到 o 中,并返回 o
* @param o
* @param p
* @returns o
*/
function extend(o, p) {
for (prop in p) {
o[prop] = p[prop];
}
return o;
}
// 这是 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 的类实现,定义一个表示复数的类 Complex,实现构造函数、实例字段、实例方法、类字段和类方法:
/**
* Complex.js
* 这个文件定义了 Complex 类,用来描述复数
*/
/**
* 这个构造函数为它所创建的每个实例定义了实例字段r和i
* @param real 复数的实部
* @param imaginary 复数的虚部
* @constructor
*/
function Complex(real, imaginary) {
if (isNaN(real) || isNaN(imaginary)) { // 确保两个实参都是数字
throw new TypeError();
}
this.r = real;
this.i = imaginary;
}
// 下面定义类的实例方法
/**
* 复数相加
* @param that
* @returns {Complex}
*/
Complex.prototype.add = function (that) {
return new Complex(this.r + that.r, this.i + that.i);
};
/**
* 复数相乘
* @param that
* @returns {Complex}
*/
Complex.prototype.mul = function (that) {
return new Complex(this.r * that.r - this.i * that.i, this.r * that.i + this.i * that.r);
};
/**
* 计算复数的模
* @returns {number}
*/
Complex.prototype.mag = function () {
return Math.sqrt(this.r * this.r + this.i * this.i);
};
/**
* 复数的求负运算
* @returns {Complex}
*/
Complex.prototype.neg = function () {
return new Complex(-this.r, -this.i);
};
/**
* 将复数对象转换为字符串
* @returns {string}
*/
Complex.prototype.toString = function () {
return "{" + this.r + "," + this.i + "}";
};
/**
* 判断两个复数是否相等
* @param that
* @returns {boolean}
*/
Complex.prototype.equals = function (that) {
return that !== null && that.constructor === Complex &&
this.r === that.r && this.i === that.i;
};
// 下面定义类字段(常量)和类方法(静态方法)
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._format = /^\{([^,]+),([^}]+)\}$/;
下面我们对复数类进行调用:
No Comments