首页 » 编写高质量代码:改善JavaScript程序的188个建议 » 编写高质量代码:改善JavaScript程序的188个建议全文在线阅读

《编写高质量代码:改善JavaScript程序的188个建议》建议104:谨慎使用伪类

关灯直达底部

JavaScript的原型存在诸多矛盾,某些看起来有点像基于类的语言的复杂语法问题遮蔽了它的原型机制。原型不但不让对象直接从其他对象继承,反而插入了一个多余的间接层,从而使构造器函数产生对象。当一个函数对象被创建时,Function构造器产生的函数对象会运行类似这样的一些代码:


this.prototype={constructor:this};


新函数对象被赋予一个prototype属性,其值包含一个constructor属性且属性值为该新函数对象。该prototype对象是存放继承特征的地方。因为JavaScript语言没有提供一种方法来确定哪个函数是用做构造器的,所以每个函数都会得到一个prototype对象,constructor属性没什么太大作用,重要的是prototype对象。

定义一个构造器并扩展它的原型:


var Mammal=function(name){

this.name=name;

};

Mammal.prototype.get_name=function{

return this.name;

};

Mammal.prototype.says=function{

return this.saying||'';

};


构造实例:


var myMammal=new Mammal('mammal');

var name=myMammal.get_name;//'mammal'


构造另一个伪类来继承Mammal,这是通过定义它的constructor函数并替换它的prototype为一个Mammal的实例来实现的。


var Cat=function(name){

this.name=name;

this.saying='meow';

};

Cat.prototype=new Mammal;

Cat.prototype.purr=function(n){

var i,s='';

for(i=0;i<n;i+=1){

if(s){

s+='-';

}

s+='r';

}

return s;

};

Cat.prototype.get_name=function{

return this.says+''+this.name+''+this.says;

};

var myCat=new Cat('cat');

var says=myCat.says;//'meow'

var purr=myCat.purr(5);//'r-r-r-r-r'

var name=myCat.get_name;//'meow cat meow'


伪类模式的本意是想向面向对象靠拢,但它看起来与面向对象格格不入。我们可以隐藏一些“丑陋”的细节,这是通过使用method方法定义一个inherits方法来实现的。


Function.method('inherits',function(Parent){

this.prototype=new Parent;

return this;

});


inherits和method方法都返回this,这样就可以通过链式语法只用一行语句构造Cat。


var Cat=function(name){

this.name=name;

this.saying='meow';

}.inherits(Mammal).method('purr',function(n){

var i,s='';

for(i=0;i<n;i+=1){

if(s){

s+='-';

}

s+='r';

}

return s;

}).method('get_name',function{

return this.says+''+this.name+''+this.says;

});


隐藏了prototype操作细节,现在看起来就没那么怪异了。现在有了行为像“类”的构造器函数,但它们没有私有环境,所有的属性都是公开的,因此无法访问父类super的方法。同时,使用构造器函数存在一个严重的隐患。如果在调用构造器函数时忘记了在前面加上new前级,那么this将不会被绑定到一个新对象上,而是被绑定到全局对象上,这样不但没有扩充新对象,反而会破坏全局变量。发生这种情况时,既没有编译时警告,也没有运行时警告。

这是一个严重的语言设计错误。为了降低这个问题带来的风险,所有的构造器函数都约定命名成首字母大写的形式,并且不以首字母大写的形式拼写任何其他的东西。这样至少可以通过人工检查去发现是否缺少了new前缀。当然,一个更好的备选方案就是根本不使用new。

伪类形式可以给不熟悉JavaScript的程序员提供便利,但它也隐藏了该语言的真实本质。借鉴类的表示法可能误导程序员去编写过于深入和复杂的层次结构。许多复杂的类层次结构的产生就是源于静态类型检查的约束。JavaScript完全摆脱了这些约束。在基于类的语言中,类的继承是代码重用的唯一方式。JavaScript有着更多且更好的选择。