与其他高级语言不同,javascript的类和对象比较特殊,这里将详细阐述。
1.javascript没有真正的类。
2.因为不是强类型语言,所以javascript不支持早期绑定。
3.js中,所有的对象并非同等创建的,一般分为三类:
(1)本地对象:定义为“独立于宿主环境的js实现提供的对象”。包括Object,Function,Array,String,Boolean,Number,Date,RegExp,Error,EvalError,RangeError,ReferrenceError,SyntaxError,TypeError,URIError.
(详细介绍见另一篇文章(javascript常用本地对象的使用))
(2) 内置对象:定义为“独立于宿主环境的js实现提供的对象,在程序开始执行时出现”。从定义可以看出,每个内置对象都是本地对象。内置对象有两个:Global,Math.
(3) 宿主对象:定义为:“有js实现的宿主环境所提供的对象”。所有的DOM和BOM对象都是宿主对象。
4.作用域。
对js讲对象的作用域几乎毫无意义,因为js只存在一种作用域,公用作用域。因此,定义自己的类或对象时,必须格外小心,因为默认它们都是公用的。
5.this关键字
this总是指向调用该方法的对象。
var oCar = new Object;
oCar.color = "red";
oCar.showColor = function (){
alert(this.color); //outputs "red",this指向oCar
}
6.定义类:
(1).工厂方式
function createCar(sColor,iDoors,iMpg){
var oCar = new Object;
oCar.color = sColor;
oCar.doors = iDoors;
oCar.mpg = iMpg;
oCar.showcolor = function(){
alert(this.color);
}
return oCar;
}
var oCar1 = CreateCar("red",4,23);
var oCar2 = CreateCar("blue",2,20);
oCar1.showColor();//ouputs "red"
oCar2.showColor();//outputs "blue"
这种工厂方式的缺点是:每次调用craeteCar,都要创建新函数showcolor(),每一个对象都有自己的showcolor版本
(2).构造函数方式
function createCar(sColor,iDoors,iMpg){
var oCar = new Object;
this.color = sColor;
this.doors = iDoors;
this.mpg = iMpg;
this.showcolor = function(){
alert(this.color);
}
}
var oCar1 = new createCar("red",4,23);
var oCar2 = new createCar("blue",2,20);
同工厂方法一样,构造函数方式同样会重复生成函数,为每个对象创建独立的函数版本
(3).原型方式
function Car(){
Car.prototype.color = "red";
Car.prototype.doors = 4;
Car.prototype.mpg = 23;
Car.prototype.showcolor = function(){
alert(this.color)
}
}
var oCar1 = new Car();
var oCar2 = new Car();
该方法利用了对象的prototype属性,用空的构造函数来设置类名,然后所有的属性和方法都被直接赋予prototype属性。
问题:1.构造函数没有参数,使用原型方式时,不能通过给构造函数传递参数初始化属性;2.也是最致命的问题,当属性指向的是对象
,而不是函数时,对象是被多个实例共享的(函数共享不会造成任何问题):
function Car(){
Car.prototype.color = "red";
Car.prototype.doors = 4;
Car.prototype.mpg = 23;
Car.prototype.dirvers = new Array("Mike","Sue");
Car.prototype.showcolor = function(){
alert(this.showcolor);
}
}
var oCar1 = new Car();
var oCar2 = new Car();
oCar1.dirvers.push("Matt");
alert(oCar1.dirvers)://outputs "Mike,Sue,Matt"
alert(oCar2.dirvers)://outputs "Mike,Sue,Matt"
改变oCar1.dirvers,同时也改变了oCar2.dirvers !!!
(4).混合的构造函数/原型方式
这种方式汲取了构造函数和原型方式的优点:
function Car(sColor,iDoors,iMpg){
this.color = sColor;
this.doors = iDoors;
this.mpg = iMpg;
this.dirvers = new Array("Mike","Sue");
}
Car.prototype.showcolor = function(){
alert(this.color);
}
var oCar1 = new Car("red",4,23);
var oCar2 = new Car("blue",2,23);
oCar1.drivers.push("Matt");
alert(oCar1.drivers);//outpust "Mike,Sue,Matt"
alert(oCar2.drivers);//outputs "Mike,Sue"
这种方式,所有的非函数属性都在构造函数里创建,而在原型里创建函数showcolor,避免了内存的浪费。除了视觉上的不习惯,这种解决方式堪称完美。
(5).动态原型方法
上面提到,混合的构造函数/原型方式,给人的视觉上不习惯。封装这一概念体现的很不明确。于是产生了动态原型方法,它与上面方法的
唯一区别是赋予对象方法的位置:
function Car(sColor,iDoors,iMpg){
this.color = sColor;
this.doors = iDoors;
this.mpg = iMpg;
this.drivers = new Array("Mike","Sue");
if(typeof Car._initialized =="undefined"){
Car.prototype.showcolor = function(){
alert(this.color);
}
}
car._initialized = true;
}
这样封装,传统的OOP开发者会高兴地发现,这样看起来舒服多了。
(6).混合工厂方式
function Car(){
var oCar = new Object;
oCar.color ="red";
oCar.doors = 4;
oCar.mpg =23;
oCar.showcolor = function (){
alert(this.color);
}
return oCar;
}
var car = new Car();
与工厂方式不同,仅仅是生成对象的方式,使用了new。其实因为构造函数内部调用了new,这个new将被忽略。
这种方式与经典工厂方式存在相同的问题。极不提倡使用这种方式。