2.10 JavaScript中的面向对象
面向对象是一种新兴的程序设计方法,是一种新的程序设计规范,其基本思想是使用对象、类、继承、封装、消息等基本概念来进行程序设计。从现实世界中客观存在的事物(即对象)出发来构造软件系统,并且在系统构造中尽可能运用人类的自然思维方式。
面向对象最重要的两个概念是对象和类。
对象是系统中用来描述客观事物的一个实体,它是构成系统的一个基本单位。一个对象由一组属性和对这组属性进行操作的一组函数组成。
类是具有相同属性和函数的一组对象的集合,它为属于该类的所有对象提供了统一的抽象描述,其内部包括属性和函数两个主要部分。需要注意的是,JavaScript语言中并没有提供类的定义能力,我们只能直接创建对象。
2.10.1 创建对象
JavaScript语言中虽然不能定义类,但可以直接创建对象,面向对象的效果是一样的。JavaScript语言中创建对象代码的写法与其他常见语言(如Java、C#和C++等)完全不同,有多种函数可以创建JavaScript中的对象。下面分别介绍。
1.采用字面量创建对象
JavaScript中的对象与数值、字符串等都属于基本数据类型,它们可以使用字面量来表示。对象字面量类似于JSON对象,采用对象字面量表示的JavaScript对象是一个无序的“名称/值”对集合,一个对象以“{”(左括号)开始,“}”(右括号)结束。每个“名称”后跟一个“:”(冒号),“名称-值”对之间使用“,”(逗号)分隔,语法如图2-11所示。
图2-11 JavaScript对象字面量表示语法结构图
字面量的每一个“名称/值”对就是对象的一个属性。
示例代码如下:
var Person = { ① name: "Tony", ② age : 18, ③ description : function() { ④ var rs = this.name + "的年龄是:" +this.age; ⑤ return rs; } } var p = Person; ⑥ console.log(p.description()); ⑦
上述代码创建了Person对象,其中第①行代码是声明对象名为Person,第②行代码是定义Person对象的name属性,第③行代码是定义Person对象的age属性,它们都采用“名称/值”对方式。
第④行代码很特殊,它是定义对象的description函数,也是采用“名称/值”对结构,但是“值”部分是一个函数。第⑤行代码是访问对象的name和age属性,我们需要使用this关键字,this关键字指代当前对象。
第⑥行代码var p = Person是将Person对象赋值给p变量,这时p和Person是同一个东西。第⑦行代码调用Person对象description()函数。
2.使用Object.create()函数创建对象
使用Object.create()函数的优势在于能够在原来对象基础上复制出一个新的对象。
示例代码如下:
var Person = { ① name: "Tony", age: 18, description: function () { var rs = this.name + "的年龄是:" + this.age; return rs; } } var p = Person; console.log(p.description()); var p1 = Object.create({ ② name: "Tom", age: 28, description: function () { var rs = this.name + "的年龄是:" + this.age; return rs; } }); console.log(p1.description()); var p2 = Object.create(Person); ③ p2.age = 29; ④ console.log(p2.description()); console.log(Person.description()); ⑤
运行结果:
Tony的年龄是:18 Tom的年龄是:28 Tony的年龄是:29 Tony的年龄是:18
上述代码第①行创建对象Person,第②行和第③行代码都是通过Object.create()函数创建对象。但是第②行Object.create()函数的参数还是采用对象字面量标识。第③行的Object.create()函数的参数为Person对象,相当于赋值了Person对象,而且是“深层复制”,但在第④行修改p2对象的age属性后,不会对Person对象产生任何影响,所有在第⑤行代码打印Person对象的内容仍然是“Tony的年龄是:18”。
3.使用函数对象
我们还可以通过构造函数创建对象,示例代码如下:
function Student(name, age) { ① this.name = name; ② this.age = age; ③ this.description = function () { ④ var rs = this.name + "的年龄是:" + this.age; ⑤ return rs; } } var p3 = new Student('Tony', 28); ⑥ var p4 = new Student('Tom', 38); ⑦ console.log(p3.description()); console.log(p4.description());
上述代码第①行是声明构造函数,构造函数可以初始化对象属性,其中name和age是构造函数的参数。第②行代码this.name = name是通过name参数初始化name属性,第③行代码this.age = age是通过age参数初始化age属性。第④行代码很特殊,它是定义对象的description函数,第⑤行代码是访问对象的name和age属性,我们需要使用this关键字,this关键字指当前对象。
第⑥行和第⑦行代码是创建Student对象p3和p4,p3和p4是两个不同的对象。
2.10.2 常用内置对象
JavaScript中有一些常用的内置对象,它们是Object、Array、Boolean、Number、String、Math、Date、RegExp和Error。
下面分别介绍Object、String、Math和Date类的使用。
1.Object对象
Object对象是所有JavaScript对象的根,每一个对象都继承于Object对象。示例代码如下:
var o = new Object(); ① console.log(o.toString()); ② console.log(o.constructor); ③ console.log(o.valueOf()); ④
运行结果如下:
[object Object] [Function: Object] {}
上述代码第①行是创建Object对象,第②行代码是调用Object对象的toString()函数,该函数返回描述对象的字符串。第③行代码是调用Object对象的constructor属性,可以返回对象的构造函数。第④行代码是调用Object对象的valueOf()函数,可以返回对象的对应的值。
2.String对象
String是字符串对象,String对象有很多常用函数。示例代码如下:
var s = new String("Tony Guan"); ① console.log(s.length); //9 ② console.log(s.toUpperCase()); //TONY GUAN ③ console.log(s.toLowerCase()); //tony guan ④ console.log(s.charAt(0)); //T ⑤ console.log(s.indexOf('n')); //2 ⑥ console.log(s.lastIndexOf('n')); //8 ⑦ console.log(s.substring(5, 9)); //Guan ⑧ console.log(s.split(" ")); //[ 'Tony', 'Guan' ] ⑨
上述代码第①行是创建String对象,第②行代码调用String对象的length属性,属性length是获得字符串的长度。第③行代码调用String对象的toUpperCase()函数,它将字符串中的字符转换为大写。第④行代码调用String对象的toLowerCase()函数,它将字符串中的字符转换为小写。
第⑤行代码调用String对象的charAt(index)函数,获得字符串index索引位置的字符。第⑥行代码调用String对象的indexOf('n')函数,从前面查找字符串中字符串n所在的位置。第⑦行代码调用String对象的lastIndexOf('n')函数,从后面查找字符串中字符串n所在的位置。
第⑧行代码调用String对象的substring(5,9)函数,截取子字符串5为开始位置,9为结束位置。第⑨行代码调用String对象的split(" ")函数,指定字符分割字符串,返回值是数组类型。
3.Math对象
Math对象是与数学计算有关系的对象。示例代码如下:
console.log(Math.PI); ① console.log(Math.SQRT2); ② console.log(Math.random()); ③ console.log(Math.min(1,2,3)); ④ console.log(Math.max(1,2,3)); ⑤ console.log(Math.pow(2, 3)); ⑥ console.log(Math.sqrt(9)); ⑦
上述代码第①行Math.PI是获得圆周率常量,第②行Math.SQRT2是2的平方根,第③行Math.random()是获得0~1之间随机数。第④行是获得集合中的最小值,第⑤行是获得集合中的最大值。第⑥行Math.pow(2,3)是计算2的3次幂。第⑦行Math.sqrt(9)是计算9的平方根。
4.Date对象
Date是日期对象。示例代码如下:
var d = new Date(); ① console.log(d.toString()); ② var d = new Date('2009 11 12'); ③ console.log(d.toString()); var d = new Date('1 2 2012'); ④ console.log(d.toString()); console.log(d.getYear()); //112 ⑤ console.log(d.getMonth()); //0 ⑥ console.log(d.getDay()); //1 ⑦
运行结果:
Sat Aug 30 2014 15:06:44 GMT+0800 (中国标准时间) Thu Nov 12 2009 00:00:00 GMT+0800 (中国标准时间) Mon Jan 02 2012 00:00:00 GMT+0800 (中国标准时间) 112 0 1
上述代码第①行、第③行和第④行创建Date对象,它们提供了不同的构造函数,其中第①行代码的构造函数是空的,它能够获得当前系统时间。第③行代码的构造函数是通过年、月、日创建对象,第④行代码的构造函数是通过月、日、年格式创建对象。
第②行代码通过toString()函数输出对象的描述信息,这些信息是对象日期相关信息。
第⑤行代码通过getYear()函数获得日期对象的“年”信息,这个“年”需要+1900才是习惯的表示方式。第⑥行代码通过getMonth()函数获得日期对象的“月”信息,这个“月”需要+1才是习惯的表示方式。第⑦行代码通过getDay()函数获得日期对象的“星期”信息,如果是星期日,getDay()函数返回0,如果是星期一,getDay()函数返回1,以此类推,星期六返回6。
2.10.3 原型
每一个JavaScript对象都是从一个原型继承而来的,可以通过它的prototype属性获得该原型对象。JavaScript对象继承机制是建立在原型模型基础之上的。
下面通过矢量对象介绍原型的使用。我们知道,在物理学中矢量是有方向和大小的,因此需要两个属性分别表示大小和方向。矢量Vector对象代码如下:
function Vector(v1, v2) { ① this.vec1 = v1; ② this.vec2 = v2; ③ this.add = function (vector) { ④ this.vec1 = this.vec1 + vector.vec1; ⑤ this.vec2 = this.vec2 + vector.vec2; ⑥ } this.toString = function () { ⑦ console.log("vec1 = " + this.vec1 + ", vec2 = " + this.vec2); } } var vecA = new Vector(10.5, 4.7); var vecB = new Vector(32.2, 47); //vecA = vecA + vecB 赋值给vecA vecA.add(vecB); vecA.toString();
运行结果:
vec1 = 42.7,vec2 = 51.7
上述代码第①行声明Vector矢量对象,第②行和第③行定义的vec1和vec2属性分别代表矢量的大小和方向属性。第④行定义两个矢量相加函数,第⑤行是两个矢量的vec1属性相加,第⑥行是两个矢量的vec2属性相加。第⑦行是定义打印矢量内容函数。
随着需要的变化,还需要矢量相减函数。可以使用原型扩展矢量相减功能。示例代码如下:
function Vector(v1, v2) { this.vec1 = v1; this.vec2 = v2; this.add = function (vector) { this.vec1 = this.vec1 + vector.vec1; this.vec2 = this.vec2 + vector.vec2; } this.toString = function () { console.log("vec1 = " + this.vec1 + ", vec2 = " + this.vec2); } } Vector.prototype.sub = function (vector) { ① this.vec1 = this.vec1 - vector.vec1; ② this.vec2 = this.vec2 - vector.vec2; ③ } var vecA = new Vector(10.5, 4.7); var vecB = new Vector(32.2, 47); vecA.sub(vecB); vecA.toString();
运行结果:
vec1 = -21.700000000000003,vec2 = -42.3
上述代码第①行是增加sub(矢量相减)函数,Vector.prototype是矢量对象的原型属性。第②行是两个矢量的vec1属性相减,第③行是两个矢量的vec2属性相减。
我们不仅可以使用原型扩展对象函数,还可以扩展对象的属性。