面向对象的 JavaScript

面向对象的 JavaScript

文章来源:《JavaScript 实战》(ISBN:9787115189158)P19

大多数的 JavaScript 开发人员的生活从他们发现原型(prototype)那天就开始改变了。一旦他们发现所有的 JavaScript 对象都可以通过其原型来扩展,并且这个功能允许他们创建自定义类,世界就不可能再回到以前那样了。比如,看看下面的代码:

var answer = 0;
function addNumbers(num1, num2){
  answer = num1 + num2;
}
function subtractNumbers(num1, num2){
  answer = num1 - num2;
}
function multiplyNumbers(num1, num2){
  answer = num1 * num2;
}
function divideNumbers(num1, num2){
  if(num2 != 0){
    answer = num1 / num2;
  }else {
    answer = 0;
  }
}

这段代码技术上没有什么错误,它能够运行。但它组织得足够好吗?我看不怎么样。在全局作用域里的 answer 变量代码味太浓,每个函数都是如此:都是孤立的函数,都在全局作用域里。编写更专业的 JavaScript 代码的风格就是:不要污染全局作用域。

在大多数的其他语言里面,使用全局变量会被认为是一个坏习惯,因为不在局部作用域里就意味着它们可以在程序的任何部分被修改,导致全局依赖和难以定位的问题(通常都是转瞬即逝的,很难察觉)。JavaScript 里面也是一样。全局作用域里面的函数相对没有那么头疼,不过,缺乏结构意外着在函数之间没有内在的关系,并且没有逻辑上的分组来帮助人们更高的级别来理解它们。

与之对比,让我们看看用更加面向对象的方式重写代码会如何:

function NumberFunctions(){
  var answer = 0;
}
NumberFunctions.prototype.addNumbers = function (num1, num2) {
    this.answer = num1 + num2;
}
NumberFunctions.prototype.subtractNumbers = function (num1, num2) {
  this.answer = num1 - num2;
}
NumberFunctions.prototype.multiplyNumbers = function (num1, num2) {
  this.answer = num1 * num2;
}
NumberFunctions.prototype.divideNumbers = function (num1, num2) {
  if(num2 != 0){
    this.answer = num1 / num2;
  }else {
    this.answer = 0;
  }
}
NumberFunctions.prototype.toString = function () {
  return this.answer;
}

要使用这段代码,我们需要做这样的事情:

var nf = new NumberFunctions();
nf.addNumbers(2, 1);
alert(nf);
nf.subtractNumbers(10, 3);
alert(nf);
nf.multiplyNUmbers(4, 5);
alert(nf);
nf.divideNumbers(12, 6);
alert(nf);

这个版本的代码有如下的优点:

  • 没有对全局作用域的污染,因为有 NumberFunctions 函数。对于 answer 变量来说,这是最重要的。因为它是使用 var 关键字声明的,因此就不能从类的外部访问,只有 NumberFunctions 的函数可以修改它。
  • 所有函数实际上都是 NumberFunctions 类的成员,因此构造了一个清晰的关系。
  • 基本的面向对象:数据和操作数据的函数都封装得很好。