日期:2014-05-16  浏览次数:20345 次

如何编写可维护的面向对象JavaScript代码

作者: Ara Pehlivanian  发布时间: 2012-04-25 22:34  阅读: 880 次  原文链接   全屏阅读  [收藏] 
  英文原文:How to Write Maintainable OO JavaScript Code
  能够写出可维护的面向对象 JavaScript 代码不仅可以节约金钱,还能让你很受欢迎。不信?有可能你自己或者其他什么人有一天会回来重用你的代码。如果能尽量让这个经历不那么痛苦,就可以节省不少时间。地球人都知道,时间就是金钱。同样的,你也会因为帮某人省去了头疼的过程而获得他的偏爱。但是,在开始探索如何编写可维护的面向对象 JavaScript 代码之前,我们先来快速看看什么是面向对象。如果已经了解面向对象的概念了,就可以直接跳过下一节。
  什么是面向对象?
  面向对象编程主要通过代码代表现实世界中的实质对象。要创建对象,首先需要写一个“类”来定义。 类几乎可以代表所有的东西:账户,员工,导航菜单,汽车,植物,广告,饮料,等等。而每次要创建对象的时候,就从类实例化一个对象。换句话说,就是创建类的实例做为对象。事实上,通常处理一个以上的同类事物时就会使用到对象。另外,只需要简单的函数式程序就可以做的很好。对象实质上是数据的容器。因此在一个 employee 对象中,你可能要储存员工号,姓名,入职日期,职称,工资,资历,等等。对象也包括处理数据的函数(也叫做“方法”)。方法被用作媒介来确保数据的完整性,以及在储存之前对数据进行转换。例如,方法可以接收任意格式的日期然后在储存之前将其转化成标准化格式。最后,类还可以继承其他的类。继承可以让你在不同类中重复使用相同代码。例如,银行账户和音像店账户都可以继承一个基本的账户类,里面包括个人信息,开户日期,分部信息,等等。然后每个都可以定义自己的交易或者借款处理等数据结构和方法。
  警告:JavaScript 面向对象是不一样的
  在上一节中,概述了经典的面向对象编程的基本知识。说经典是因为 JavaScript 并不遵循这些规则。相反地,JavaScript 的类是写成函数的样子,而继承则是通过原型实现的。原型继承基本上意味着使用原型属性来实现对象的继承,而不是从类继承类。
  对象的实例化
  以下是 JavaScript 中对象实例化的例子:
// 定义 Employee 类
function Employee (num, fname, lname) {
    this.getFullName = function () {
        return fname + " " + lname;
    }
};
// 实例化 Employee 对象
var john = new Employee ("4815162342", "John", "Doe");
alert ("The employee's full name is " + john.getFullName ());
  在这里,有三个重点需要注意:
  1. “class”函数名的第一个字母要大写。这表明该函数的目的是被实例化而不是像一般函数一样被调用。
  2. 在实例化的时候使用了 new 操作符。如果省略掉 new 而仅仅调用函数则会产生很多问题。
  3. 因为 getFullName 指定给 this 操作符了,所以是公共可用的,但是 fname 和 lname 则不是。由 Employee 函数产生的闭包给了 getFullName 到 fname 和 lname 的入口,但同时对于其他类仍然是私有的。
  原型继承
  下面是 JavaScript 中原型继承的例子:
// 定义 Human 类
function Human () {
    this.setName = function (fname, lname) {
        this.fname = fname;
        this.lname = lname;
    }
    this.getFullName = function () {
        return this.fname + " " + this.lname;
    }
}

// 定义 Employee 类
function Employee (num) {
    this.getNum = function () {
        return num;
    }
};
//让 Employee 继承 Human 类
Employee.prototype = new Human ();

// 实例化 Employee 对象
var john = new Employee ("4815162342");
    john.setName ("John", "Doe");
alert (john.getFullName () + "'s employee number is " + john.getNum ());
  这一次,创建的 Human 类包含人类的一切共有属性——我也将 fname 和 lname 放进去了,因为不仅仅是员工才有名字,所有人都有名字。然后将 Human 对象赋值给它的 prototype 属性。
  通过继承实现代码重用
  在前面的例子中,原来的 Employee 类被分解成两个部分。所有的人类通用属性被移到了 Human 类中,然后让 Employee 继承 Human。这样的话,Human 里面的属性就可以被其他的对象使用,例如 Student(学生),Client(顾客),Citizen(公民),Visitor(游客),等等。现在你可能注意到了,这是分割和重用代码很好的方式。处理 Human 对象时,只需要继承 Human 来使用已存在的属性,而不需要对每种不同的对象都重新一一创建。除此以外,如果要添加一个“中间名字”的属性,只需要加一次,那些继承了 Human 类的就可以立马使用了。反而言之,如果我们只是想要给一个对象加“中间名字”的属性,我们就直接加在那个对象里面,而不需要在 Human 类里面加。
  Public(公有的)和 Private(私有的)
  接下来的主题,我想谈谈类中的公有和私有变量。根据对象中处理数据的方式不同,数据会被处理为私有的或者公有的。私有属性并不一定意味着其他人无法访问。可能只是某个方法需要用到。
  只读
  有时,你只是想要在创建对象的时候能有一个值。一旦创建,就不想要其他人再改变这个值。为了做到这点,可以创建一个私有变量,在实例化的时候给它赋值。
function Animal (type) {
    var data = [];
    data['type'] = type;
    this.getType = function () {
        return data['type'];
    }
}

var fluffy = new Animal ('dog');
fluffy.getType (); // 返回 'dog'
  在这个例子中,Animal 类中创建了一个本地数组 data。当 Animal 对象被实例化时,传递了一个 type 的值并将该值放置在 data 数组中。因为它是私有的,所以该值无法被覆盖(Animal 函数定义了它的范围)。一旦对象被实例化了,读取 type 值的唯一方式是调用 getType 方法。因为 getType 是在 Animal 中定义的,因此凭借 Animal 产生的闭包,getType 可以进