定义类或对象
1 工厂方式
对象的属性可在对象创建后动态定义
问题是可能需要创建多个car实例。
所有这些问题引发了开发者定义的构造函数的出现。
2 构造函数方式
在构造函数内部无创建对象,而是使用this关键字。
现在,用new运算符和类名car创建对象,就更像创建ECMAScript中一般对象了。你也许会问,这种方式在管理函数方面是否存在与前一种方式相同的问题呢?是的。
就像工厂函数,构造函数会重复生成函数,为每个对象都创建独立的函数版本。
3 原型方式
该方式利用了对象的prototype属性,可把它看成创建新对象所依赖的原型。
这里,用空构造函数来设置类名。
然后所有的属性和方法都被直接赋予prototype属性。
首先,这个构造函数没有参数。使用原型方式时,不能通过给构造函数传递参数初始化属性的值,
因为car1和car2的color属性都等于"red",doors属性都等于4,mpg属性都等于23。
这意味必须在对象创建后才能改变属性的默认值,这点很令人讨厌,但还不至于是世界末日。
真正的问题出现在属性指向的是对象,而不是函数时。
函数共享不会造成任何问题,但对象却很少被多个实例共享的。
由于创建对象时有这么多问题,你一定会想,是否有种合理的创建对象的方法呢?答案是联合使用构造函数和原型方式。
4 混合的构造函数/原型方式
联合使用构造函数和原型方式,就可像用其他程序设计语言一样创建对象。
这种概念非常简单,即
用构造函数定义对象的所有非函数属性,
用原型方式定义对象的函数属性(方法)。
function Car (sColor, iDoors, iMpg){
this.color = sColor;
this.doors = iDoors;
this.mpg = iMpg;
this.drivers = new Array("Mike","Sue");
}
Car.prototype.showColor = function(){
alert(this.color);
};
var oCar1 = new Car("red", 4, 23);
var oCar2 = new Car("black", 5, 24);
oCar1.drivers.push("Matt");
alert(oCar1.drivers);//outputs Mike Sue Matt
alert(oCar2.drivers);//outputs Mike Sue
这种方式是ECMAScript主要采用的方式,它具有其他方式的特性,却没有它们的副作用。不过,有些开发者仍觉得这种方法不够完美。
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);
};
}
}
直到检查typeof Car._initialized是否等于"undefined"之前,这个构造函数都未发生变化。
这行代码是动态原型方法中最重要的部分。
如果这个值未定义,构造函数将用原型方式继续定义对象的方法,然后把Car._initialized设置为true。
如果这个值定义了(它的值为true时,typeof的值为Boolean),那么就不再创建该方法。
简而言之,该方法使用标志(_initialized)来判断是否已给原型赋予了任何方法。
该方法只创建并赋值一次,为取悦传统的OOP开发者,这段代码看起来更像其他语言中的类定义了。
6 混合工厂方式
这种方式通常是在不能应用前一种方式时的变通方法。它的目的是创建假构造函数,只返回另一种对象的新实例。
这种方式在对象方法的内部管理方面与经典方式有着相同的问题。
强烈建议:除非万不得已(请参阅第15章),还是避免使用这种方式。
7 采用哪种方式
如前所述,目前使用最广泛的是混合的构造函数/原型方式。
此外,动态原型方法也很流行,在功能上与构造函数/原型方式等价。
可以采用这两种方式中的任何一种。
不过不要单独使用经典的构造函数或原型方式,因为这样会给代码引入问题。
8 实例
对象令人感兴趣的一点是用它们解决问题的方式。ECMAScript中最常见的一个问题是字符串连接的性能。
与其他语言类似,ECMAScript的字符串是不可变的,即它们的值不能改变。考虑下面的代码:
var str = "hello";
str += "world";
实际上,这段代码在幕后执行的步骤如下:
(1) 创建存储"hello"的字符串。
(2) 创建存储"world"的字符串。
(3) 创建存储连接结果的字符串。
(4) 把str的当前内容复制到结果中。
(5) 把"world"复制到结果中。
(6) 更新str,使它指向结果。
每次完成字符串连接都会执行步骤2到6,使得这种操作非常消耗资源。如果重复这一过程几百次,甚至几千次,就会造成性能问题。
解决方法是用Array对象存储字符串,然后用join()方法(参数是空字符串)创建最后的字符串。想像用下面的代码代替前面的代码:
var arr = new Array();
arr[0] = "hello";
arr[1] = "world";
var str = arr.join("");
这样,无论在数组中引入多少字符串都不成问题,
因为只在调用join()方法时才会发生连接操作。
此时,执行的步骤如下:
(1) 创建存储结果的字符串。
(2) 把每个字符串复制到结果中的合适位置。
虽然这种解决方法很好,但还有更好的方法。
问题是这段代码不能确切反映出它的意图。要使它更容易理解,可以用StringBuffer类打包该功能:
function StringBuffer(){
this._strings_ = new Array;
}
StringBuffer.prototype.append = function(str){
this._strings_.push(str);
};
StringBuffer.prototype.toString = function(){
return this._strings_.join("");
};
第一点要注意的是,这段代码是strings的属性,本意是私有属性。
它只有两个方法,即append()和toString()方法。
append()方法有一个参数,它把该参数附加到字符串数组中,
toString()方法调用数组的join()方法,返回真正连接成的字符串。
要用StringBuffer对象连接一组字符串,可以用下面的代码:
var buffer = new StringBuffer();
buffer.append("hello");
buffer.append("world");
alert(buffer.toString());
可用下面代码测试StringBuffer对象和传统的字符串连接方法的性能:
这段代码对字符串连接进行两个测试,第一个使用加号,第二个使用StringBuffer类。每个操作都连接10000个字符串。日期值d1和d2用于判断完成操作需要的时间。记住,创建新Date对象时,如果没有参数,赋予对象的是当前的日期与时间。要计算连接操作历经多少时间,把日期的毫秒表示(getTime()方法的返回值)相减即可。这是衡量JavaScript性能的常用方法。该测试的结果应该说明使用StringBuffer类比使用加号节省了100%~200%的时间。
分享到:
相关推荐
《JavaScript高级程序设计》是JavaScript开发者的经典教材,它深入探讨了这门语言的核心概念和技术。这篇读书笔记主要聚焦在第三部分,这部分通常涵盖了更高级的主题,如对象、原型、闭包以及模块化等。结合提供的...
### HTML5高级程序设计学习笔记 #### 一、HTML5新增结构标签 在HTML5中,为了更好地组织页面内容并增强语义性,引入了一系列新的结构标签,这些标签不仅能够帮助开发者更清晰地定义页面的不同部分,同时也为搜索...
在《JavaScript高级程序设计》中,对ECMAScript中引用类型的相关知识点进行了详细阐述。引用类型在JavaScript中是非常重要的概念,它们相较于原始类型而言,在内存中占有较大的比重,并且其行为也更为复杂。以下是对...
Window对象 窗口操作 Window对象对操作浏览器窗口非常有用,开发者可以移动或调整浏览器窗口的大小。可用四种方法实现这些操作: moveBy(dx,dy):把浏览器窗口相对当前位置水平移动dx个像素,垂直移动dy个像素。...
JavaScript中的继承机制是其面向对象编程的关键特性,允许一个对象或类从另一个对象或类获取并复用属性和方法。本文将深入探讨JavaScript中的五种主要继承实现方式:对象冒充、call()、apply()、原型链以及混合方式...
JavaScript 中定义类或对象的方式 JavaScript 中定义类或对象有多种方式,包括工厂方式、构造函数方式、原型方式、混合的构造函数/原型方式和动态原型方法。 工厂方式是通过创建并返回特定类型的对象的函数来定义...
JavaScript中的Function类是一种特殊的数据类型,它可以用来动态创建或表示任何开发者定义的函数。通过`new Function()`构造函数,我们可以传递参数列表和函数体来创建一个新的函数。例如,`var sayHi = new ...
JavaScript,作为一种广泛应用于Web开发的脚本语言,其核心规则和规范由ECMAScript标准定义。ECMA-262是这个标准的正式名称,它详细规定了JavaScript的语法、类型、关键字、保留字、操作符以及对象。ECMAScript为...
### JavaScript高级程序设计读书笔记之九:本地对象Array #### 创建Array对象 在JavaScript中,`Array`是一种非常重要的本地对象,主要用于存储一系列有序的数据。创建`Array`对象的方法有多种,具体包括: 1. **...
### JavaScript高级程序设计阅读笔记(七):ECMAScript中的语句 #### 一、引言 本篇文章基于《JavaScript高级程序设计》一书第七章的内容进行总结与扩展,主要探讨了ECMAScript中的各种语句类型及其用法。通过...
在JavaScript中,内置对象是指由ECMAScript标准实现并提供的一类特殊对象,它们独立于宿主环境(例如浏览器或Node.js环境)存在,并在程序执行时自动创建。这些对象提供了许多基本功能和常用方法,使得开发者可以...
JavaScript高级程序设计中的第十七章主要探讨了JS事件的相关概念和处理机制。事件流、事件处理函数以及事件对象是这一章的核心内容。 **事件流**是指事件在DOM(文档对象模型)中传播的方式。在IE中,事件流采用的...
### JavaScript中的Date对象详解 #### 一、概述 在JavaScript编程语言中,`Date`对象是一种内置对象,用于处理日期和时间数据。它提供了一系列的方法和属性,可以帮助开发者轻松地进行日期时间的操作,如获取当前...