《JavaScript高级程序设计》学习笔记(对象4)

《JavaScript高级程序设计》学习笔记(对象4)

 使用预定义对象的能力只是面向对象语言的能力的一部分。真正的强大之处在于能够创建自己专用的类和对象。与ECMAScript中的许多特性一样,可以用各种方法实现这一点。


1  工厂方式

对象的属性可在对象创建后动态定义
例如:
var car= new Object;
car.color = "red";
car.show = function(){alert(this.color);};
car.show();
这里创建对象car,然后给它设置"color"属性,和show方法

可以用能创建并返回特定类型的对象的工厂函数(factory function),用来需要创建多个car实例
例如:
function createCar(){
 var obj= new Object;
 obj.color = "red";
 obj.show = function(){alert(this.color);};
 return obj;
}
var car1 = createCar();
var car2 = createCar();
car1.show();
car2.show();
这里,前面的所有代码都包含在createCar()函数中,并返回car对象作为(obj)函数值。
调用此函数时,将创建新对象,并赋予它所有必要的属性,复制出一个前面说明的car对象。
使用该方法,可以容易地创建car对象的两个版本(car1和car2),它们的属性完全一样。

可以修改createCar()函数,给它传递各个属性的默认值,而不是赋予属性默认值
例如:
function createCar(col){
 var obj= new Object;
 obj.color = col;
 obj.show = function(){alert(this.color);};
 return obj;
}
var car = new createCar("red");
car.show();
给createCar()函数加上参数,即可为要创建的car对象的color属性赋值。
注意:每创建一个实例都会新建一个对象给

这样创建对象的方法有两个缺点:
1 语义:不像使用带有构造函数的new运算符那么正规
2 功能:用这种方式必须创建对象的方法
前面的例子中,每次调用函数createCar(),都要创建新函数show(),意味着每个对象都有自己的show()方法,事实上,每个对象都共享了同一个函数。

在工厂函数外定义对象的方法,然后通过属性指向该方法,可以避开这个问题
例如:
var show = function(){alert(this.color);};
function createCar(col){
 var obj= new Object;
 obj.color = col;
 obj.show = show;
 return obj;
}
var car = new createCar("red");
car.show();
在这段重写的代码中,在函数createCar()前定义了函数show()。在createCar()内部,赋予对象一个指向已经存在的showColor()函数的指针。
从功能上讲,这样解决了重复创建函数对象的问题,但该函数看起来不像对象的方法。

 

2  构造函数方式

构造函数方式的类名,即构造函数的名字,根据惯例,这个名字的首字母大写,以便区分
除了这点不同,构造函数看起来很像工厂函数
例如:
function Car(col){
 this.color = col;
 this.show = function(){alert(this.color);};
}
var car = new Car("red");
与工厂函数的区别是在构造函数内部无创建对象,而是使用this关键字。
使用new运算符调用构造函数时,在执行第一行代码前先创建一个对象,只有用this才能访问该对象。
然后可以直接赋予this属性,默认情况下是构造函数的返回值(不必明确使用return运算符)。

但就像工厂函数,构造函数会重复生成函数,为每个对象都创建独立的函数版本。不过,与工厂函数相似,也可以用外部函数重写构造函数,同样的,语义上无任何意义。这就是原型方式的优势所在。

 

3  原型方式

该方式利用了对象的prototype属性,可把它看成创建新对象所依赖的原型。
用空构造函数来设置类名,然后所有的属性和方法都被直接赋予prototype属性。
例如:
function Car(){}
Car.prototype.color = "red";
Car.prototype.show = function(){alert(this.color);};
var car = new Car();
首先定义构造函数(Car),然后通过给Car的prototype属性添加属性定义Car对象的属性。
调用new Car()时,原型的所有属性都被立即赋予要创建的对象,即所有Car实例存放的都是指向show()函数的指针。
所有属性看起来都属于一个对象,不会重复生成函数。当然,car也可以自定义相同的属性而不影响Car的prototype属性。

使用该方法,可以用instanceof运算符检查给定变量指向的对象的类型
例如:
alert(car instanceof Car);

缺点:
(1)不能通过给构造函数传递参数初始化属性的值,而必须在对象创建后才能修改属性的默认值
(2)当属性指向的是对象时,对象也会被共享,但实际上这不是我们想要的
例如:
function Car(){}
Car.prototype.colors = new Array("red","black");
var car1 = new Car();
var car2 = new Car();
car1.colors.push("yellow");
alert(car1.colors) //显示"red,black,yellow"
alert(car2.colors) //显示"red,black,yellow"

 

4  混合的构造函数/原型方式

联合使用构造函数和原型方式,即用构造函数定义对象的所有非函数属性,用原型方式定义对象的函数属性(方法)。结果所有函数都只创建一次,而每个对象都具有自己的对象属性实例。
例如:
function Car(col){
 this.color = col;
}
Car.prototype.show = function(){alert(this.color);};
var car = new Car("red");
所有的非函数属性都在构造函数中创建,每个对象都具有自己的对象属性实例。
只创建show()函数的一个实例,节省资源。
由于使用了原型方式,能用instanceof运算符判断对象的类型。


5  动态原型方法

动态原型方法的基本想法与混合的构造函数/原型方式相同,即在构造函数内定义非函数属性,而函数属性则利用原型属性定义。唯一的区别是赋予对象方法的位置,对属性和方法进行了视觉上的封装。
例如:
function Car(col){
 this.color = col;
 if (typeof Car._initialized == "undefined"){
  Car.prototype.show = function(){alert(this.color);};
  Car._initialized = true;
 }
}
var car = new Car("red");

该方法使用标志(_initialized)来判断是否已给原型赋予了任何方法。


6  混合工厂方式

这种方式通常是在不能应用前一种方式时的变通方法。它的目的是创建假构造函数,只返回另一种对象的新实例。
例如:
function Car(){
 var obj= new Object;
 obj.color = "red";
 obj.show = function(){alert(this.color);};
 return obj;
}
var car = new Car();

与经典方式不同,这种方式使用new运算符,使它看起来像真正的构造函数。
由于在Car()构造函数内部调用了new运算符,所以将忽略第二个new运算符(位于构造函数之外)。
但这种方式在对象方法的内部管理方面与经典方式有着相同的问题,不推荐使用。

 

修改对象

每个构造函数都有个prototype属性,可用于定义方法。
在ECMAScript中,每个本地对象也有个用法完全相同的prototype属性。


1  创建新方法

可以用prototype属性为任何已有的类定义新方法。
例如:
Array.prototype.enqueue = function(vItem){
 this.push(vItem);
}
Array.prototype.dequeue = function(){
 return this.shift();
}


2  重定义已有方法

函数名只是指向函数的指针,因此可以轻易地使它指向其他函数。
例如:
Function.prototype.toString = function(){
 return "code hidden";
}
function show(){
 alert("hi");
}
alert(show.toString());

覆盖Function的toString()方法,toString()指向的原始函数将被无用存储单元回收程序回收,原始函数将不能恢复,所以在覆盖原始方法前,存储它的指针比较安全,以便以后的使用。


3  极晚绑定

ECMAScript中能够在对象实例化后再定义它的方法。
例如:
var o = new Object;
Object.prototype.show = function(){
 alert("hi");
};
o.show();

在大多数程序设计语言中,必须在实例化对象之前定义对象的方法。
不建议使用极晚绑定方法,因为很难对其跟踪和记录。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值