JavaScript 引用类型
引用类型
引用类型的值是引用类型的一个实例。在ECMAScript中,引用类型是一种数据结构,用于将数据和功能组织在一起。它也被称为类,引用类型有时候也被称为对象定义,因为它表述的是一类对象所具有的属性和方法。
ECMAScript提供了很多原生引用类型。
Object类型
Object是ECMAScript使用最多的一个类型。创建Object实例的方式有两种:
1 | var person=new Object(); |
使用对象字面量表示法。
1 | var person={ |
另一种表示方法:
1 | var person={};//与new Object()相同 |
Array类型
虽然ECMAScript中的数据与其他语言中的数组都是数据的有序列表,但与其他语言不同的是,ECMAScript数组的每一项可以保存任何类型的数据。也就是说,可以用数组的第一个位置来保存字符串,第二个位置来保存数值,
第三个位置来保存对象,以此类推。而且ECMAScript数组的大小是可以动态调整的,即可以随着数据的添加自动增长以容纳新增数据。
- var colors=new Array();在使用Array构造函数的时候,可以省略new操作符。
- 使用数组字面量方法。
var colors=[“red”,“blue”,“green”]//创建一个包含三个字符串的数组
利用length属性在数组末尾添加新项:1
2
3var colors=[“red”,“blue”,“green”];//创建一个包含三个字符串的数组
colors[colors.length]=“black”;//在位置3添加一种颜色
colors[colors.length]=“brown”;//在位置4添加一种颜色
检测数组
用instanceof
操作符的问题在于,它假定只有一个全局执行环境。如果网页中包含多个框架,那实际上就存在两个以上不同的全局执行环境,从而存在两个以上不同版本的Array构造函数。为了解决这个问题,ECMAScript5
新增了Array.isArray()
方法。这个方法的目的是最终确定某个值到底是不是数组,而不管他是在那个全局执行环境中创建的。
栈方法
ECMAScript专门为数组提供了push()
和pop()
方法,以实现数组类似栈的行为。push()
方法可以接收任意数量的参数,把它们逐个添加到数组末尾,并返回修改后数组的长度。而popo()
方法则从数组末尾移除最后一项,减少数组的lengh值,然后返回移除的项。例如:
1 | var color=new Array(); |
队列方法
实现队列操作的数组方法就是shift()
,它能够移除数组中的第一个项并返回该项,同时将数组长度减1.结合shift()
和push()
方法,可以像使用队列一样使用数组。
1 | var color=new Array(); |
ECMAScript还为数组提供了一个unshift()
方法 ,同时使用unshift()
和pop()
方法,可以从相反的方向来模拟队列,即在数组的前端添加项,从数组末端移除项。
重排序方法
数组中已经存在两个可以直接用来重排序的方法:reverse()和sort()
。reverse()方法会反转数组项的顺序。
1 | var value=[1,2,3,4,5]; |
sort()按升序排列数组项———即最小的值位于前面,最大的值位于后面。为了实现排序,sort()方法会调用每个数组项的toString()转型方法,然后比较得到的字符串,以确定如何排序。即使数组中
的每一项都是数值,sort()方法比较的也是字符串,例如:
1 | var value=[0,1,5,10,15] |
这种比较方法在很多时候都不是最佳方案,因此sort()方法可以接收一个比较函数作为参数,以便我们指定哪个值位于哪个值的前面。比较函数接收两个参数,如果第一个参数位于第二个参数前面就返回一个负数,如果两个
参数相等,就返回0,如果第一个参数位于第二个参数后面就返回一个正数。
比较函数:
1 | function compare(value1,value2){ |
引用例如:
1 | var value=[0,1,5,10,15]; |
操作方法
其中concat()
方法可以基于当前数组中的所有项创建一个新数组。具体来说,这个方法会先创建当前数组一个副本,然后将接收到的参数添加到这个副本的末尾,最后返回新构建的数组。
下一个方法是slice()
,它能够基于当前数组中的一个或多个项创建一个新数组。可以包含以一个或两个参数,只有一个参数,则返回从该参数指定位置开始到结束位置的项。如果有两个参数,则返回起始位置和结束位置
之间的项,但是不包括结束位置的项。slice()
方法不会影响原数组。
splice()
1 | 删除:可以删除任意数量的项,splice(0,2)会删除数组中的前两项,要删除的第一项的位置和要删除的项数 |
splice()方法始终都会返回一个数组,该数组包含从原数组删除的项(没有删除就是返回一个空数组)。
位置方法
indexOf()和lastIndexOf()。其中indexOf()方法从数组的开头开始向后查找,lastIndexOf()方法则从数组的末尾开始向前查找。这两个方法都返回要查找项在数组中的位置,或者在没找到的情况下返回-1.
归并方法
reduce()和reduceRight()。这两个方法都会迭代数组的所有项,然后构建一个最终返回的值,其中reduce()从数组的第一项开始,reduceRight()从数组的最后一项开始遍历到第一项。
Date类型
创建一个日期对象:var now =new Date()
;再调用Date()构造函数而不传递参数的情况下,创建的对象自动获得当前日期和时间。如果想获得特定的日期和时间可用两个方法Date.parse()
和Date.UTC()
。
其中Date.parse()
表示接收一个表示日期的字符串参数,然后尝试根据这个字符串返回相应日期的毫秒数。例如为2004年5月25日创建一个日期对象:var someDate=new Date(Date.parse(“May 25,2004”)).
等价于var someDate=new Date(“May 25,2004”)
.因为后台会自动调用Date.parse()
方法。
Date.UTC()在构建数值时使用不同的信息。参数分别是年份、基于0的月份(一月是0,二月是1,以此类推)、月中的哪一天、小时数、分钟、秒以及毫秒数。只有两个参数是必须的:年和月。如果没有提供天数,则
假设天数为1,如果省略其他参数,则统统假设为0。
Date.now表示调用这个方法时的日期和时间的毫秒数
日期格式化方法
- toDateString():以特定于实现的格式显示星期几、月、日和 年
- toTimeString():以特定的格式显示时、分、秒和时区
- toLocaleDateString():以特定于地区的格式显示星期几、月、日和年
- toLocaleTimeString():以特定的格式显示时、分、秒
- toUTCStriing():以特定于实现的格式完整的UTC日期
RegExp类型
通过该类型来支持正则表达式。语法:var expression=/pattern/flags
;每个正则表达式都可带有一个或多个标志,用以标明正则表达式的行为,正则表达式的匹配模式支持下列三个标志:
- g:表示全局模式,即模式将被应用于所有字符串,而非在发现第一个匹配时立即停止;
- i:表示不区分大小写模式,即在确定匹配项时忽略模式与字符串的大小写
- m:表示多行模式,即在到达一行文本末尾时还会继续查找下一行中是否存在与模式匹配的项。
例如:另一种创建正则表达式的方法是使用RegExp构造函数,它接收两个参数:一个是要匹配的字符串表达式,另一个是可选的标志字符串。1
匹配字符串中所有“at”的实例:var pattern1=/at/g; 匹配第一个“bat”或“cat”,不区分大小写:var pattern2=/【bc】at/i; 匹配所有以“at”结尾的3个字符的组合,不区分大小写:var pattern3=/.at/gi 模式中所有的元字符必须使用转义,例如 匹配第一个“【bc】at”,不区分大小写:var pattern2=/\[bc\]at/i; 匹配所有“.at”,不区分大小写:var pattern4=/\.at/gi;
匹配第一个“bat”或“cat”,不区分大小写:var pattern2=/[bc]at/i;
与pattern2相同,只不过是使用构造函数创建的:var pattern3=new RegExp(“[bc]at”,“i”);
RegExp实例属性
- global:布尔值,表示是否设置了g标志。
- ignoreCase:布尔值,表示是否设置了i标志。
- lastIndex:整数,表示开始搜索下一个匹配项的字符位置,从0算起。
- multiline:布尔值,表示是都设置了m标志。
- source:正则表达式的字符串表示,按照字面量形式而非传入构造函数中的字符串模式返回。
实例方法
对象的主要方法有exec()
和test()
两种。
对于exec()方法来讲,即使在模式中设置了全局标志(g),它每次也会返回一个匹配项。在不设置全局标志的情况下,在同一个字符串上多次调用exec()将始终返回第一个匹配项的信息。
1 | var text=“000-00-0000”; |
在这个例子中,我们使用正则表达式来测试了一个数字序列。如果输入的文本与模式匹配,则显示一条消息。这种用法经常出现在验证用户输入的情况下,因为我们想知道输入是不是有效。
RegExp实例继承的toLacaleString()
和toString()
方法都会返回正则表达式的字面量,与创建正则表达式的方式无关。
1 | var pattern=new RegExp(“\\[bc\\]at”,"gi"); |
Function类型
函数是对象,函数名是指针。所以一个函数可能会有多个名字。例如:
1 | function sum(num1,num2){return sum1+sum2;} |
声明变量anotherSum与sum相等。此时sum和anotherSum都指向了同一个函数,因此anotherSum()也可以调用并返回结果。即使sum设置为null,但仍然可以正常调用anotherSum()。
没有重载
1 | function add(num){return num+100;} |
这个例子声明了两个同名函数,结果是后面的函数覆盖了前面的 函数。以上代码和以下代码没有区别:
1 | var add=function(num){return num+100;}; |
通过这个例子可以看出,在创建第二个函数时,实际上覆盖引用了第一个函数的变量。
函数内部属性
在函数内部,有两个特殊的对象:arguments
和this
。其中,arguments是一个类数组对象,包含着传入函数的所有参数。虽然arguments的主要用途是保存函数参数,但是这个对象还有一个名叫callee的属性,该属性是
一个指针,指向拥有这个arguments对象的函数。例子:
1 | function factorial(num){ |
在函数有名字,而且名字以后也不会变的情况下,这样定义没有问题。但问题是这个函数的执行与函数名factorial紧紧耦合在了一起。为了消除这种耦合现象,可以用下列代码:
1 | function factorial(num){ |
没有引入函数名,这时无论函数使用的是什么名字,都可以保证正常完成递归调用。
1 | var factorial1=factorial; |
如果是用函数名来调用的话,这个函数出来的结果两个都为0.
一定要记住,函数的名字仅仅是一个包含指针的变量而已,函数才是对象,当复制函数名给另一个变量的时候,这时两个变量名都指向函数内容对象。
函数内部的另一个特殊对象是this。this引用的是函数执行的环境对象——或者也可以说是this值(当在网页的全局作用域中调用函数时,this对象引用的就是window)
函数属性和方法
每个函数都包含两个属性:length
和prototype
。其中,length属性表示函数希望接收的命名参数的个数。
对于ECMAScript中的引用类型而言,prototype是保存它们所有实例方法的真正所在。诸如toString()和valueOf()方法都保存在prototype名下。prototype属性是不可枚举的,,因此使用for-in方法无法实现。
每个函数都包括两个非继承而来的方法:apply()
和call()
。这两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内this对象的值。首先,apply()方法接收两个参数:
- 一个是在其中运行函数的作用域,另一个是参数数组。
- 第二个参数可以是Array实例也可以是arguments对象。
例如:call方法跟apply的作用相同,它们的区别仅在于接收参数的方式不同。对于call()方法而言,第一个参数值是this值没有变化,变化的是其余参数都直接传递给函数。也就是说在使用call()方法时,传递给函数的参数1
2
3
4
5function sum(num1,num2){return num1+num2;}
function sum1(num1,num2){return sum.apply(this,arguments);}//传入arguments对象
function sum2(num1,num2){return sum.apply(this,【num1,num2】)};//传入数组
alert(sum1(10,10));//20
alert(sum2(10,10));//20
必须逐个列举出来,如:事实上,传递参数并非call和apply的最大作用,它们真正强大的地方是能够扩充函数赖以运行的作用域。使用它们来扩充作用域的最大好处就是对象不需要与方法有任何耦合关系。1
2
3function sum(num1,num2){return num1+num2;}
function sum1(num1,num2){return sum.call(this,num1,num2);}
alert(sum1(10,10));//20
基本包装类型
ECMAScript还提供了3个特殊的引用类型:Boolean()
、Number()
、String()
。
引用类型与基本包装类型的只要区别就是对象的生存期。使用new操作符创建的引用类型的实例,在执行流离开当前作用域之前都一直保存在内存中。而自动创建的基本包装类型的对象,则指存在于一行代码的执行瞬间,然后立即被销毁。这意味着我们不能在运行时为基本类型值添加属性和方法。
1 | var s1=“text”; |
第二行代码试图为字符串添加一个color属性。但是当第三行代码再次访问s1时,color属性不见了。这是因为第二行创建的String对象在执行第三行代码时已经被销毁了。