Cocos2d-x学习笔记:完全掌握JS API与游戏项目开发 (未来书库,触控未来官方教材)
上QQ阅读APP看书,第一时间看更新

2.10 JavaScript中的面向对象

面向对象是一种新兴的程序设计方法,是一种新的程序设计规范,其基本思想是使用对象、类、继承、封装、消息等基本概念来进行程序设计。从现实世界中客观存在的事物(即对象)出发来构造软件系统,并且在系统构造中尽可能运用人类的自然思维方式。

面向对象最重要的两个概念是对象和类。

对象是系统中用来描述客观事物的一个实体,它是构成系统的一个基本单位。一个对象由一组属性和对这组属性进行操作的一组函数组成。

类是具有相同属性和函数的一组对象的集合,它为属于该类的所有对象提供了统一的抽象描述,其内部包括属性和函数两个主要部分。需要注意的是,JavaScript语言中并没有提供类的定义能力,我们只能直接创建对象。

2.10.1 创建对象

JavaScript语言中虽然不能定义类,但可以直接创建对象,面向对象的效果是一样的。JavaScript语言中创建对象代码的写法与其他常见语言(如Java、C#和C++等)完全不同,有多种函数可以创建JavaScript中的对象。下面分别介绍。

1.采用字面量创建对象

JavaScript中的对象与数值、字符串等都属于基本数据类型,它们可以使用字面量来表示。对象字面量类似于JSONJSON(JavaScript Object Notation),是一种轻量级的数据交换格式。对象,采用对象字面量表示的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属性相减。

我们不仅可以使用原型扩展对象函数,还可以扩展对象的属性。