JavaScript中的Java式继承
区别
Java类似的强类型面向对象语言,类为
实例字段
他们是基于实例的属性或变量,用以保存独立对象的状态
实例方法
他们是类的所有实例所共享的方法,由每个独立的实例调用
类字段
这些属性或者变量是属于类的,而不是属于类的某个实例的
类方法
这些方法是属于类的,而不是属于类的某个实例的
js和Java的不同之处
js的函数是以值的形式出现的,方法和字段没有太大的区别,如果属性值是函数,那么这个属性就定义了一个方法,否则仅仅是一个普通的属性或者字段,用js模拟出Java的这四种类成员类型。js中有三种不同的对象,三种对象的属性和行为和下方的类似
构造函数对象
构造函数为js的类定义名称,任何添加到这个构造函数对象中的属性都是类字段和类方法。(如果属性值是函数的话为类方法)
参考上一篇
原型对象
原型对象的属性被类的所有实例所继承。如果原型对象的属性值是函数,这个函数是作为类的实例方法调用
实例对象
类的每个实例都为一个独立的对象,直接给这个实例定义属性是不会为所有实例对象所共享的,定义在实例上的非函数属性,实际上是实例的字段
js中定义类的方法
- 先定义一个构造函数并设置初始化实例对象的属性
- 给构造函数的prototype对象定义实例的方法
- 给构造函数定义类字段和类属性
实现一个表示复数的类
先讨论try和catch的错误处理机制
首先出现throw会直接暂停执行,之前已经说过了
/*这是一个使用try和catch的示范程序*/
function a() {
try {
throw '发生错误';
return 'O(∩_∩)O哈哈~';
} catch(x) {
throw '继续发送错误';
}
}
复制代码
如果程序没有内容会直接执行try,但是由try必须有catch(仅仅是当程序内容为空的情况下),但是对于其扫尾的finally来说则是不一定的,仅此而已
叮, 又发现一个框架 https://formatjs.io/ 不急慢慢来
一些下方要用到的方法
Math.sqrt 平方根 exec()字符串匹配 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec 制定一个字符串,进行匹配操作 parseFloat() 函数解析一个字符串参数并返回一个浮点数。
下面是完整的一个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; // 复数的虚部
};
/*
* 类的实例方法定义为原型对象的函数值属性
* 这里的定义的方法可以被所有实例进行继承(因为是在其属性上直接添加prototyp,而不是__proto__的,是在其子代直接进行继承的
* js的实例的方法必须使用this,因为是方法,不是属性,也不是一些帮助运行的属性,再次重复是方法,方法是用来进行对于对象进行处理的
* 这里是用this来存取实例的字段的
*/
// 当前复数对象加上另外一个复数,并返回一个新的计算和值后的复数对象
Complex.prototype.add = function (that) {
return new Complex(this.r + that.r, this.i + that.r);
};
// 当前复数乘以另外一个复数,并返回一个新的计算乘积之后的复数对象
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.neg = function () {
return '(' + this.r + ',' this.i + ')';
};
// 检测当前复数对象是否和另外一个复数值相等
Complex.prototype.equals = function (that) {
return that != null && // 该对象必须有定义,为了防止两个值是空值仍然相等的情况,至于为什么能分行写,是因为没有结束,并且用&&作为语句的连接,使其变为一个语句
that.constructor == Complex && // 比较的另外一个复数必须是Complex的类,否则无法比较
this.r === that.r && this.i === that.i // 实部必须和虚部相等,一般比较使用严格相等比较运算符,因为严格相等比较运算符比相等比较运算符更加严格,也跟加安全,防止出现undefined或者null的问题
};
/* 类字段(比如常量)和类方法直接定义为构造函数的属性(这里是用来书写一些构造函数的属性的)
* 需要注意的是,类的方法通常不使用this 这是因为一般js使用类的时候是new,而new的过程是,先创建一个空对象,然后将对象的原型链进行赋值(prototype),然后再将空对象的方法,进行赋值,其中全是赋值,并没有使用其对象的方法,所以一般不使用this
* 并且,类仅仅是作为一个工厂生产相关的函数,仅此而已。因为只作为生产,而不进行更改,同样也不使用this
* 这些只是对其参数进行操作。参考上一条
*/
// 这里预定了一些对复数运算的有帮助的类字段
// 这里的类方法通常不使用关键字this
// 他们的命名全是大写,用来表明他们是常量,用来进行替代某些内容
// 在ES5中的这些类字段的属性值为只读
Complex.ZERO = new Complex(0, 0); // 使用这个常量创建一个新的对象
Complex.ONE = new Complex(1, 0);
Complex.I = new Complex(0, 1);
// 这个类方法将由实例对象的toString方法返回的字符串格式解析为一个Comoplex对象的parse属性
// 即这个类方法是是将字符串进行解析的一个类方法
// 使用try的原因是因为js为单线程的,即使捕获异常,避免线程调用失败(毕竟为单线程的嘛) 部分异常不重要,放置局部影响到全局(降低耦合性) 进行对项目的分层,MVC模式,方便更加‘优雅’的找出错误(*^__^*) 嘻嘻……防止找错误的时候不必要心慌,这是关键(⊙o⊙)
// 或者抛出一个类型错误异常
// 因为这里是另外的处理,和其余不同,所以命名使用_开头,方便进行查找
Complex.parse = function(s) {
try { // 假设解析成功
var m = Complex._format.exec(s); // 利用正则表达式进行匹配
return new Complex(parseFloat(m[1]), parseFloat(m[1]));
} catch(x) { // 如果解析失败则抛出异常
throw new TypeError("can't parse '"+ s"'as a complex number.");
}
};
// 定义类的私有字段,这个字段在Complex.parse()中用到了
// 依旧,命名,下划线前缀表明是类的内部使用的,不属于类的公有api部分,因为公有api部分要使用prototype进行继承
Complex._format = /^\{([^,]+), ([^}]+)\}&/;
// \(^o^)/完成啦~\(≧▽≦)/~啦啦啦
复制代码
文件地址https://github.com/melovemingming/code/tree/master/web/07/19