您的位置:首页 > Web前端 > JavaScript

JavaScript高级程序设计——第5章:引用类型

2016-07-06 14:37 633 查看
引用类型的值(对象)是引用类型的一个实例。

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 + "\n";
}
if (typeof args.age == "number") {
output += "Age:" + args.age + "\n";
}
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类型

[b]5.4 RegExp类型[/b]

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对象提供了很多属性和方法,用于完成复杂的数学计算任务。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: