JavaScript面向对象编程探讨:实战代码(二)

   当我们的代码需要一个属性的时候,Javascript 的引擎会先看当前的这个对象中是否有这个属性,如果没有的话,就会查找他的 Prototype 对象是否有这个属性,一直继续下去,直到找到或是直到没有 Prototype 对象。

  为了证明这个事,我们可以使用 Object.getPrototypeOf ()来检验一下:

 Student.name = 'aaa';

  //输出 aaa

  document.write('

' + Student.name + '

');

  //输出 Chen Hao

  document.write('

' +Object.getPrototypeOf(Student).name + '

');

  于是,你还可以在子对象的函数里调用父对象的函数,就好像 C++ 里的 Base::func () 一样。于是,我们重载 hello 的方法就可以使用父类的代码了,如下所示:

//新版的重载SayHello方法

  Student.sayHello = function (person) {

  Object.getPrototypeOf(this).sayHello.call(this);

  var hello = "my student no is: " + this. no + ",
" +

  "my departent is: " + this. dept;

  document.write(hello + '
');

  }

  这个很强大吧。

  组合

  上面的那个东西还不能满足我们的要求,我们可能希望这些对象能真正的组合起来。为什么要组合?因为我们都知道是这是 OO 设计的最重要的东西。不过,这对于 Javascript 来并没有支持得特别好,不好我们依然可以搞定个事。

  首先,我们需要定义一个 Composition 的函数:(target 是作用于是对象,source 是源对象),下面这个代码还是很简单的,就是把 source 里的属性一个一个拿出来然后定义到 target 中。

function Composition(target, source)

 {

 var desc = Object.getOwnPropertyDescriptor;

 var prop = Object.getOwnPropertyNames;

 var def_prop = Object.defineProperty;

 prop(source).forEach(

 function(key) {

 def_prop(target, key, desc(source, key))

 }

 )

 return target;

 }

 有了这个函数以后,我们就可以这来玩了:

//艺术家

  var Artist = Object.create(null);

  Artist.sing = function() {

  return this.name + ' starts singing...';

  }

  Artist.paint = function() {

  return this.name + ' starts painting...';

  }

  //运动员

  var Sporter = Object.create(null);

  Sporter.run = function() {

  return this.name + ' starts running...';

  }

  Sporter.swim = function() {

  return this.name + ' starts swimming...';

  }

  Composition(Person, Artist);

  document.write(Person.sing() + '
');

  document.write(Person.paint() + '
');

  Composition(Person, Sporter);

  document.write(Person.run() + '
');

  document.write(Person.swim() + '
');

  //看看 Person中有什么?(输出:sayHello,sing,paint,swim,run)

  document.write('

' + Object.keys(Person) + '
');

  Prototype和继承

  我们先来说说 Prototype。我们先看下面的例程,这个例程不需要解释吧,很像C语言里的函数指针,在C语言里这样的东西见得多了。

var plus = function(x,y){

  document.write( x + ' + ' + y + ' = ' + (x+y) + '
');

  return x + y;

  };

  var minus = function(x,y){

  document.write(x + ' - ' + y + ' = ' + (x-y) + '
');

  return x - y;

  };

  var operations = {

  '+': plus,

  '-': minus

  };

  var calculate = function(x, y, operation){

  return operations[operation](x, y);

  };

  calculate(12, 4, '+');

  calculate(24, 3, '-');

  那么,我们能不能把这些东西封装起来呢,我们需要使用 prototype。看下面的示例:

var Cal = function(x, y){

  this.x = x;

  this.y = y;

  }

  Cal.prototype.operations = {

  '+': function(x, y) { return x+y;},

  '-': function(x, y) { return x-y;}

  };

  Cal.prototype.calculate = function(operation){

  return this.operations[operation](this.x, this.y);

  };

  var c = new Cal(4, 5);

  c.calculate('+');

  c.calculate('-');

  这就是 prototype 的用法,prototype 是 javascript 这个语言中最重要的内容。网上有太多的文章介始这个东西了。说白了,prototype 就是对一对象进行扩展,其特点在于通过“复制”一个已经存在的实例来返回新的实例,而不是新建实例。被复制的实例就是我们所称的“原型”,这个原型是可定制的(当然,这里没有真正的复制,实际只是委托)。上面的这个例子中,我们扩展了实例 Cal,让其有了一个 operations 的属性和一个 calculate 的方法。

  这样,我们可以通过这一特性来实现继承。还记得我们最最前面的那个 Person 吧, 下面的示例是创建一个 Student 来继承 Person。

function Person(name, email, website){

  this.name = name;

  this.email = email;

  this.website = website;

  };

  Person.prototype.sayHello = function(){

  var hello = "Hello, I am "+ this.name + ",
" +

  "my email is: " + this.email + ",
" +

  "my website is: " + this.website;

  return hello;

  };

  function Student(name, email, website, no, dept){

  var proto = Object.getPrototypeOf;

  proto(Student.prototype).constructor.call(this, name, email, website);

  this.no = no;

  this.dept = dept;

  }

  // 继承prototype

  Student.prototype = Object.create(Person.prototype);

  //重置构造函数

  StudentStudent.prototype.constructor = Student;

  //重载sayHello()

  Student.prototype.sayHello = function(){

  var proto = Object.getPrototypeOf;

  var hello = proto(Student.prototype).sayHello.call(this) + '
';

  hello += "my student no is: " + this. no + ",
" +

  "my departent is: " + this. dept;

  return hello;

  };

  var me = new Student(

  "Chen Hao",

  "haoel@hotmail.com",

  "http://coolshell.cn",

  "12345678",

  "Computer Science"

  );

  document.write(me.sayHello());