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

DOJO中的面向对象__第一章 JS中的对象模型

DOJO中的面向对象

?

  在JS中并没有Java等面向对象语言中常见的类(class)的概念。也就是说,JS中的对象并非基于类的。它仅仅为开发者提供了一些原类型和基本的内置对象。从写法上来看,它更加的类似于面向过程式的语言,函数仿佛成了JS中的顶级实体。事实上,JS是一门函数式编程的语言。所以当我们需要以面向对象的方式来构建大型web应用时,原生态的JS并不能很好的满足这一点。而DOJO的出现完美的解决了这个问题,它使得程序员能够以传统的面向对象的思维来进行开发,进而使JS用起来更加得心应手。

?

第一章 JS中的对象模型

(一) 构造器(伪类)

  在JS中创建一个对象很容易,JS提供了语法糖允许我们以字面量的方式来构建对象。例如:

var foo={	'name':'Jack' }

?

  但是在JS中构建一个类却显得稍微复杂。由于JS并没有从语言层面上支持任何自定义类型,所以我们只能通过模拟的方式来构建出一个类。这得益于JS中的强大的函数以及原型机制。先来看一个简单的例子:

function Foo(){
	this.name='Jack';
}
var foo=new Foo();
console.log(foo.name) // 输出jack

?

  在这个例子中,Foo已经不仅仅是一个简单的函数了,我们将Foo看成一个‘伪类’(在javascript中称为构造器)。因此可以用new运算符来生成该类型的对象。通常JS中的类型构造都采用该方法。新生成的对象将自动拥有‘伪类’中定义的字段,所以此例中生成的foo将拥有name属性。

?

  注意Foo中的this.name='Jack'。由于JS中的某些不良设计,一般的函数调用会将该函数中的this绑定到全局对象上,这使得this的使用容易造成混乱。通常而言,如果并不涉及到面向对象编程,可以不必使用this。只有存在了某个对象,this的使用才会有意义。


  如果对构造器进行new运算,构造器中的this会被绑定到生成的新的对象。换句话说,上例中new Foo()时,Foo中的this会被绑定到新生成的实例foo。可以猜测,对一个Foo调用new运算符的时候,会发生类似于下面的过程:?

var obj=new Object();	//obj是一个新生成的对象
Foo.apply(obj);			//将Foo中的this绑定到obj
var foo=obj;			//最后将obj引用返回给foo
?

(二) prototype是什么

  JS中的继承有点特殊,在JS中并不存常见的基于类的继承。JS中的继承是对象与对象之间的行为。也就是说,一个对象可以直接继承另一个对象里的属性。而这一切,都是依靠prototype来完成的。示例如下:

var foo={
	'name':'Jack'
}
function Bar(age){
	this.age=age;
}
Bar.prototype=foo;
var bar=new Bar(22);

?

  这个例子中,我们首先创建了一个对象foo,它包含一个name属性。然后我们创建了一个构造器Bar,由于将Bar当做类来使用,所以将其首字母大写。随后我们将Bar的原型指向foo对象。接着,我们以new的方式来创建了一个Bar的实例bar。很显然,对象bar中包含了两个属性,name属性值为Jack,还有age属性,值为22。值得考究的是Bar. prototype=foo这一句。该语句将Bar的原型设定成一个对象foo。这一句的运行结果是通过Bar创建的所有对象都将继承foo对象的属性。于是,接下来bar便从foo中继承了name属性。

?

  推广开来说, JS中的每个构造器都有一个prototype属性。JS里的构造器,除了包括了我们上面自己定义的‘伪类’,还包括了内置的Object、Function、String 、Array、Number、Date、RegExp等等一系列函数。prototype本身也是一个对象,也就是我们所说的原型对象,如果我们用构造器创建了一个新的对象,该对象便与原型对象发生了继承关系。注意,JS中的继承是发生在两个对象之间的关系,而JAVA之中的继承是两个类之间的关系。


  JS中的继承有两个突出的特点,一是动态性,二是对修改封闭。下面的例子阐述了这两点,例一:

var foo={ 'name':'Jack' }			
function Bar(age){
	this.age=age;
}			
Bar.prototype=foo;			
var bar=new Bar(22);
console.log(bar.name)    //Jack
foo.name='Mike';
console.log(bar.name)    //Mike

?

  当我们修改了foo的属性时,通过bar来访问这些属性也会收到影响。也就是说,我们可以将一些需要的特性动态添加到JS的对象中。这是一种非常强大的编程技术。比如JS中的String对象缺少trim方法。通过

String.prototype.trim=function(){//dojo中的实现
  return this.replace(/^\s\s*/,'').
  replace(/\s\s*$/,'');
} 

?

语句,可以为所有的string加上trim方法。

例二:

var foo={ 'name':'Jack' }
function Bar(age){
	this.age=age;
}
Bar.prototype=foo;
var bar=new Bar(22);
bar.name='Mike';
console.log(bar.name)    // Mike
console.log(foo.name)    // Jack

?

  从上例中可以清楚的看出,如果我们试图通过修改bar来影响foo,这样是行不通的。通过bar可以访问foo的属性,但是却无法改变这些属性的值。当我们修改bar.name='Mike'之后,foo的name值依然是Jack。

?

(三) 原型链(prototype chain)

  事实上,在bar对象中,有一个看不见的_proto属性。该属性指向了Bar.prototype,也就是foo。在Ecma-262 3rd Edition中有如下描述:

写道
Each constructor has a Prototype property that is used to implement prototype-based inheritance and shared properties.
写道
Every constructor has an associated prototype, and every object created by that constructor has an implicit reference to the prototype associated with its constructor.

?

  这段话的意思是JS中的构造器都拥有一个prototype属性。每个由构造器创建出来的object都含有一个指向该prototype的隐式引用。

function Foo(){
	this.name='Jack';
}
var foo=new Foo();
function Bar(age){
	this.age=age;
}
Bar.prototype=foo;
var bar=new Bar(22);

?

因此,上例可以表示成: