JS高级程序设计笔记(四)引用类型

引用类型的值是引用类型的一个实例。引用类型是一种数据结构,用于将数据和功能主治在一起,他也常被称呼为类,但这种称呼不妥当。引用类型有时候也被称为对象定义,因为他们描述的是一类对象所句有的属性和方法。

如前所述,对象是某个特定的引用类型的实例。新对象是使用new操作符后跟一个构造函数来创造的。构造函数本身就是一个函数,只不过该函数是出于创建新对象的目的而定义的。

var person()= new Object();

Object类型

在应用程序中存储和传输数据而言Object()是非常理想的选择。

创建Object的方法有两种。第一种是使用new操作符后跟Object构造函数:

var person = new Object();
person.name = "Nicholas";
person.age = 29;

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

var person = {
	name:"Nicholas",
	age:29
};

Array类型

检测数组

if(value instanceof Array){
	//对数组执行某些操作
}

转换方法

var colors = ["red","blue","green"];//创建一个包含3个字符串的数组
alert(colors.toString());//red,blue,green
alert(colors.valueOf()); //red,blue,green
alert(colors);           //red,blue,green

toString()方法把数组的值拼接成一个字符串,中间用逗号隔开。valueOf()方法直接将数组传给alert().由于alert()要接收字符串参数,所以它会在后台调用toString()方法,由此会得到用toString()方法相同的结果。

toLocaleString()方法经常也会返回与toString()和valueOf()方法相同的值,但也不总是如此。当调用数组的toLocaleString()方法时,它会创建一个数组值以逗号分隔的字符串。而与前两个方法唯一的不同之处在于,这一次为了取得每一项的值,调用的是每一项的toLocaleString()方法,而不是toString()方法。

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

alert(colors.join(",")); //red,blue,green
alert(colors.join("||"));//red||blue||green

如果不传入任何值或者传入undefined,则使用逗号作为分隔符。

栈方法

ECMAScript数组也提供了一种让数组的行为类似于其他数据结构的方法。具体来说,数组可以表现得就像栈一样,后者是一种可以限制插入和删除项的数据结构。栈是一种LIOF(Last-In-First-Out,后进先出)的数据结构,也就是最新添加的项最早被移出。而栈中项的插入和移出,只发生在一个位置----栈的顶部。ECMAScript为数组提供了push()和pop()方法,以便实现类似栈的行为。

队列方法

队列数据结构的访问方法是先进先出。shift()从数组顶端获取项的方法。数组还提供了一个unshift()方法。在数组前端添加任意个项。

重新排序方法

reverse()和sort()方法。reverse()方法会反转数组项的顺序。

var values = [1,2,3,4,5];
values.reverse();
alert(values);//5,4,3,2,1

sort()方法会调用每个数组项的toString()转型方法,然后比较得到的字符串,以确定如何排序。即使每一项都是数值,sort方法比较的也是字符串。

var values = [0,1,5,10,15];
values.sort();
alert(values);//0,1,10,15,5

这种发虚方式在很多情况下不是最佳方案。因此sort()方法可以接收一个比较函数作为参数,以便我们制定那个位置于哪个值得前面。

function compare(value1 , value2)
{
	if(value1 < value2)
	{
		return -1;
	}else if(value1 > value2)
	{
		return 1;
	}
	else {
		return 0;
	}
}

这个比较函数适用于大多数数据类型,只要将其作为参数传递给sort()方法即可。

var values = [0,1,5,10,15];
values.sort(compare);
alert(values);//0,1,5,10,15

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

function compare(value1,value2)
{
	return value2-value1;
}

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

操作方法

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

var colors = ["red","green","blue"];
var colors = colors.concat("yellow",["black","brown"]);

alert(colors);//red,green,blue
alert(colors2);//red,green,blue,yellow,black,brown

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

var colors = ["red","green","blue","yellow","purple"];
var colors2 = colors.slice(1);
var colors3 = colors.slice(1,4);

alert(colors2);  //green,blue,yellow,purple
alert(colors3);  //green,blue,yellow

splice()方法

删除:可以删除任意数量的项,只需指定两个参数:要删除的第一项的位置和要删除的项数。例如,splice(0,2)会删除数组中的前两项。

插入:可以向指定位置插入任意数量的项,只需提供3个参数:起始位置,0(要删除的项数)和要插入的项。如果要插入多个项,可以再传入第四、第五、以至任意多个项。例如,splice(2.0,"red","green")会删除当前数组位置2的项,然后再从位置2开始插入字符串“red"和"green".

替换:可以向指定位置插入任意数量的项,且同时删除任意数量的项,只需指定3个参数:起始位置、要删除的项数和要插入的任意数量的项。插入的项数不必与删除的项数相等。例如,splice(2,1,"red","green")会删除当前数组位置2的项,然后再从位置2开始插入字符串“red"和"green".

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

var colors = ["red","green","blue"];
var removed = colors3.splice(0,1); //删除第一项
alert(colors);    //green,blue
alert(removed);   //red,返回数组中只包含一项

removed = colors.splice(1,0,"yellow","orange"); //从位置1开始插入两项
alert(colors);//green,red,purple,orange,blue
alert(removed);//返回的是一个空数组

removed = colors.splice(1,1,"red","purple");
alert(colors);//green,red,purple,orange,blue
alert(removed);//yellow,返回数组中只包含一项

位置方法

indexOf()和lastIndexOf().这两个参数:要查找的项和(可选的)表示查找起点位置的索引。其中,indexOf()方法从数组的开头开始向后查找,lastIndexOf()方法则从数组的末尾开始向前查找。在没找到的情况下返回-1.在比较第一个参数与数组中的每一项时,都会使用全等操作符,

var numbers = [1,2,3,4,5,4.3.2.1];
alert(numbers.indexOf(4));//3
alert(numbers.lastIndexOf(4)); //5

alert(numbers.indexOf(4,4)); //5
alert(numbers.lastIndexOf(4,4));//3

var person = {name:"Nicholas"};
var people = [{name:"Nicholas"}];

var morePeople = [person];

alert(people.indexOf(person)); //-1
alert(morePeople.indexOf(person));//0

迭代方法

ECMAScript5为数组定义了5个迭代方法。每个方法都接收两个参数:要在每一项上运行的函数和(可选的)运行该函数的作用域对象——影响this的值。传入这些方法中的函数会接收三个参数:数组项的值、该项在数组中的位置和数组对象本身。根据使用方法不同,这个函数执行后的返回值可能会也可能不会影响方法的返回值:

every():对数组中的每一项运行给定函数,如果该函数对每一项都返回true,则返回true。

filter():对数组中的每一项运行给定函数,返回该函数会返回true的项组成的数组。

forEach():对数组的每一项运行给定函数,这个方法没有返回值。

map():对数组中每一项运行给定函数,返回每次函数调用的结果组成的数组。

some():对数组中的每一项运行给定函数,如果该函数对任一项返回true,则返回true。

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

eyery()和some()都用于查询数组中的项是否满足某个条件。对every()来说,传入的函数必须对每一项都返回true,这个方法才返回true;否则,它就返回false。而some()方法则是只要传入的函数对数组中的某一项返回true,就返回true。

var numbers = [1,2,3,4,5,4,3,2,1];
var eyeryResult = numbers.every(function(item,index,array){
	return (item>2);
});
alert(everyResult);//false
var someResult = numbers.some(function(item,index,array){
	return (item>2);
});
alert(someResult);//true

filter()函数,它利用指定的函数确定是否返回的数组中包含某一项。(返回一个所有数值都大于2的数组)

var numbers = [1,2,3,4,5,4,3,2,1];
var filterResult = numbers.filter(function(item,index,array){
	return (item>2);
});
alert(filterResult); //[3,4,5,4,3]

map()也返回一个数组,而这个数组的每一项都是在原始数组中的对应项上运行函数的结果。例如,可以给数组中的每一项乘以3,然后返回这些乘积组成的数组。

var numbers = [1,2,3,4,5,4,3,2,1];
var mapResult = numbers.map(function(item,index,array){
	return item*2;
});
alert(mapResult);//[2,4,6,8,10,8,6,4,2]

forEach()只是对数组中的每一项运行传入的函数。这个方法没有返回值,本质上与使用for循环迭代数组一样。

numbers.forEach(function(item,index,array){
	//执行某些操作
});

归并方法

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

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

使用reduce()方法可以执行求数组中所有值之和的操作。

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

第一次执行回调函数,prev是1,cur是2.第二次,prev是3(1加2的结果),cur是3(数组的第三项)。这个过程会把数组中的每一项都访问一遍,最后返回结果。

reduceRight()的作用类似,只不过方向相反而已。

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

第一次执行回调函数,prev是5,cur是4.当然,最终结果相同,因为执行的都是简单相加的操作。

使用reduce()还是redudeRight(),主要取决于要从哪头开始遍历数组。除此之外,它们完全相同。

Date类型

创建一个日期对象:

var now = new Data();

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

其中,Data.parse()方法接收一个表示日期的字符串参数,然后尝试根据这个字符串返回相应日期的毫秒数。

var someData = new Data(Data.parse("May 25,2004"));

如果传入Data.parse()方法的字符串不能表示日期,那么它就会返回NaN。实际上,如果直接将表示日期的字符串传递给Data构造函数,也会在后台调用Data.parse().

var someData = new Data("May 25,2004");

Date.UTC()方法同样也返回表示日期的毫秒数,但它与 Date.parse()在构建值时使用不同的信息。Date.UTC()的参数分别是年份、基于 0 的月份(一月是 0,二月是 1,以此类推)、月中的哪一天(1 到 31)、小时数(0 到 23)、分钟、秒以及毫秒数。在这些参数中,只有前两个参数(年和月)是必需的。如果没有提供月中的天数,则假设天数为 1;如果省略其他参数,则统统假设为 0。

// GMT 时间 2000 年 1 月 1 日午夜零时
var y2k = new Date(Date.UTC(2000, 0)); 
// GMT 时间 2005 年 5 月 5 日下午 5:55:55 
var allFives = new Date(Date.UTC(2005, 4, 5, 17, 55, 55)); 

Data.now()方法,返回表示调用这个方法时的日期和时间的毫秒数。这个方法简化了使用Data对象分析代码的工作。

//取得开始时间
var start = Data.now();
//调用函数
doSomething();
//取得停止时间
var stop = Data.now();
result = stop - start;

日期格式化方法

  • toDataString()——以特定的格式显示星期几、月、日和年;
  • toTimeString()——以特定的格式显示时、分、秒和时区;
  • toLocaleDataString()——以特定与地区的格式显示星期几、月、日和年;
  • toLocaleTimeString()——以特定于现实的格式显示时、分、秒;
  • toUTCString()——以特定与现实的格式完整的UTC日期。

日期时间组件方法

剩下还未介绍的方法,都是直接取得和设置日期值中特定部分的方法了。

RegExp类型

ECMAScript通过RegExp类型来支持正则表达式。使用下面类似Perl的语法就可以创建一个正则表达式。

var expression = /pattern/flags;

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

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

因此,一个正则表达式就是一个模式与上述3个标志的组合体。不同组合产生不同结果。

//匹配字符串中所有"at"的实例
var pattern1 = /at/g;
//匹配第一个'bat'或"cat",不区分大小写
var pattern2 = /[bc]at/i;
//匹配所有以at结尾的3个字符的组合,不区分大小写
var pattern3 = /.at/gi;

与其他语言中的正则表达式类似,模式中使用的所有元字符都必须转义。正则表达式中的元字符包括:([{\^$|)?*+.]}

/* 
* 匹配第一个"bat"或"cat",不区分大小写
*/ 
var pattern1 = /[bc]at/i; 
/* 
* 匹配第一个" [bc]at",不区分大小写
*/ 
var pattern2 = /\[bc\]at/i; 
/* 
* 匹配所有以"at"结尾的 3 个字符的组合,不区分大小写
*/ 
var pattern3 = /.at/gi; 
/* 
* 匹配所有".at",不区分大小写
*/ 
var pattern4 = /\.at/gi; 
/* 
* 匹配第一个"bat"或"cat",不区分大小写
*/ 
var pattern1 = /[bc]at/i; 
/* 
* 与 pattern1 相同,只不过是使用构造函数创建的
*/ 
var pattern2 = new RegExp("[bc]at", "i"); 

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

var re = null, 
 i; 
for (i=0; i < 10; i++){ 
 re = /cat/g; 
 re.test("catastrophe"); 
} 
for (i=0; i < 10; i++){ 
 re = new RegExp("cat", "g"); 
 re.test("catastrophe"); 
} 

在第一个循环中,即使是循环体中指定的,但实际上只为/cat/创建了一个 RegExp 实例。由于实例属性(下一节介绍实例属性)不会重置,所以在循环中再次调用 test()方法会失败。这是因为第一次调用 test()找到了"cat",但第二次调用是从索引为 3 的字符(上一次匹配的末尾)开始的,所以就找不到它了。由于会测试到字符串末尾,所以下一次再调用 test()就又从开头开始了。

第二个循环使用 RegExp 构造函数在每次循环中创建正则表达式。因为每次迭代都会创建一个新的RegExp 实例,所以每次调用 test()都会返回 true。

RegExp实例属性

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

RegExp实例方法

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

var matches = pattern.exec(text);
alert(matches.index);    //0
alert(matches.input);    //"mom and dad and baby"
alert(matcher[0]);       //"mom and dad and baby"
alert(matches[1]);       //" and dad and baby"
alert(matches[2]);       //" and baby"

这个例子中包含两个捕获组。最内部的捕获组匹配“and baby”,而包含他的捕获组匹配“and dad”或者“and dad and baby”。当把字符串传入exec()方法中之后,发现了一个匹配项。因为整个字符串本身与模式匹配,所以返回的数组matchs的index属性值为0。数组中的第一项是匹配的整个字符串,第二项包含与第一个捕获组匹配的内容,第三项包含与第二个捕获组匹配的内容。

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

var text = "cat,bat,sat,fat";
var pattern1 = /.at/;

var matches = pattern1.exec(text);
alert(matches.index);      //0
alert(matches[0]);         //cat
alert(pattern1.lastIndex); //0

matches = pattern1.exec(text);
alert(matches.index);      //0
alert(matches[0]);         //cat
alert(pattern1.lastIndex); //0

var pattern2 = /.at/g;
var matches = pattern2.exec(text);
alert(matches.index);      //0
alert(matches[0]);         //cat
alert(pattern2.lastIndex); //3

matches = pattern2.exec(text);
alert(matches.index);      //5
alert(matches[0]);         //bat
alert(pattern2.lastIndex); //8

test()方法,接受一个字符串参数。在模式与该参数匹配的情况下返回true;否则,返回false。在只想知道目标字符串与某个模式是否匹配,但不需要知道其文本内容的情况下,使用这个方法非常方便。因此,test()方法经常被用在if语句中。

var text = "000-00-0000";
var pattern = /\d{3}-\d{2}-\d{4}/;
if(pattern.text(text))
{
	alert("The pattern was matched");
}

RegExp构造函数属性

RegExp构造函数包含一些属性(这些属性在其他语言中被看成静态属性)。这些属性适用于作用域中的所有正则表达式,并且基于所执行的最近一次正则表达式操作而变化。关于这些属性的另一个独特之处,就是可以通过两种不同的方式访问他们。

var text = "this has been a short summer";
var pattern = /(.)hort/g;
/*
注意:Opera不支持input、lastMatch、lastParen和multiline属性
Interenet Explorer不支持multiline属性
 */
if (pattern.test(text)) {
	alert(RegExp.input);        //this has been a short summer
	alert(RegExp.leftContext);  //this has been a
	alert(RegExp.rightContext); //summer
	alert(RegExp.lastMatch);    //short
	alert(RegExp.lastParen);    //s
	alert(RegExp.multiline);    //false
}

以上代码创建了一个模式,匹配任何一个字符后跟hort,而且把第一个字符放在了一个捕获组中。RegExp构造函数的各个属性返回了下列值:

  • input属性返回了原始字符串;
  • leftContext属性返回了单词short之前的字符串,而rightContext属性则返回了short之后的字符串;
  • lastMatch属性返回最近一次与整个正则表达式匹配的字符串,即short;
  • lastParen属性返回最近一次匹配的捕获组,即例子中的s。
if(pattern.test(text)){
	alert(RegExp.$_);        //this has been a short summer
	alert(RegExp["$`"]);     //this has been a
	alert(RegExp["$'"]);     //summer
	alert(RegExp["$&"]);     //short
	alert(RegExp["$+"]);     //s
	alert(RegExp["$*"]);     //false
}

Function类型

函数实际上是对象。每个函数都是Function类型的实例,而且都与其他引用类型一样具有属性和方法。由于函数就是对象。每个函数都是Function类型的实例,而且都与其他引用类型一样具有属性和方法。由于函数是对象,因此函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定。函数通常是使用函数声明语法定义的。


function sum(num1,num2) {
	return num1+num2;
	// body...
}

使用函数表达式定义函数的方法

var sum = function(num1,num2){
	return num1+num2;
};

以上代码定义了变量sum并将其初始化为一个函数,function关键字后面没有函数名。在使用函数表达式定义函数时,没有必要使用函数名——通过变脸sum即可引用函数。

使用Function构造函数。Function构造函数可以接收任意数量的参数,但最后一个参数始终都被看成函数体,而前面的参数则枚举出了新函数的参数。

var sum = new Function("num1","num2","retrun num1+num2");//不推荐

这种语法会导致解析两次代码,从而影响性能。不过这种语法对于理解“函数是对象,函数名是指针”的概念非常直观的。

由于函数名仅仅是指向函数的指针,因此函数名与包含对象指针的其他变量没有什么不同。换句话说一个函数可以有多个名字。

function sum(num1, num2){ 
 return num1 + num2; 
} 
alert(sum(10,10)); //20 
var anotherSum = sum; 
alert(anotherSum(10,10)); //20 
sum = null; 
alert(anotherSum(10,10)); //20 

使用不带圆括号的函数名是访问函数指针,而非调用函数。

没有重载

将函数名想象为指针,也有利于理解为什么ECMAScript中没有函数重载的概念。

var addSomeNumber = function(num){
	return num+100;
};

addSomeNumber = function(num){
	return num+200
};

建第二个函数时覆盖了第一个函数。

函数声明与函数表达式

解析器在加载数据时,会先读取函数声明,并使用其在执行任何代码之前可用;以至于函数表达式,则必须等到解析器执行到它所在的代码行,才会真正被执行。

作为值的函数

因为ECMAScript中的函数名本身就是变量,所以函数也可以作为值来使用。也就是说,不仅可以像传递参数一样把一个函数传递给另一个函数,而且可以将一个函数作为另一个函数的结果返回。

function add10(num){ 
 return num + 10; 
} 
var result1 = callSomeFunction(add10, 10); 
alert(result1); //20 
function getGreeting(name){ 
 return "Hello, " + name; 
} 
var result2 = callSomeFunction(getGreeting, "Nicholas"); 
alert(result2); //"Hello, Nicholas" 

可以从一个函数中返回另一个函数,而且这也是一种极为有用的技术。例如,假设有一个对象数组,我们想要根据某个对象属性对数组进行排序。而传递给数组sort()方法的比较函数要接收两个参数,即要比较值。可是,我们需要一种方式来指明按照那个属性来排序。要解决这个问题,可以定义一个函数,它接收一个属性名,然后根据这个属性名来创建一个比较函数,下面就是这个函数的定义。

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;
		}
	}
}

函数内部属性

在函数内部,有两个特殊的对象:arguments和this。其中arguments是一类数组对象,包含着传入函数中的所有参数。虽然arguments的主要用途是保存函数参数,但这个对象还有一个名叫callee的属性,该属性是一个指针,指向拥有这个arguments对象的函数。看下面的阶乘函数

function factorial(num)
{
	if(num<=1){
		return 1;
	}else
	{
		return num*factorial(num-1);
	}
}

定义阶乘函数一般都要用到递归算法:如上面的代码所示,在函数有名字,而且名字以后也不会变得情况下,这样定义没有问题。但问题是这个函数的执行与函数名factorial紧紧耦合在了一起。为了消除这种紧密耦合的现象,可以像下面这样使用arguments.callee.


function factorial(num)
{
	if(num<=1){
		return 1;
	}else
	{
		return num*arguments.callee(num-1);
	}
}

在这个重写后的factorial()函数的函数体内,没有再引用函数名factorial。这样,无论引用函数时使用的是什么名字,都可以保证正常完成递归调用。

var trueFactorial = factorial;
factorial = function(){
	return 0;
};
alert(trueFactorial(5)); //120
alert(factorial(5));     //0

在此,变量trueFactorial获得了factorial的值,实际上是在另一个位置上保存了一个函数的指针。然后,我们又将一个简单地返回0的函数赋值给factorial变量。如果像原来的factorial()那样不使用arguments.callee,调用trueFactorial()就会返回0.可是,在解除了函数体内的代码与函数名的耦合状态之后,trueFactorial()仍然能够正常地计算阶乘;至于factorial(),它现在只是一个返回0的函数。

函数内部的另一个特殊对象时this,其行为与java和c#中的this大致类似。换句话说,this引用的是函数执行的环境对象----或者也可以说是this值。

window.color = "red"; 
var o = { color: "blue" }; 
function sayColor(){ 
 alert(this.color); 
} 
sayColor(); //"red" 
o.sayColor = sayColor; 
o.sayColor(); //"blue" 

上面这个函数sayColor()是在全局作用域中定义的,它引用了this对象。由于在调用函数之前,this的值并不确定,因此this可能会在代码执行过程中引用不同的对象。当在全局作用域中调用sayColor()时,this引用的是全局对象window;换句话说,对this.color求值会转换成对window.color求值,于是结果就返回了"red"。而当把这个函数赋给对象o并调用o.sayColor()时,this引用的是对象o,因此对this.color求值会转换成o.color求值,结果就返回了"blue".

caller:这个属性保存着调用当前函数的函数的引用,如果是在全局作用域中调用当前函数,他的值为null。

function outer(){
	inner();
}
function inner(){
	alert(inner.caller);
}
outer()

以上代码会导致警告框中显示outer()函数的源代码。因为outer()调用了inter(),所以inner.caller就指向outer().为了实现更松散的耦合,也可以通过arguments.callee.caller来访问相同的信息。

function outer(){
	inner();
}
function inner(){
	alert(arguments.callee.caller);
}
outer()

函数属性和方法

函数是对象,所以函数也有属性和方法。每个函数都包含两个属性:length和prototype。

其中length属性表示函数希望接收的命名参数的个数。

function sayName(name){
	alert(name);
}
function sum(num1,num2){
	return num1+num2;
}
function sayHi(){
	alert("hi");
}
alert(sayName.length); //1
alert(sum.length);     //2
alert(sayHi.length);   //0

prototype保存所有实例方法的真正所在。换句话说诸如toString()和valueOf()等方法实际上都保存在prototype名下,只不过是通过各自对象的实例访问罢了。在创建自定义引用类型以及实现继承时,protptype属性的作用极为重要的。

每个函数否包含了两个非继承而来的方法:apply()和call().这两个方法的用途是在特定的作用域中调用函数,实际上等于设置函数体内this对象的值。首先,apply()方法接收两个参数:一个是在其中运行函数的作用域,另一个是参数数组。其中,第二个参数可以是Array的实例,也可以是arguments对象。

function sum(num1,num2){
	return num1+num2;
}
function callSum1(num1,num2){
	return sum.apply(this,arguments);   //传入arguments对象
}
function callSum2(num1,num2){
	return sum.apply(this,[num1,num2]);  //传入数组
}
alert(callSum1(10,10));  //20
alert(callSum2(10,10));  //20

call()方法与apply()方法的作用相同,他们的区别仅在于接收参数的方式不同。对于call()方法而言,第一个参数是this值没有变化,变化的是其余参数都有直接传递给函数。换句话说,在使用call()方法时,传递给函数的参数必须逐个力矩出来。

function sum(num1,num2){
	return num1+num2;
}
function callSum(num1,num2){
	return sum.call(this,num1,num2);   
}
alert(callSum(10,10));  //20

传递参数并非apply()和call()真正的用武之地;它怎正强大的地方是能够扩充函数赖以运行的作用域。

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

使用call()或apply()来扩充作用域的最大好处,就是对象不需要与方法有任何耦合关系。

bind()创建一个函数的实例,其this值会被绑定到传给bind()函数的值。

window.color = "red";
var o = {color:"blue"};
function sayColor(){
	alert(this.color);
}
var objectSayColor = sayColor.bind(o);
objectSayColor();       //blue

sayColor()调用bind()并传入对象o,创建了objectSayColor()函数。objectSayColor()函数的this值等于o,因此即使是在全局作用域调用这个函数,也会看到'blue"。

基本包装类型

每当读取一个基本类型的值的时候,后台就会创建一个对应的基本包类型的对象,从而让我们能够调用一些方法来操作这些数据。

var s1 = "some text";
var s2 = s1.substring(2);

基本类型不是对象,从逻辑上讲它们不应该有方法。其实,为了让我们实现这种直观的操作,后台已经自动完成了一系列的处理。当第二行代码访问s1时,访问过程处于一种读取模式,也就是要从内存中读取这个字符串的值。而在读取模式中访问字符串时,后台都会自动完成下列处理。

  1. 创建string类型的一个实例
  2. 在实力上调用指定的方法
  3. 销毁这个实例
var s1 = new String("some text");
var s2 = s1.substring(2);
s1 = null;

经过此番处理,基本的字符串值就变的跟对象一样了。而且,上面这三个步骤也分别适用于Boolean和Number类型对应的布尔值和数字值。

引用类型与基本包装类型的主要区别就是对象的生命周期。使用new操作符创建的引用类型的实例,在执行流离开当前作用域之前都一直保存在内存中。而自动创建的基本包装类型的对象,则只存在于一行代码的执行瞬间,然后立即被销毁。这意味着我们不能在运行时为基本类型值添加属性和方法。

 var s1 = "some text";
 s1.color = "red";
 alert(s1.color); //undefined

对于基本包装类型的实例调用typeof会返回"object',而且所有基本包装类型的对象都会被转换为布尔值true。

Object构造函数也会像工厂方法一样,根据传入值的类型返回响应的包装类型的实例。

 var obj = new Object("some text");
 alert(obj instanceof String);      //true

把字符串传给Object构造函数,就会创建String的实例;而传入数值参数会得到Number的实例,传入布尔值参数就会得到Boolean的实例。

要注意的是,使用new调用基本包装类型的构造函数,与直接调用同名的转型函数是不一样的。

var value = "25"; 
var number = Number(value); //转型函数
alert(typeof number); //"number" 
var obj = new Number(value); //构造函数
alert(typeof obj); //"object" 

Boolean类型

Boolean类型是与布尔值对应的引用类型。要创建Boolean对象,可以像下面这样调用Boolean构造函数并传入true或false值。

var booleanObject = new Boolean(true);

Boolean类型的实例重写了valueOf()方法,返回基本类型值true或false;重写了toString()方法,返回字符串“true”和“false”。可是Boolean对象在ECMAScript中用处不大,因为它经常造成人们的误解。其中最常见的问题就是在布尔表达式中使用Boolean对象,

 var falseObject = new Boolean(false);
 var result = falseObject && true;
 alert(result);   //true

 var falseValue = false;
 result = falseValue && true;
 alert(result);   //false

我们使用false创建了一个Boolean对象。然后将这个对象与基本类型值true构成了逻辑与表达式。在布尔运算中,false&&true等于false。可是,示例中的这行代码是对falseObject而不是对它的值(false)进行求值。布尔表达式中的所有对象都会被转换为true,因此falseObject对象在布尔表达式中代表的是true。结果true&&true当然是true。

基本类型与引用类型的布尔值还有两个区别。首先typeof操作对基本类型返回"boolean",而对引用类型返回"object".其次,由于Boolean对象时Boolean类型的实例,所以使用instanceof操作符测试Boolean对象会返回true,而测试基本类型的布尔值则返回false。

 alert(typeof falseObject); //object
 alert(typeof falseValue);  //boolean
 alert(falseObject instanceof Boolean); //true
 alert(falseValue instanceof Boolean); //false

Number类型

Number 是与数字值对应的引用类型。要创建 Number 对象,可以在调用 Number 构造函数时向其中传递相应的数值。

var numberObject = new Number(10); 

可以为 toString()方法传递一个表示基数的参数,告诉它返回几进制数值的字符串形式

var num = 10; 
alert(num.toString()); //"10" 
alert(num.toString(2)); //"1010" 
alert(num.toString(8)); //"12" 
alert(num.toString(10)); //"10" 
alert(num.toString(16)); //"a" 

Number 类型还提供了一些用于将数值格式化为字符串的方法。其中,toFixed()方法会按照指定的小数位返回数值的字符串表示

var num = 10; 
alert(num.toFixed(2)); //"10.00" 

可用于格式化数值的方法是 toExponential(),该方法返回以指数表示法(也称 e 表示法)表示的数值的字符串形式。与 toFixed()一样,toExponential()也接收一个参数,而且该参数同样也是指定输出结果中的小数位数。

var num = 10; 
alert(num.toExponential(1)); //"1.0e+1" 

如果你想得到表示某个数值的最合适的格式,就应该使用 toPrecision()方法。对于一个数值来说,toPrecision()方法可能会返回固定大小(fixed)格式,也可能返回指数(exponential)格式;具体规则是看哪种格式最合适。这个方法接收一个参数,即表示数值的所有数字的位数(不包括指数部分)。

var num = 99; 
alert(num.toPrecision(1)); //"1e+2" 
alert(num.toPrecision(2)); //"99" 
alert(num.toPrecision(3)); //"99.0"

String类型

String类型是字符串的对象包装类型,可以像下面这样使用String构造函数来创建。

 var stringObject = new String("hello world");

String 类型提供了很多方法,用于辅助完成对 ECMAScript 中字符串的解析和操作。

1.字符方法

两个用于访问字符串中特定字符的方法是:charAt()和charCodeAt().这两个方法都接收一个参数,即基于0的字符位置。其中,charAt()方法以单字符串的形式返回给定位置的那个字符。

var stringValue = "hello world";
alert(stringValue.charAt(1));   //"e"

字符串"hello world"位置 1 处的字符是"e",因此调用 charAt(1)就返回了"e"。

charCodeAt()得到字符编码

var stringValue = "hello world"; 
alert(stringValue.charCodeAt(1)); //输出"101" 

这个例子输出的是"101",也就是小写字母"e"的字符编码。

ECMAScript 5 还定义了另一个访问个别字符的方法。在支持此方法的浏览器中,可以使用方括号加数字索引来访问字符串中的特定字符。

var stringValue = "hello world"; 
alert(stringValue[1]); //"e" 

2.字符串操作方法

concat()用于将一或多个字符串拼接起来,返回拼接得到的新字符串。

var stringValue = "hello "; 
var result = stringValue.concat("world"); 
alert(result); //"hello world" 
alert(stringValue); //"hello" 

实际上,concat()方法可以接受任意多个参数,也就是说可以通过它拼接任意多个字符串。

var stringValue = "hello "; 
var result = stringValue.concat("world", "!"); 
alert(result); //"hello world!" 
alert(stringValue); //"hello" 

ECMAScript还提供了三个基于子字符串创建新字符串的方法:slice()、substr()和 substring()。这三个方法都会返回被操作字符串的一个子字符串,而且也都接受一或两个参数。第一个参数指定子字符串的开始位置,第二个参数(在指定的情况下)表示子字符串到哪里结束。具体来说,slice()和substring()的第二个参数指定的是子字符串最后一个字符后面的位置。而 substr()的第二个参数指定的则是返回的字符个数。如果没有给这些方法传递第二个参数,则将字符串的长度作为结束位置。与concat()方法一样,slice()、substr()和 substring()也不会修改字符串本身的值——它们只是返回一个基本类型的字符串值,对原始字符串没有任何影响。

var stringValue = "hello world"; 
alert(stringValue.slice(3)); //"lo world" 
alert(stringValue.substring(3)); //"lo world" 
alert(stringValue.substr(3)); //"lo world" 
alert(stringValue.slice(3, 7)); //"lo w" 
alert(stringValue.substring(3,7)); //"lo w" 
alert(stringValue.substr(3, 7)); //"lo worl" 

在传递给这些方法的参数是负值的情况下,它们的行为就不尽相同了。其中,slice()方法会将传入的负值与字符串的长度相加,substr()方法将负的第一个参数加上字符串的长度,而将负的第二个参数转换为 0。最后,substring()方法会把所有负值参数都转换为 0。

var stringValue = "hello world"; 
alert(stringValue.slice(-3)); //"rld" 
alert(stringValue.substring(-3)); //"hello world" 
alert(stringValue.substr(-3)); //"rld" 
alert(stringValue.slice(3, -4)); //"lo w" 
alert(stringValue.substring(3, -4)); //"hel" 
alert(stringValue.substr(3, -4)); //""(空字符串)

在给 slice()和 substr()传递一个负值参数时,它们的行为相同。这是因为-3 会被转换为 8(字符串长度加参数 11+(3)=8),实际上相当于调用了 slice(8)和 substr(8)。但 substring()方法则返回了全部字符串,因为它将-3 转换成了 0。

当第二个参数是负值时,这三个方法的行为各不相同。slice()方法会把第二个参数转换为 7,这就相当于调用了 slice(3,7),因此返回"lo w"。substring()方法会把第二个参数转换为 0,使调用变成了 substring(3,0),而由于这个方法会将较小的数作为开始位置,将较大的数作为结束位置,因此最终相当于调用了 substring(0,3)。substr()也会将第二个参数转换为 0,这也就意味着返回
包含零个字符的字符串,也就是一个空字符串。

3.字符串位置方法

有两个可以从字符串中查找子字符串的方法:indexOf()和lastIndexOf().两个方法都是从一个字符串中搜索给定的子字符串,然后返子字符串的位置(如果没有找到该子字符串,则返回-1).这两个的区别在于:indexOf()方法从字符串头的开头向后搜索字符串,而lastIndexOf()方法是从字符串的末尾向前搜索字符串。

var stringValue = "hello world"; 
alert(stringValue.indexOf("o")); //5 
alert(stringValue.lastIndexOf("o")); //7 

这两个方法都可以接收可选的第二个参数,表示从字符串中的哪个位置开始搜索。换句话说,indexOf()会从该参数指定的位置向后搜索,忽略该位置之前的所有字符;而 lastIndexOf()则会从指定的位置向前搜索,忽略该位置之后的所有字符。

var stringValue = "hello world"; 
alert(stringValue.indexOf("o", 6)); //7 
alert(stringValue.lastIndexOf("o", 6)); //5

4.trim()方法

创建一个字符串的副本,删除前置及后缀的所有空格,然后返回结果。

var stringValue = " hello world "; 
var trimmedStringValue = stringValue.trim(); 
alert(stringValue); //" hello world " 
alert(trimmedStringValue); //"hello world" 

5.字符串大小写转换方法

toLowerCase()、toLocaleLowerCase()、toUpperCase()和 toLocaleUpperCase()。其中,toLowerCase()和 toUpperCase()是两个经典的方法,借鉴自 java.lang.String 中的同名方法。而 toLocaleLowerCase()和 toLocaleUpperCase()方法则是针对特定地区的实现。对有些地区来说,针对地区的方法与其通用方法得到的结果相同,但少数语言(如土耳其语)会为 Unicode 大小写转换应用特殊的规则,这时候就必须使用针对地区的方法来保证实现正确的转换。

var stringValue = "hello world"; 
alert(stringValue.toLocaleUpperCase()); //"HELLO WORLD" 
alert(stringValue.toUpperCase()); //"HELLO WORLD" 
alert(stringValue.toLocaleLowerCase()); //"hello world" 
alert(stringValue.toLowerCase()); //"hello world" 

以上代码调用的 toLocaleUpperCase()和 toUpperCase()都返回了"HELLO WORLD",就像调用toLocaleLowerCase()和 toLowerCase()都返回"hello world"一样。一般来说,在不知道自己的代码将在哪种语言环境中运行的情况下,还是使用针对地区的方法更稳妥一些。

6.字符串的模拟匹配方法

String类型定义了几个用于在字符串中匹配模式的方法。第一个方法就是match(),在字符串上调用这个方法,本质上与调用RegExp的exec()方法相同。match()方法只接受一个参数,要么是一个正则表达式要么是一个RegExp对象。

var text = "cat,bat,sat,fat";
var pattern = /.at/;

//与pattern.exec(text)相同
var matches = text.match(pattern);
alert(matches.index);    //0
alert(matches[0]);       //"cat"
alert(pattern.lastIndex) //0

本例中的match()方法返回了一个数组;如果是调用RegExp对象的exec()方法并传递本例中的字符串作为参数,那么也会得到与此相同的数组:数组的第一项是与整个模式匹配的字符串,之后的每一项(如果有)保存着与正则表达式中的捕获组匹配的字符串。

另一个用于查找模式的方法是search().这个方法的唯一参数与match()方法的参数相同:由字符串或RegExp对象指定的一个正则表达式。search()方法返回字符串中第一个匹配项的索引;如果没有找到匹配项,则返回-1.而且,search()方法始终是从字符串开头向后查找模式。

var text = "cat,bat,sat,fat";
var pos = text.search(/at/);
alert(pos);    //1

search()方法返回1,即at在字符串中第一次出现的位置。

为了简化替换子字符串的操作,ECMAScript提供了replace()方法。这个方法接受两个参数:第一个参数可以是一个RegExp对象或者一个字符串(这个字符串不会被转换成正则表达式),第二个参数可以是一个字符串或者一个函数。如果第一个参数是字符串,那么只会替换第一个子字符串。要想替换所有子字符串,唯一的办法是提供一个正则表达式,而且要指定全局(g);

var text = "cat, bat, sat, fat"; 
var result = text.replace("at", "ond"); 
alert(result); //"cond, bat, sat, fat" 
result = text.replace(/at/g, "ond"); 
alert(result); //"cond, bond, sond, fond" 

var text = "cat, bat, sat, fat"; 
result = text.replace(/(.at)/g, "word ($1)"); 
alert(result); //word (cat), word (bat), word (sat), word (fat) 

每个以"at"结尾的单词都被替换了,替换结果是“word”后跟一对圆括号,而圆括号中是被字符序列$1所替换的单词。

replace()方法的第二个参数也可以是一个函数。在只有一个匹配项的情况下,会向这个函数传递3个参数:模式的匹配、模式匹配项在字符串中的位置和原始字符串。在正则表达式中定义了多个捕获组的情况下,传递给函数的参数一次是模式的匹配项、第一个捕获组的匹配项、第二个捕获组的匹配项......,但最后两个参数仍然分别是模式的匹配项在字符串中的位置和原始字符串。这个函数应该返回一个字符串,表示应该被替换的匹配项使用函数作为replace()方法第二个参数可以实现更加精细的替换操作。

function htmlEscape(text){
	return text.replace(/[<>"&]/g,function(match,pos,originalText){
		switch(match){
			case "<":
			return "&lt;";
			case ">":
			return "&gt;";
			case "&":
			return "&amp;";
			case "\"":
			return "&quot;";
		}
	});
}

alert(htmlEscape("<p class=\"greeting\">Hello world!</p>"));

//&lt;p class=&quot;greeting&quot;&gt;Hello world!&lt;/p&gt;

split()方法可以基于指定的分隔符将一个字符串分割成多个字符串,并将结果放在一个数组中。分隔可以是字符串,也可以是一个RegExp对象(这个方法不会将字符串看成正则表达式)。split()方法可以接受可选的第二个参数,用于指定数组的大小,以便确保返回的数组不会超过既定大小。

var colorText = "red,blue,green,yellow";
var colors1 = colorText.split(",");        //["red","blue","green","yellow"]
var colors2 = colorText.split(",",2);      //["red","blue"]
var colors3 = colorText.split(/[^\,]+/);   //["", ",", ",", ",", ""]

7.localeCompare()方法

比较两个字符串,并返回下列值中的一个:

  • 如果字符串在字母表中应该排在字符串参数之前,则返回一个负数(大多数情况下是-1,具体的值要视现实而定);
  • 如果字符串等于字符串参数,则返回0;
  • 如果字符串在字母表中应该排在字符串参数之后,则返回一个正数(大多数情况下是1,具体的值同样要视现实而定);
alert(stringValue.localeCompare("brick")); //1 
alert(stringValue.localeCompare("yellow")); //0 
alert(stringValue.localeCompare("zoo")); //-1 
function determineOrder(value) { 
 var result = stringValue.localeCompare(value); 
 if (result < 0){ 
 alert("The string 'yellow' comes before the string '" + value + "'."); 
 } else if (result > 0) { 
 alert("The string 'yellow' comes after the string '" + value + "'."); 
 } else { 
 alert("The string 'yellow' is equal to the string '" + value + "'."); 
 } 
} 
determineOrder("brick"); 
determineOrder("yellow"); 
determineOrder("zoo"); 

8.fromCharCode()方法

接收一或多个字符编码,然后将它们转换成一个字符串。

alert(string.fromCharCode(104,101,108,108,111)); //hello

9.HTML方法

早期的 Web 浏览器提供商觉察到了使用 JavaScript 动态格式化 HTML 的需求。于是,这些提供商就扩展了标准,实现了一些专门用于简化常见 HTML 格式化任务的方法。下表列出了这些 HTML 方法。不过,需要请读者注意的是,应该尽量不使用这些方法,因为它们创建的标记通常无法表达语义。

单体内置对象

由ECMAScript提供的、不依赖于宿主环境的对象,这些对象在ECMAScript程序之前就已经纯在了。也就是说,开发人员不必显式地实例化内置对象,因为它们已经实例化了。

Global对象

不属于任何其他对象的属性和方法,最终都是它的属性和方法。事实上,没有全局变量和函数;所有在全局作用域中定义的属性和函数,都是Global对象的属性。

1.URI编码方法

Global对象的encodeURI()和encodeURIComponent()方法可以对URI(Uniform Resource Identifiers,通用资源标识符)进行编码,以便发送给浏览器。有效的URI中不能包含某些字符,例如空格。而这两个URI编码方法就可以对URI进行编码,它们用特殊的UTF-8编码替换所有无效的字符,从而让浏览器能够接受和理解。

其中,encodeURI主要用于整个URI,而encodeURIComponent()主要用于URI中的某一段进行编码。它们的主要区别在于,encodeURI()不会对本身属于URI的特殊字符进行编码,例如冒号、正斜杠、问好和井字号;而encodeURIComponent()则会对它发现的任何非标准字符进行编码。

var uri = "http://www.wrox.com/illegal value.htm#start";

//"http://www.wrox.com/illegal%20value.htm#start";
alert(encodeURI(uri));

//"http%3A%2F%2Fwww.wrox.com%2Fillegal%20value.html%23start"
alert(encodeURIComponent(uri));

使用encodeURI()编码后的结果是除了空格之外的其他字符都原封不动,只有空格被替换成了%20。而encodeURIComponent()方法则会使用对应的编码替换所有非字母数字字符。

与encodeURI()和encodeURIComponent()方法对应的两个方法分别是decodeURI()和decodeURIComponent().其中,decodeURI()只能对使用encodeURI()替换的字符进行解码。

2.eval()方法

现在,我们介绍最后一个——大概也是整个ECMAScript语言中最强大的一个方法:eval()。eval()方法就像是一个完整的ECMAScript解析器,它只接受一个参数,即要执行的ECMAScript字符串。

eval("alert("hi")");

等价于

alert("hi");

当解析器发现代码中调用eval()方法时,它会将传入的参数当作实际的ECMAScript语句来解析,然后把执行结果插入到原位置。通过eval()执行的代码被认为是包含该次调用的执行环境的一部分,因此被执行的代码具有与该执行环境相同的作用域链。这意味着通过eval()执行的代码可以引用在包含环境中定义的变量。

在eval()中创建的任何变量或函数都不会被提升,因为在解析代码的时候,它们被包含在一个字符串中;它们只在eval()执行的时候创建。

3.Global对象的属性

4.window对象

ECMAScript虽然没有指出如何直接访问Global对象,但Web浏览器都是将这个全局对象作为window对象的一部分加以实现的。因此,在全局作用域中声明的所有变量和函数,就都成为了window对象的属性。

var color = "red";
function sayColor(){
	alert(window.color);
}
window.sayColor(); //"red"

在这里定义了一个名为color的全局变量和一个名为sayColor()的全局函数。在sayColor()内部,我们通过window.color来访问color变量,以说明全局变量时window对象的属性。然后,又使用window.sayColor()直接通过window对象调用这个函数,结果显示在了警告框中。

另一种取得Global对象的方法是使用以下代码:

var global = function(){
	return this;
}();

以上代码创建了一个立即调用的函数表达式,返回this值。如前所诉,在没有给函数明确指定this值得情况下(无论是通过将函数添加为对象的方法,还是通过调用call()或apply()),this值等于Global对象。而像这样通过简单地返回this来取得Global对象,在任何环境下都是可行的。

Math对象

1.Math对象的属性

2.min()和max()方法

最小值最大值

var max = Math.max(3,54,32,16);
alert(max);       //54

var min = Math.min(3,54,32,16);
alert(min);       //3

要找出数组中最大或最小值,可以像下面这样使用apply()方法。

var values = [1,2,3,4,5,6,7,8];

var max = Math.max.apply(Math,values);

3.舍入方法

Math.ceil()执行向上舍入,即它总是将数值向上舍入为最接近的整数;

Math.floor()执行向下舍入,即它总是将数值向下舍入为最接近的整数;

Math.fround()执行标准舍入,即它总是将数值四舍五入为最接近的整数。

4.random()方法

Math.random()方法返回大于等于0小于1的一个随机数。

值 = Math.floor(Math.random()*可能值得总数 +第一个可能的值)

5.其他方法

猜你喜欢

转载自blog.csdn.net/baidu_29474379/article/details/84400719