JavaScript提供了一套丰富的代码重用模式。它可以模拟那些基于类的模式,同时可以支持其他更具表现力的模式。我们下面将研究几种最为直接的模式。
伪类 Pseudoclassical
JavaScript的原型存在这许多的矛盾,它不直接让对象从其他对象继承,反而插入了一个多余的间接层:通过构造函数产生对象。
当一个函数对象被创建时,Function构造器产生的函数对象会运行类似这样的一些代码:
this.prototype = {constructor: this};
新函数对象被赋予一个Prototype属性,它的值是一个包含consrtuctor属性且属性值为该新函数的对象。
我们可以定义一个构造器并扩充它的原型
1 var Mammal = function(name){ 2 this.name = name; 3 }; 4 5 Mammal.prototype.get_name = function(){ 6 return this.name; 7 }; 8 9 Mammal.prototype.says = function(){ 10 return this.saying || ''; 11 }; 12 13 var mymammal = new Mammal('QQQ'); 14 var name = mymammal.get_name(); 15 16 //我们可以构造另一个伪类来继承Mammal,这是通过定义它的constructor函数并替换它的prototype为一个Mammal的实例来实现的: 17 18 var Cat = function(name){ 19 this.name = name; 20 this.saying = 'OW'; 21 }; 22 23 Cat.prototype = new Mammal(); 24 25 Cat.prototype.purr = function(n){ 26 var i, s = ''; 27 for(i = 0;i < n;i++){ 28 if(s){ 29 s += '-'; 30 } 31 s += 'r'; 32 } 33 return s; 34 }; 35 36 Cat.prototype.get_name = function(){ 37 return this.says() + ' ' + this.name + ' ' + this.says(); 38 }; 39 40 var myCat = new Cat('XMM'); 41 var says = myCat.says(); //"OW" 42 var myname = myCat.get_name(); //"OW XMM OW" 43 var pr = myCat.purr(5); //"r-r-r-r-r"
这种方法没有私有属性,所有的属性都是公开的,无法访问父类的方法。更糟糕的是,使用构造器函数存在一个严重的危害,如果你在调用构造器函数时忘记了在前面加上new前缀,那么this将不会绑定到一个新对象上。而是绑定到全局对象上
原型 Prototype
让我们先用对象字面量去构造一个有用的对象
1 var myMammal = { 2 name: 'QQQ', 3 get_name: function(){ 4 return this.name; 5 }, 6 says: function(){ 7 return this.says || ' '; 8 } 9 }; 10 11 //定制新的实例 12 var myCat = Object.create(myMammal); 13 myCat.name = 'XMM'; 14 myCat.says = 'OW'; 15 myCat.pr = function(n){ 16 var i, s = ''; 17 for(i = 0;i < n;i++){ 18 if(s){ 19 s += '-'; 20 } 21 s += 'r'; 22 } 23 return s; 24 }; 25 myCat.get_name = function(){ 26 return this.says() + ' ' + this.name + ' ' + this.says(); 27 };
这是一种“差异化继承”。通过定制一个新的对象,我们指明它与所基于的基本对象的区别。
函数化 Function
迄今为止,我们所看到的继承模式的一个弱点就是没办法保护隐私。对象的所有属性都是可见的。我们无法得到私有变量和私有函数。现在,我们有一个更好的选择,那就是应用模块模式
我们从构造一个生成对象的函数开始。我们以小写字母开头,因为它并不需要new前缀。该函数包含4个步骤
【1】创建一个新对象,有很多的方式去创建一个对象,他可以构造一个对象字面量,或者用构造函数,或者使用Object.create(),或者调用一个会返回一个对象的函数。
【2】有选择的定义私有变量和方法
【3】给这个对象扩充方法。这些方法拥有特权去访问参数
【4】返回那个新对象
这里是一个函数化构造器的伪代码模板:
1 var constructor = function(spec,my){ 2 var that,其他私有变量; 3 my = my || {}; 4 5 把共享的变量和函数添加到my中 6 7 that = 一个新对象 8 9 添加给that的特权方法 10 11 return that; 12 };
让我们把这个模式应用到mammal例子里。此处不需要my,所以我们先抛开它,但会使用一个spec对象。
1 var mammal = function (spec){ 2 var that = {}; 3 4 that.get_name: function(){ 5 return this.name; 6 }; 7 that.says: function(){ 8 return this.says || ' '; 9 }; 10 11 return that; 12 }; 13 14 var myMammal = mammal({name: 'Herb'});