引用类型的值(对象)是引用类型的一个实例。
5.1 Object类型
创建Object实例的方式有两种:
第一种使用new操作符后跟Object构造函数,如下所示:
var person=new Object();
person.neme="Nicholas";
person.age=27;
第二种使用对象字面量表示法(通过对象字面量定义对象时,实际不会调用Object构造函数),如下所示:
var person={
name:"Nicholas",
age:29
};
后一种要求的代码量少,而且给人封装数据的感觉。实际上,对象字面量也是向函数传递大量可选参数的首选方式,例如:
function displayInfo(args)
{
var output = "";
if (typeof args.name == "string") {
output += "Name:" + args.name + "
";
}
if (typeof args.age == "number") {
output += "Age:" + args.age + "
";
}
alert(output);
}
displayInfo({
name: "Nicholas",
age: 29
});
访问对象属性通常用点表示法,很多面向对象语言的通用语法。不过,在JavaScript也可以使用方括号表示法来访问对象属性,如下:
alert(person["name"]);
alert(person.name]);
功能上这两种语法没什么区别,但方括号语法的优点是可以通过变量来访问属性(或者属性名使用关键字、保留字,包含导致语法错误的字符):
var propertyName="name";
alet(person[propertyName])
alet(person["first name"])
通常,除非必须使用变量来访问属性,建议使用.表示法。
5.2 Array类型
var colors=new Array();//通过构造函数来创建
var colors=new Array{"blue","red","black"};
var colors=["blue","red","black"];//通过字面量表示法来创建,也不会调用构造函数
检测数组:if(Array.isArray(value)){//对数组进行某些操作}
转换方法:所有对象都具有toLocaleString(),toString()和valueOf()方法。
栈方法:栈是一种LIFO(Last-In-First-Out)的数据结构。ECMAScript为数组专门提供了push()和pop()方法,以便实现类似栈的行为。
栈数据结构的访问规则是LIFO(后进先出),而队列数据结构的访问规则是FIFO(First-In-First-Out)
队列方法:shift()从数组前端取得项的方法。unshift()从数组前端添加任意个项并返回新数组长度。
重排序方法:reverse()反转数组项的顺序; sort()与一个比较方法配合,对数组升序、降序。
操作方法:concat()复制、追加数组项;slice()传参(开始结束位置);splice()主要用途是向数组中部插入项,但使用这种方法的方式则有如下三种(删除、插入、替换);
位置方法:IndexOf()、lastIndexOf();
迭代方法:每个方法都接收两个参数(要在每一项上运行的函数和(可选的)运行该函数的作用域对象,影响this的值。函数会接收三个参数:数组项的值、该项在数组中的位置、和数组对象本身);
every()对数组中的每一项运行给定函数,如果该函数对每一项返回true,则返回true;
filter()对数组中的每一项运行给定函数,返回该函数会返回true的项组成的数组;
forEach()对数组中的每一项运行给定函数,这个方法没有返回值;
map()对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组;
some()对数组中的每一项运行给定函数,如果该函数对任一项返回true,则返回true;
var numbers=[1,2,3,4,5,4,3,2,1];
var everyResult=numbers.every(function(item,index,array){
reture (item>2);
})
alert(everyResult)//false
缩小方法:ECMAScript还新增了两个缩小数组的方法reduce()和reduceRight(),迭代数组所有项,构建一个最终返回的值。每个方法都接收两个参数(要在每一项上运行的函数和(可选的)缩小基础的初始值,函数接收四个参数:前一个值、当前值、项的索引和数组对象。这个函数返回的任何值都会作为第一个参数自动传给下一项。)
reduce()方法可以执行求数组中所有值之和的操作,比如:
var values=[1,2,3,4,5];
var sum=numbers.reduce(function(prev,cur,index,array){
reture (prev+cur);
})
alert(sum)//15
第一次执行回调函数,prev是1,cur是2。第二次prev是3(1+2的结果),cur是3(数组中的第三项)。
5.3 Data类型
5.4 RegExp类型
ECMAScript通过RegExp类型来支持正则表达式,类似Perl的语法,就可以创建一个正则表达式:
var expression=/pattern/flags;
模式(pattern)是任何简单复杂的正则表达式。
标志(flags)是用以标明正则表达式的行为,正则表达式的匹配模式支持下列3个标志:
g:表示全(global)局模式。
i:表示不区分大小(case-insensitive)写模式。
m:表示多行(multiline)模式。
一个正则表达式是模式与上述3个标志的组合体,如下面的例子所示。
以字面量形式来定义的正则表达式:
var patternl=/at/g; //匹配字符串中所有“at”的实例;
var patternl=/[bc]at/i; //匹配每一个"bat"或"cat",不区分大小写;
另一种创建正则表达式的方式是使用RegExp构造函数:
var patternl=new RegExp("at","g"); //匹配字符串中所有“at”的实例;
var patternl=new RegExp("[bc]at","i"); //匹配每一个"bat"或"cat",不区分大小写;
在RegExp构造函数,所有元字符都必须双重转义。字符在字符串通常被转义为\,但在RegExp构造函数中就会变成\\.例如:
字面量模式 | 等价的字符串 |
/[bc]at/ | "/\[bc\]at/" |
/.at/ | "\.at" |
ECMAScript5明确规定,使用正则表达式字面量必须像直接调用RegExp构造函数一样,每次都创建新的RegExp实例。
正则表达式中的元字符包括:
{ [ ( ^ $ | ? * + . ) ] }
RegExp构造函数属性:
长属性名 | 短属性名 | 说明 |
input | $_ | 最近一次要匹配的字符串 |
lastMatch | $& | 最近一次的匹配项 |
lastParen | $+ | 最近一次匹配的捕获组 |
leftContext | $' | input字符串中lastMatch之前的文本 |
multiline | $* | 布尔值,表示所有文本是不是都使用多行模式 |
rightContext | $' | input字符串中lastMatch之后的文本 |
5.5 Function类型
说起来ECMAScript中什么最有意思,我想那莫过于函数----而有意思的根源,则在于函数实际上是对象,与其它引用类型一样有属性和方法。
函数声明语法定义:
function sum(){
}
函数表达式定义:
var sum=function (){
};
两者差异不大。
使用Function构造函数定义(可以接收任意数量的参数,但最后一个参数始终被看成是函数体):
var sum=new Function('a','b','return a+b')//不推荐
function sum(num1,num2){ return num1+num2; } alert(sum(1,2));//20 var anotherSum=sum; alert(anotherSum(1,2));//20 sum=null; alert(anotherSum(1,2));//20
注意:使用不带圆括号的函数名是访问函数指针,而非调用函数。此时anotherSum和sum都指向同一个函数,即使将sum设置为null,让它与函数断绝关系,但仍然可以正常调用anotherSum。
函数声明与函数表达式
代码在开始执行之前,解析器就已经通过一个名为函数声明提升的过程,读取并将函数声明添加到执行环境中。
作为值的函数
不仅可以像传参数一样把一个函数传给另一个函数,也可以把一个函数当作另一个函数的结果返回(极为有用的一种技术)。
<!DOCTYPE html> <html> <head> <title>Function returning Function Example</title> <script type="text/javascript"> function createComparisonFunction(propertyName) { return function(object1, object2){ var value1 = object1[propertyName]; var value2 = object2[propertyName]; if (value1 < value2){ return -1; } else if (value1 > value2){ return 1; } else { return 0; } }; } var data = [{name: "Zachary", age: 28}, {name: "Nicholas", age: 29}]; data.sort(createComparisonFunction("name")); alert(data[0].name); //Nicholas data.sort(createComparisonFunction("age")); alert(data[0].name); //Zachary </script> </head> <body> </body> </html>
函数的内部属性
函数内部有两个特殊对象:arguments和this。
arguments有一个callee属性,该属性是个指针,指向拥有这个arguments对象的函数。请看下面这个非常经典的阶乘函数。
<!DOCTYPE html> <html> <head> <title>Function Type Arguments Example</title> //定义阶乘函数一般都会用到递归算法。直接用函数名这样函数的执行和函数名就紧紧耦合在一起。为了消除这样紧密耦合的现象,可以像下面这样使用arguments.callee <script type="text/javascript"> function factorial(num){ if (num <= 1) { return 1; } else { return num * arguments.callee(num-1) } } var trueFactorial = factorial; factorial = function(){ return 0; }; alert(trueFactorial(5)); //120 alert(factorial(5)); //0 </script> </head> <body> </body> </html>
this引用的是函数具体执行的的环境对象,或者也可以说是this值(当在网页的全局作用域调用函数时,this对象引用的就是window)
<!DOCTYPE html> <html> <head> <title>Function Type this Example</title> <script type="text/javascript"> window.color = "red"; var o = { color: "blue" }; function sayColor(){ alert(this.color); } sayColor(); //red o.sayColor = sayColor; o.sayColor(); //blue </script> </head> <body> </body> </html>
另一个函数对象的属性:caller,这个属性保存着调用当前函数的函数的引用。如果在全局作用域中调用当前函数,它的值为null。
<!DOCTYPE html> <html> <head> <title>Function Type Arguments Caller Example</title> <script type="text/javascript"> function outer(){ inner(); } function inner(){ alert(inner.caller);//警告框中显示outer函数的源码,因为outer调用了inner,所以inner.caller就指向了outer。还可以是:alert(arguments.callee.caller);实现更松散的耦合 } outer(); </script> </head> <body> </body> </html>
当函数在严格模式下运行时,访问arguments.callee会导致错误,ECMAScript5 还正义了arguments.caller属性,但在严格模式下访问它也会导致错误,而非严格模式下会返回undefined。定义这个属性是为了分清arguments.caller和函数caller属性。
以上这些变化都是为了加强这门语言的安全性,这样第三方代码就不能在相同的环境里窥视其他代码了。
严格模式还有个限制:不能为函数caller属性赋值,否则会导致错误。
5.5.5 函数属性和方法
在ECMAScript核心所定义的全部属性中最耐人寻味的就要数prototype属性了,对于ECMAScript中的引用类型而言,prototype是保存它们所有实例方法的真正所在。换句话说,诸如toString()或valueOf()等方法实际上都保存在prototype名下,只不
过通过各自对象的的实例访问罢了。在创建自定义引用类型或者实现继承,prototype属性的作用是极为重要的。在ECMAScript5中prototype是不可枚举的,因此使用for-in无法发现。
每个函数都有非继承而来的方法:apply()和call().这两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内this对象的值。(在严格模式下,未指定环境对象而调用函数,则this值不会转型为window。除非明确把函数添加到某个对象或调用apply()、call(),否则this值将为undefined)
它们真正强大的地方是能够扩充函数赖以运行的作用域,这样作的最大好处就是对象不需要与方法有任何耦合关系。
<!DOCTYPE html> <html> <head> <title>Function Type call() Example</title> <script type="text/javascript"> window.color = "red"; var o = { color: "blue" }; function sayColor(){ alert(this.color); } sayColor(); //red sayColor.call(this); //red 显示的在全局作用域中调用函数 sayColor.call(window); //red sayColor.call(o); //blue 函数执行环境改变,函数体内的this对象指向了o </script> </head> <body> </body> </html>
ECMAScript还定义了一个方法:bind()。这个方法会创建函数的实例,其this值会被绑定到传给bind()函数的值。例如:
<!DOCTYPE html> <html> <head> <title>Function Type bind() Method Example</title> <script type="text/javascript"> window.color = "red"; var o = { color: "blue" }; function sayColor(){ alert(this.color); } var objectSayColor = sayColor.bind(o); objectSayColor(); //blue 即使在全局作用域调用这个函数,也会看到“blue”,这种技巧的优点可参考22章 </script> </head> <body> <p>This example is known to work in Internet Explorer 9, Firefox 4, and Chrome 6.</p> </body> </html>
5.6 基本包装类型
为了便于操作基本类型值,ECMAScript还提供了3个特殊的引用类型:String、Number、Boolean(建议永远不要使用)。每当读取一个基本类型,后台就会创建创建一个基本包装类型的对象,从而让我们能够调用一些方法来操作这些数据。对基本包装类型的实例调用typeof会返回"object",而且所有基本包装类型的对象都会被转换为布尔值true(布尔表达式中所有对象都会被转换为true)。Object构造函数也会像工厂方法一样,根据传入值的类型返回相应基本包装类型的实例。
5.7单体内置对象
ECMA-262对内置对象的定义:由ECMAScript实现提供的,不依赖于宿主环境的对象,这些对象在ECMAScript程序执行之前就已经存在了。开发人员不必显示的实例化内置对象,因为他们已经实例化了。Object、Array、String都是内置对象。ECMAScript-262还定义了两个单体内置对象:Global、Math。
5.7.1 Global对象
事实上没有全局变量、全局函数,所有在全局作用域中定义的属性和函数,都是Global对象的属性。
1.URI编码
一般来说,我们使用encodeURIComponent()方法的时候比encodeURI()更多,因为在实践中更常见的是对查询字符串参数而不是对基础URI进行编码。相对应的两个方法是decodeURIComponent()、decodeURI()。URI方法能够编码所有Unicode字符。
2.eval()方法
它像一个完整的ECMAScript解析器。在eval()定义的任何变量和函数都不会被提升,因为在解析代码的时候,它们被包含在字符串中,它们只在eval()执行时创建。
3.Global对象的属性
特殊的值:undefined、NaN以及Infinity都是Glbal对象的属性,此外,还有原生引用类型的构造函数:Object、Function等。
4.window对象
ECMAScript虽然没有指出如何直接访问Global对象,但是Web浏览器都是将这个全局对象作为window对象的一部分加以实现的。因此,在全局作用域中声明的所有变量和函数,就都成为了window对象的属性。
另一种取得Global对象的方法是使用以下代码:
var global=function(){
return this;
}();
5.7.2 Math对象
5.8 小结
对象在JavaScript中被称为引用类型的值,而且有一些内置的引用类型可以用来创建特定的对象:
现简要总结如下:
*引用类型与传统面向对象程序设计中的类相似,但实现不同;
*object是个基础类型,其他所有类型都从object继承了基本的行为;
*Array是一组值的有序列表,同时还提供了操作和转换这些值的功能。
*Data类型提供了有关日期和时间的信息,包括当前日期和时间以及相关的计算功能。
*RegExp类型是ECMAScript支持正则表达式的一个接口,提供了基本的和一些高级的正则表达式功能。
函数实例实际上是Function类型的实例,因此函数也是对象;而这一点是Javascript最有特色的的地方。由于函数是对象,所以函数也拥有方法,可以用来增强其行为。
因为有了基本包装类型,所以JavaScript中的基本类型可以被当作对象来访问。三种基本包装类型是:String、Boolean、Number,下面是它们共同特性:
*每个包装类型都映射到同名的基本类型。
*在读取模式下访问基本类型值时,就会创建对应的基本包装类型的一个对象,从而方便了数据的操作。
*操作基本类型值的语句一经执行完毕,就会立即销毁新创建的包装对象。
在所有代码执行之前,作用域中就已经存在两个内置对象:Global和Math。在大多数ECMAScript实现中都不能直接访问Global对象;不过,Web浏览器实现了承担该角色的Window对象。全局变量和
函数都是Global对象的属性。Math对象提供了很多属性和方法,用于完成复杂的数学计算任务。