JavaScript 高级程序设计 - 引用类型

应用类型的值是应用类型的一个实例。在 ECMAScript 中,引用类型是一种数据结构,用于将数据和功能组织在一起,它也常被称为 ,但这种称呼并不妥当。尽管 ECMAScript 从技术上讲是一门面向对象的语言,但它不具备传统的面向对象语言所支持的类和接口等基本结构。引用类型有时候也被称为对象定义,因为它们描述的是一类对象所具有的属性和方法。

Object 类型

Object 是 ECMAScript 中使用最多的一个类型。虽然 Object 的实例不具备多少功能,但对于在应用程序中存储和传输数据而言,它们确实是非常理想的选择。

创建 Object 实例的两种方式 :

  • 使用 new 操作符

    1
    2
    var person = new Object();
    person.name = "CocoaLei";
  • 使用对象字面量

    1
    2
    3
    var person = {
    name = "CocoaLei"
    };

    对象字面量是对象定义的一种简写形式 ,目的在于简化创建包含大量属性的对象的过程。

    对象字面量也是向函数传递大量可选参数的首选方式。

一般来说,访问对象属性时使用的都是点语法,这也是很多面向对象语言中通用的语法。不过,在 JavaScript 中也可以使用方括号表示法来访问对象的属性。

1
2
3
4
5
6
7
var person = {
name = "CocoaLei"
};
console.log(person.name); // CocoaLei
console.log(person["name"]); // CocoaLei
var propertyName = "name";
console.log(person[propertyName]); // CocoaLei

通常,除非必须使用变量来访问属性,否则建议使用点语法。

Array 类型

Array 类型是出了 Object 之外 ECMAScript 中最常用的类型了。而且,ECMAScript 中的数组与其它语言中的数组有着相当大的区别。虽然 ECMAScript 数组与其它语言中的数组都是数据的有序列表,但与其它语言不通的是, ECMAScript 中的数组的每一项可以保存任何类型的数据。并且,ECMAScript 数组的大小是可以动态调整的,即可以随着数据的添加自动增长以容纳新增数据。

创建数组的两种方式 :

  • Array 构造函数

    1
    var colors = new Array();

    如果预先知道数组要保存的项目数量,也可以给构造函数传递该数量,而改数量会自动编程 length 的值。

    1
    var colors = new Array(255);
  • 使用数组字面量

    1
    var colors = ["red", "yellow", "blue"];

在读取和设置数组的值时,要使用方括号并提供相应值的基于 0 的数字索引。

数组最多可以包含 4294967295 个项,这几乎已经能够满足任何编程需求了。如果想添加的项数超过这个上限值,就会发生异常。而创建一个初始大小与这个上限值接近的数组,则可能会导致运行时间超长的脚本错误。

检测数组

如果假定只有一个全局执行环境,使用 instanceof 操作符就能检测值是不是数组。但是,实际上存在的全局环境不止一个,那么 Array 构造函数也不止一个。

为了解决这个问题,ECMAScript 5 新增了 Array.isArray() 方法。这个方法的母的是最终确定某个值到底是不是数组,而不管它在哪个全局执行环境中创建的。

1
Array.isArray(value);

转换方法

所有对象都具有 toLocalString()toString()valueOf() 方法。调用数组的 toString() 方法会返回数组中每个值得字符串形式拼接而成的一个以逗号分隔的字符串。调用 valueOf() 方法返回的还是数组。

数组继承的 toLocalString()toString()valueOf() 方法,在默认情况下都会以逗号分隔的字符串的形式返回数组项。而如果使用 join() 方法,则可以使用不同的分隔符来构建这个字符串。join() 方法只接受一个参数,即用作分隔符的字符串,然后返回包含所有数组项的字符串。

如果数组中的某一项的值是 nullundefined , 那么该值在 toLocalString()toString()valueOf() 方法返回的结果中以空字符串表示。

栈方法

ECMAScript 数组也提供了一种让数组的行为类似于其它数组结构的方法。具体来说,数组可以表现的像栈一样。

ECMAScript 专门为数组提供了push()pop() 方法,以便实现类似栈的行为。

push() 方法可以接收任意数量的参数,把它们逐个添加到数组末尾,并返回修改后数组的长度。而 pop() 方法则从数组末尾移除最后一项,减少数组的 length 值,然后返回移除的项。

1
2
3
var sampleArr = new Array();
var arrLength = sampleArr.push("A","B"); // arrLength is 2 and sampleArr is ["A", "B"]
var removedItem = sampleArr.pop(); // removedItem is "B"

队列方法

栈数据结构的访问规则是后进先出,而队列数据结构的访问规则是先进先出。队列在列表的末端添加项,从列表的前端移除项。由于 push() 是向数组末端添加项的方法,因此要模拟队列只需一个从数组前端取得项的方法。实现这一操作的数组方法就是 shift() ,它能够移除数组中的第一个项并返回该项,同时将数组长度减 1 。结合使用 shift()push() 方法,就可以像使用队列一样使用数组。

1
2
3
var sampleArr = new Array();
sampleArr.push("A", "B");
var firstItem = sampleArr.shift(); // firstItem is "A"

ECMAScript 还未数组提供了一个 unshift() 方法,它能在数组前端添加任意个项并返回新数组的长度。

重排序方法

数组中已经存在两个可以直接用来重排序的方法 :reverse()sort()

数组的 reverse() 方法会反转数组的顺序 :

1
2
var originalArr = [3, 7, 1, -1, 22];
originalArr.reverse(); // 22, -1, 1, 7, 3

在默认情况下,sort() 方法按升序排列数组。为了实现排序,sort() 方法会调用数组中每项的 toString() 方法,然后比较得到的字符串,以确定如何排序。这种排序方式在很多情况下都不是最佳方案。不过,sort() 方法可以接收一个比较函数作为参数,以便我们指定哪个值位于哪个值的前面。

1
2
3
4
5
6
7
8
9
10
// 升序
function compare (value_1, value_2) {
if (value_1 < value_2) {
return -1;
} else if (value_1 > value_2) {
return 1;
} else {
return 0;
}
}

reverse()sort() 方法的返回值是经过排序后的数组。

对于数值类型或者其 valueOf() 方法会返回数值类型的对象类型,可以使用一个更简单的比较函数。这个函数只要用第二个值减第一个值即可。

1
2
3
function compare (value_1, value_2) {
return value_2 - value_1;
}

由于比较函数通过返回一个小于零、等于零或大于零的结果来影响排序,因此减法操作就可以适当地处理所有这些情况。

操作方法

ECMAScript 为操作已经包含在数组中的项提供了很多方法。

contact()

contact() 方法可以基于当前数组的所有项创建一个新数组。具体来说,这个方法会先创建当前数组的一个副本,然后将接收到的参数添加到这个副本的末尾,最后返回新构建的数组。在没有给 contact() 方法传递参数的情况下,它只是复制当前数组并返回副本。如果传递给 contact() 方法的是一或多个数组,则该方法会将这些数组中的每一项都添加到结果数组中。如果传递的不是数组,这些值就会被简单地添加到结果数组的末尾。

1
2
3
var sampleArr = nes Array();
sampleArr.push("A", "B");
var copyArr = sampleArr.contact("C"); // now sampleArr is ["A", "B", "C"]
slice()

slice() 方法能够基于当前数组中的一个或多个项创建一个新数组。slice() 方法可以接受一个或两个参数,即要返回项的起始和结束位置。在只有一个参数的情况下,slice() 方法返回从该参数指定位置开始到当前数组末尾的所有项。如果有两个参数,该方法返回起始和结束位置之间的项(不包括结束位置)。

1
2
3
var sampleArr = [1, 2, 3, 4, 5, 6, 7];
var resultArr = sampleArr.slice(3); // resultArr is [4, 5, 6, 7]
resultArr = sampleArr.slice(3,5); // resultArr is [4, 5]

如果 slice() 方法中有一个负数,则用数组长度加上该数来确定相应的位置。如果结束位置小于起始位置,则返回空数组。

splice()

splice() 方法主要用途是向数组的中部插入项,但使用这种方法的方式则有如下三种 :

  • 删除 :可以删除任意数量的项,只需指定要删除的第一项的位置和要删除的项数两个参数

    1
    2
    var sampleArr = [1, 2, 3, 4, 5, 6, 7];
    sampleArr.splice(5, 1); // sampleArr is [1, 2, 3, 4, 5, 6]
  • 插入 :可以向指定位置插入任意数量的项,只需提供起始位置、要删除的项数 (设为0) 和要插入的项

    1
    2
    var sampleArr = [1, 2, 3];
    sampleArr.splice(1, 0, 2.1, 2.2); // sampleArr is [1, 2, 2.1, 2.2, 3]
  • 替换 :可以向指定位置插入任意数量的项,同时删除任意数量的项

    1
    2
    var sampleArr = [1, 2, 3];
    sampleArr.splice(2, 1, 4); // sampleArr is [1, 2, 4]

splice() 方法始终都会返回一个数组,该数组包含从原始数组中删除的项 (如果没有删除任何项,则返回一个空数组) 。

位置方法

ECMAScript 5 为数组实例添加了两个位置方法 :indexOf()lastIndexOf() 。这两个方法都接收两个参数 :要查找的项和表示查找七点位置的索引。其中,indexOf() 从数组的开头开始向后查找,lastIndexOf() 则是从数组的末尾开始向前查找。

这两个方法都返回要查找的项在数组中的位置,或者在没有找到的情况下返回 -1 。在比较第一个参数与数组中的每一项时,会使用全等操作符。

1
2
3
var sampleArr = [1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1];
sampleArr.indexOf(4); // 3
sampleArr.lastIndexOf(4); // 7

迭代方法

ECMAScript 5 为数组定义了 5 个迭代方法,每个方法都接收两个参数 :要在每一项上运行的函数和运行该函数的作用域对象。传入这些方法中的函数会接收三个参数 :数组项的值,该项在数组中的位置和数组对象的本身。

  • every() : 对数组中的每一项运行给定函数,如果该函数对每一项都返回 true ,则返回 true
  • filter() :对数组中的每一项运行给定函数,返回该函数会返回 true 的项组成的数组
  • forEach() :对数组中的每一项运行给定函数,这个方法没有返回值
  • map() :对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组
  • some() :对数组中的每一项运行给定函数,如果该函数对任一项返回 true ,则返回 true

以上这些方法都不会修改数组中包含的值。

归并方法

ECMAScript 5 增加了两个归并数组的方法 :reduce()reduceRight() 。这两个方法都会迭代数组所有的项,然后构建一个最终返回的值。其中,reduce() 方法从数组的第一项开始,逐个遍历到最后。而 reduceRight() 则从数组的最后一项开始,向前遍历到第一项。

这两个方法都接收两个参数 :一个在每一项上调用的函数和作为归并基础的初始值。传给 reduce()reduceRight() 的函数接收 4 个参数 :前一个值、当前值、项的索引和数组对象。这个函数返回的任何值都会作为第一个参数自动传给下一项。第一次迭代发生在数组的第二项上,因此第一个参数是数组的第一项,第二个参数是数组的第二项。

求数组中所有值之和 :

1
2
3
4
var sampleArr = [1, 2, 3, 4, 5];
var sum = sampleArr.reduce(function(prev, cur, index, array) {
return prev + cur;
}); // sum is 15

Date 类型

ECMAScript 中的 Date 类型是在早期 Java 中的 java.util.Date 类基础上构建的。为此,Date 类型使用 UTC 1970 年 1 月 1 日午夜开始经过的毫秒数来保存日期。这使用这种数据存储格式的条件下,Date 类型保存的日期能够精确到 1970 年 1 月 1 日之前或之后的 285616 年。

在调用 Date 构造函数而不传递参数的情况下,新创建的对象自动获得当前的日期和时间。如果想根据特定的日期和时间创建日期对象,必须传入表示该日期的毫秒数。为了简化这一计算过程,ECMAScript 提供了两个方法 : Date.parse()Date.UTC()

其中,Date.parse() 方法接收一个表示日期的字符串参数,然后尝试根据这个字符串返回相应日期的毫秒数。ECMA 没有定义 Date.parse() 应该支持哪种日期格式,因此这个方法的行为因实现而异。

Date.UTC() 方法同样也返回表示日期的毫秒数,但它与 Date.parse() 在构建值时使用不同的信息。Date.UTC() 的参数分别是年份、基于 0 的月份、月中的哪一天、小时数、分钟、秒以及毫秒数。在这些参数中,只有前两个参数是必需的。

Date.now() 方法会返回表示调用这个方法时的日期和时间的毫秒数。

继承的方法

与其它引用类型一样,Date 类型也重写了 toLocalString()toString()valueOf() 方法 。Date 类型的 toLocalString() 方法会按照与浏览器设置的地区相适应的格式返回日期和时间。而 toString() 方法则通常返回带有时区信息的日期和时间。Date 类型的 valueOf() 方法,则根本不返回字符串,而是返回日期的毫秒表示,常用来比较日期值的大小。

1
2
3
4
5
var date_pre = new Date(2017, 0, 1);
var date_next = new Date(2017, 1, 1);
if (date_pre < date_next) {
consolo.log("");
}

日期格式化方法

Date 类型用于将日期格式化为字符串的方法 :

  • toDateString() :以特定于实现的格式显示星期几、月、日和年
  • toTimeString() :以特定于实现的格式显示时、分、秒和时区
  • toLocalDateString() :以特定于地区的格式显示星期几、月、日和年
  • toLocalTimeString() :以特定于实现的格式显示时、分、秒
  • toUTCString() :以特定于实现的格式显示完整的 UTC 日期

RegExp 类型

ECMAScript 通过 RegExp 类型来支持正则表达式 :

1
var expression = /pattern/flags;

其中的模式部分可以是任何简单或复杂的正则表达式,可以包含字符类、限定符、分组、向前查找以及反向引用。每个正则表达式都可以带有一或多个标志,用以表明正则表达式的行为。

正则表达式的匹配模式下支持下列 3 个标志 :

  • g :全局模式,即模式将被应用于所有字符串,而非在发现第一个匹配项时立即停止
  • i :不区分大小写模式,即在确定匹配项时忽略模式与字符串的大小写
  • m :多行模式,即在到达一行文本末尾时还会继续查找下一行中是否在与模式匹配的项

模式中使用的所有元字符都必须转义。正则表达式中的元字符包括 :

1
( [ { \ ^ $ | ) ? * + . ] }

这些元字符在正则表达式中都有一或多种特殊用途,因此如果想要匹配字符串中包含的这些字符,就必须对它们进行转义。

使用 RegExp 构造函数构造正则表达式时,需要注意 :传递的两个参数都是字符串。由于 RegExp 的构造函数的模式参数是字符串,所以在某些情况下要对字符进行双重转义。所有元字符都必须双重转义,那些已经转义过的字符也是如此。

1
2
3
字面量模式			等价的字符串
/\[bc\]at/ “\\[bc\\]at”
/\w\\hello\\123/ "\\w\\\\heollo\\\\123"

使用正则表达式字面量和使用 RegExp 构造函数创建的正则表达式不一样。在 ECMAScript 3 中,正则表达式字面量始终会共享同一个 RegExp 实例,而使用构造函数创建的每一个新 RegExp 实例都是新实例。

ECMAScript 5 明确规定,使用正则表达式字面量必须像直接调用 RegExp 构造函数一样,每次赌创建新的 RegExp 实例。

RegExp 实例属性

RegExp 的每个实例都具有下列属性 :

  • global :布尔值,表示是否设置了 g 标志
  • ignoreCase :布尔值,表示是否设置了 i 标志
  • lastIndex :整数,表示开始搜索下一个匹配项的字符位置,从 0 算起
  • multiline :布尔值,表示是否设置了 m 标志
  • source :正则表达式的字符串表示,按照字面量模式而非传入构造函数中的字符串模式返回

RegExp 实例方法

RegExp 对象的主要方法是 exec() ,该方法是专门为捕获组而设计的。 exec() 接受一个参数,即要应用模式的字符串,然后返回包含第一个匹配项信息的数组或者在没有匹配项的情况下返回 null 。返回的数组虽然是 Array 的实例,但包含两个额外的属性 :indexinput 。其中 index 表示匹配项在字符串的位置,而 input 表示应用正则表达式的字符串。在数组中,第一项是与整个模式匹配的字符串,其它项是与模式中单的捕获组匹配的字符串。

对于 exec() 方法而言,即使在模式中设置了全局标志,它每次也只会返回一个匹配项。在不设置全局标志的情况下,在同一个字符串上多次调用 exec() 将始终返回第一个匹配项的信息。而在设置全局标志的情况下,每次调用 exec() 则都会在字符串中继续查找新匹配项。

正则表达式的第二个方法是 test() ,它接受一个字符串参数。在模式与该参数匹配的情况下返回 true ;否则返回 false 。在只想知道目标字符串与某个模式是否匹配,但不需要知道其文本内容的情况下,使用这个方法

作者

Y2hlbmdsZWk=

发布于

2017-05-24

更新于

2021-09-01

许可协议