Ch5 Reference Types
本文为书籍《Professional JavaScript for Web Developers, 3rd Edition》英文版第 5 章:Reference Types 学习笔记,详细介绍对象、数组、日期时间、函数、字符串等基本数据类型及其方法、属性和主要浏览器的支持情况。
1.Object 类型
(1)产生对象的几种方式
第一种方式:
var person = new Object();
person.name = "Nicholas";
person.age = 29;
第二种方式:
var person = {}; // same as new Object()
person.name = "Nicholas";
person.age = 29;
第三种方式:
// 这是定义对象较好的一种方式,书写方便,简洁易读
var person =
{ // 属性和属性的值用冒号分开
name : "Nicholas", // 用逗号分开属性定义
age : 29 // 最后定义的属性不需要逗号
}; // 结尾最好带分号
在第三种方式中,可以使用字符串和数字作为属性名称,如:
var person =
{
"name" : "Nicholas",
"age" : 29,
5: true // 为数字的属性名称将自动转为字符串
};
(2)访问属性的方式
使用点号访问:
alert(person.name);
使用方括号访问:
alert(person["name"]);
使用方括号访问属性可以通过变量访问属性,如:
var propertyName = "name";
alert(person[propertyName]);
另外,方括号访问的属性名称可以包含空格和关键字,如:
person["first name"] = "Nicholas";
2.Array 类型
JavaScript 中,同一个数组内的各个元素的数据类型可以不相同,数
组的长度可以动态改变,数组的元素也可以动态添加,访问超过数组长度
的元素以及未赋值的元素结果将是 undefined。
(1)产生数组
// 使用 Array 对象的构造函数产生数组
var colors = new Array();
var colors = new Array(20);
var colors = new Array("red", "blue", "green");
// 当 Array 构造函数只有一个参数,参数的数据类型不同
// 产生的数组长度和内容也不同
var colors = new Array(3); // 产生一个有3个元素的数组
var names = new Array("Greg"); // 产生只有一个元素“Greg”的数组
// 可以不使用 new 运算符通过 Array 构造函数产生数组
var colors = Array(3); // 产生一个有3个元素的数组
var names = Array("Greg"); // 产生只有一个元素“Greg”的数组
// 使用方括号产生数组
var colors = ["red", "blue", "green"]; // 产生有3个字符串元
// 素的数组
var names = []; // 产生一个空数组
var values = [1,2,]; // AVOID! 产生有2个或3个元素的数组
var options = [,,,,,]; // AVOID! 产生有5个或6个元素的数组
(2)设置和获取数组的长度及元素值
// 设置和获取数组元素的值
var colors = ["red", "blue", "green"]; // 定义数组
alert(colors[0]); // 访问数组的第 1 个元素
colors[2] = "black"; // 改变数组第 3 个元素的值
colors[3] = "brown"; // 添加第 4 个元素
// 通过 length 属性获取数组的长度
var colors = ["red", "blue", "green"];
var names = [];
alert(colors.length); // 3
alert(names.length); // 0
// 设置 length 属性的值少于元素的个数
var colors = ["red", "blue", "green"];
colors.length = 2; // 数组长度改变为2,第 3 个元素被删除
alert(colors[2]); // undefined
// 设置 length 属性的值超过元素的个数
var colors = ["red", "blue", "green"];
colors.length = 4;
alert(colors[3]); // undefined
// 通过 length 属性作为元素索引添加元素
var colors = ["red", "blue", "green"];
colors[colors.length] = "black";
colors[colors.length] = "brown";
colors[99] = "black";
alert(colors.length); // 100
alert(colors[10]); // undefined
(3)要判断一个变量或对象是否为数组类型,有两种方法:
// 使用 instanceof 运算符
if (value instanceof Array)
{
//do something on the array
}
// 使用 isArray() 方法
if (Array.isArray(value))
{
//do something on the array
}
但是,instanceof 运算符在判断来自另外一个 frame、iframe等的数
组变量时就会有问题。isArray() 方法在 ECMAScript 5 中引入,目前,
支持该方法的浏览器有 IE 9+, Firefox 4+, Safari 5+, Opera 10.5+ 和
Chrome。可以结合类型与功能检测技术使用 isArray() 方法。
(4)转换方法
任何对象都具有 toLocaleString(),toString(),和 valueOf() 方法,
调用数组的这几个方法通常输出的内容都是逗号隔开的各元素字符串表示
形式,如果一个元素的值为 null 或 undefined,其输出结果是一个空字
符串。例如:
var colors = ["red", "blue", "green"];
alert(colors.toString()); //red,blue,green
alert(colors.toLocaleString()); //red,blue,green
alert(colors.valueOf()); //red,blue,green
alert(colors); //red,blue,green
然而,可以通过重写这些方法让其输出不同内容,如:
var person1 = {
toLocaleString: function ()
{
return "Nikolaos";
},
toString: function ()
{
return "Nicholas";
}
};
var person2 = {
toLocaleString: function ()
{
return "Grigorios";
},
toString: function ()
{
return "Greg";
}
};
var people = [person1, person2];
alert(people); //Nicholas,Greg
alert(people.toString()); //Nicholas,Greg
alert(people.toLocaleString()); //Nikolaos,Grigorios
可以使用 join() 方法将输出的各元素以指定的符号隔开,例如:
var colors = ["red", "blue", "green"];
alert(colors.join(",")); // red,green,blue
alert(colors.join("||")); // red||green||blue
如果 join ()方法未传递任何参数或参数为 undefined,则将用逗号
隔开各元素,但IE 7 及其以下版本会用字符串"undefined"隔开。
(5)栈(stack)方法
数组的 push() 和 pop() 方法可以对数组的元素按照后入先出的规则
进行操作,如同数据结构中的"栈"一样。
push() 方法可以接受任一多个参数,将它们按参数顺序添加到数组的
结尾。该方法的返回值为压栈后数组的长度。
pop() 方法不需要参数,它将数组最后一个元素删除,返回值为被删除
的元素。
例如:
var colors = new Array(); //create an array
var count = colors.push("red", "green"); //push two items
alert(count); // 2
alert(colors[0]); // red
alert(colors[1]); // green
alert(colors[2]); // undefined
count = colors.push("black"); //push another item on
alert(count); // 3
var item = colors.pop(); //get the last item
alert(item); // black
alert(colors.length); // 2
(6)队列( queue )方法
数组的 push() 和 shift() 方法结合可以对数组的元素按照先入先出
的规则进行操作,如同数据结构中的"队列"一样。
shift() 方法不需要参数,它将数组第一个元素(索引为 0)删除,返
回值为被删除的元素,数组的长度减 1。
例如:
var colors = new Array(); // 产生一个数组
var count = colors.push("red", "green"); // 将两个元素入队
alert(count); // 2
count = colors.push("black"); // 加入到数组结尾
alert(count); // 3
var item = colors.shift(); // 删除并获取数组第一个元素
alert(item); // "red"
alert(colors.length); // 2
数组的 unshift() 和 pop() 方法结合可以使数组成为一个反方向的队
列,从数组的头部加入元素,尾部检索元素。
unshift() 方法接受任意多个参数,并将它们加入到数组的开始出,并
返回新数组的长度值,但 IE 7及更早版本返回 undefined。
例如:
var colors = new Array();
var count = colors.unshift("red", "green");
alert(count); // 2
alert(colors); // red,green
count = colors.unshift("black"); // push another item on
alert(count); // 3
alert(colors); // black,red,green
var item = colors.pop();
alert(item); // green
alert(colors.length); // 2
alert(colors); // black,red
(7)重排序方法
reverse()方法用于颠倒元素在数组中的顺序,例如:
var values = [1, 2, 3, 4, 5];
values.reverse();
alert(values); //5,4,3,2,1
sort()方法默认行为按照字符串的比较规则将数组升序排列,即使数
组元素全是数字,也将按照字符串的比较规则排列,例如:
var values = [0, 1, 5, 10, 15];
values.sort();
alert(values); //0,1,10,15,5
sort()方法可接受一个参数,这个参数是一个比较函数(实际上相当于
回调函数,callback),用于对数组中的元素按照某种规则进行两两比较。
比较函数接受两个参数,如果第一个参数应该排在第二个参数之前,则比
较函数返回一个负值;如果两个参数相等,则返回0;如果第一个参数应该
排在第二个参数之后,则返回一个正值。
例如:
// 该比较函数用于将数字数组中的值进行升序排列
function compare(value1, value2)
{
if (value1 < value2)
{
return -1;
} else if (value1 > value2)
{
return 1;
} else
{
return 0;
}
}
var values = [0, 1, 5, 10, 15];
values.sort(compare);
alert(values); // 0,1,5,10,15
下面这个简化版的比较函数可以将数组的元素按照数字比较规则进行
升序排列:
function compare(value1, value2)
{
return value1 - value2;
}
(8)操作方法
concat()可以基于当前数组产生新的数组。它接受任意多个参数,产
生当前数组的副本,将参数添加到副本数组的结尾,并返回新数组。
如果没有参数,concat()方法只产生和返回当前数组的副本。
如果参数是一个或多个数组,concat()方法产生当前数组的副本,将
每个数组参数中的项按顺序添加到新数组的结尾并返回新数组。
如果参数不是数组类型,concat()方法产生当前数组的副本,将参数
作为数组的项添加到新数组的结尾并返回新数组。
例如:
var colors = ["red", "green"];
var colors2 =
colors.concat("yellow", ["black", "brown"],colors);
alert(colors); //red,green
alert(colors2); //red,green,yellow,black,brown,red,green
slice()方法从当前数组中按照指定的范围复制数组中的元素,并产生
一个新的数组。该方法接受一个或两个参数。
如果没有参数,slice()方法返回整个当前数组。
如果参数为一个,slice()方法返回从参数指定的当前数组元素的索引
到数组结尾的所有元素,产生一个新的数组并返回。
如果参数为两个,slice()方法返回两个参数所指定的起始索引和结束
索引之间的数组元素(包括起始索引元素,不包括结束索引元素),产生一
个新的数组并返回。
如果参数中有负值,则相当于数组的长度减去负参数的绝对值,例如
,对于一个有 5 个元素的数组,slice(-2, -1)相当于 slice(3, 4)。
如果结束索引小于开始索引,则返回一个空数组。
例如:
var colors = ["red", "green", "blue", "yellow", "purple"];
var colors2 = colors.slice();
var colors3 = colors.slice(1);
var colors4 = colors.slice(1, 4);
alert(colors2); //red,green,blue,yellow,purple
alert(colors3); //green,blue,yellow,purple
alert(colors4); //green,blue,yellow
splice()方法可对数组中的项进行删除、插入和替换操作。它的返回
结果为被移除项所组成的数组或一个空数组。
删除:通过为 splice()传递两个参数,可以删除数组中任意多个项,
第一个参数为第一个要被删除的项在数组中的索引,第二个参数为要删
除项的个数。例如:splice(0, 2)删除数组前两个项。
插入:通过为 splice()传递三个或更多个参数,可以可以将一个或
多个新项插入到数组中。第一个参数为起始位置,第二个参数为 0 (表
示要删除项的个数为 0),接下来可以指定多个参数表示要插入的项。
替换:第一个参数为起始位置,第二个参数为要删除项的个数,接下
来的一个或多个参数为要插入的项。要插入项的个数不必匹配要删除项
的个数。例如,splice(2, 1, "red", "green")将删除索引为 2的项,并
将"red", "green"两个新项插入。
例如:
var colors = ["red", "green", "blue"];
var removed = colors.splice(0, 1); // 删除第一个项
alert(colors); // green,blue
alert(removed); // red - 仅有一个项的数组
// 在位置 1插入两个项
removed = colors.splice(1, 0, "yellow", "orange");
alert(colors); // green,yellow,orange,blue
alert(removed); // empty array
// 删除一个项,插入两个项
removed = colors.splice(1, 1, "red", "purple");
alert(colors); //green,red,purple,orange,blue
alert(removed); //yellow - one item array
(9)定位方法
indexOf() 和 lastIndexOf() 方法是 ECMAScript 5中为数组的实例
加入的两个方法,用于查找为某个值的项在数组中的索引。它们都接受
两个参数,第一个参数为要查找的项的值;第二个参数是可选参数,为
查找的起始索引。indexOf() 方法从数组的开始或指定的索引处往结尾
处搜索,lastIndexOf() 方法从数组的结尾或指定的索引位置往数组的
开始处搜索。如果数组中有多个与所查找项值相同的元素,仅返回第一
个被查找到的索引。
两个方法的返回值都是被检索项在数组中的索引,如果项在数组中
没有检索到,则返回值为 -1。第一个参数与数组中的项是按照绝对相
等来比较的,没有隐式数据类型转换,相当于用 === 运算符进行比较。
当前支持该方法的浏览器有Internet Explorer 9+, Firefox 2+,
Safari 3+, Opera 9.5+, 和 Chrome。
例如:
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
(10)迭代方法
ECMAScript 5为数组定义了 5 个迭代方法,每个方法都接受两个参数:
第一个参数是一个函数,作用于数组中的每个项;第二个参数是一个可选
参数,为一个作用域对象,指定第一个参数所用于的对象(影响 this的值
)。向这五个方法传递的函数接受三个参数:数组项的值,项在数组中的
索引,和数组对象本身。当前支持这些迭代方法的浏览器有 IE 9+,
Firefox 2+, Safari 3+, Opera 9.5+, 和 Chrome。这些迭代方法是:
every():给定的函数按照某种规则检查数组中的每个项,若一个项符
合规则,函数将返回 true,否则返回 false。只有当函数所检查的所有
项都为 true,every()方法的返回值才是 true,否则为 false。
some():给定的函数按照某种规则检查数组中的每个项,若一个项符
合规则,函数将返回 true,否则返回 false。只要函数所检查的项有一
个为 true,some()方法的返回值就是 true,否则为 false。
filter():其返回值为一个数组,这个数组中的项由源数组中符合给
定函数规则的项组成。给定的函数按照某种规则检查源数组中的每个项,
若一个项符合规则,函数将返回 true,否则返回 false,能够使给定函
数返回值为 true的源数组中的项将作为 filter()方法所返回的数组中
的项。
map():返回的是源数组中的每一项按照给定函数的规则改变后形成
的新数组。如同数学中的一一映射一样。比如,该方法可以将源数组中
的每一项都乘以 2,然后结果作为返回数组的项。
forEach():该方法没有返回值,仅仅将给定函数作用于源数组中的
每个项,如同 for语句对数组中的项进行迭代。
例如:
var numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];
// 参数“array”实际上在every内部用numbers作为实参传递,下同
var everyResult = 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
var filterResult = numbers.filter(function (item, index, array)
{
return (item > 2);
});
alert(filterResult); //[3,4,5,4,3]
var mapResult = numbers.map(function (item, index, array)
{
return item * 2;
});
alert(mapResult); //[2,4,6,8,10,8,6,4,2]
numbers.forEach(function (item, index, array)
{
// do something here
});
(11).缩减(Redunction)方法
ECMAScript 5为数组引入了reduce() 和 reduceRight()两个缩减方法,
两个方法都迭代数组中的所有项,然后生成一个值作为方法的返回值。
reduce() 方法从数组的第一个项开始操作直到最后一个;reduceRight()
方法则是反向操作。
两个方法都接受两个参数:被调用作用于每个项的函数和缩减要作用
于的、可选的初始值。被传递给 reduce() 或 reduceRight()的函数接受
四个参数:前一个值,当前值,项的索引和数组对象。函数返回的任何
值将被自动地作为下一个迭代的第一个参数传递。第一次迭代的当前值是
数组中的第二个项,所以,第一个参数是数组中的第一项,第二个参数
是数组中的第二项。
可以使用 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。结果与
上个段代码是相同的,因为都是简单的相加操作。
当前,支持这两个方法的浏览器有 IE 9+, Firefox 3+, Safari 4+,
Opera 10.5+, 和 Chrome。
3.Date类型
(1)Date类型使用自 UTC 时间 1970 年 1 月 1 日午夜(零时)开始经
过的毫秒数来保存日期,能够精确到 1970 年 1 月 1 日之前或之后的
285616 年。
建立一个日期对象的方法如下:
var now = new Date();
如果建立日期对象时 Date()构造函数没有传递参数,则当前时间将
被分配给产生的对象。如果传递参数,将会显式或隐式地调用
Date.parse() 或 Date.UTC()方法将参数进行解析,如:
var someDate = new Date(Date.parse("May 25, 2004"));
var someDate = new Date("May 25, 2004");
// January 1, 2000 at midnight GMT
var y2k = new Date(Date.UTC(2000, 0));
// May 5, 2005 at 5:55:55 PM GMT
var allFives = new Date(Date.UTC(2005, 4, 5, 17, 55, 55));
// January 1, 2000 at midnight in local time
var y2k = new Date(2000, 0);
// May 5, 2005 at 5:55:55 PM local time
var allFives = new Date(2005, 4, 5, 17, 55, 55);
对于以上几个日期变量,在各浏览器显示的结果的格式不尽相同,对
于参数的解析也不尽相同。比较合适的参数如:"2010/3/25 9:8:25"。
Date.now()是 ECMAScript 5中定义的方法,用于获取当前时间的毫
秒表示,如:
var current = Date.now();
alert(current); // 1332752623015
目前支持 Date.now()方法的浏览器有 IE 9+, Firefox 3+, Safari
3+, Opera 10.5, 和 Chrome。对于不支持的浏览器,可以使用 + 运算
符来模拟,如:
var start = +new Date();
(2)继承的方法和日期格式方法
Date是一个对象,所以具有toString()、toLocaleString()、
valueOf()三个方法。valueOf()方法返回的结果是日期的毫秒表示,
可以用于比较计算。toString()方法返回日期的时区表示法,UTC或者
GMT。toLocaleString()方法返回本地日期时间格式表示法。有相应的
获取日期或时间的字符串表示方法。对于获取中国本地的日期时间,
IE 9和 FireFox 10的结果相同,其它则有所差别。以 FireFox 10为例:
var aTime = new Date("2010/2/25 5:10:9");
alert(aTime.valueOf()); // 1267045809000
alert(aTime.toLocaleString()); // 2010年2月25日 5:10:09
alert(aTime.toLocaleDateString()); // 2010年2月25日
alert(aTime.toLocaleTimeString()); // 5:10:09
alert(aTime.toString()); // Thu Feb 25 05:10:09 UTC+0800 2010
alert(aTime.toDateString()); // Thu Feb 25 2010
alert(aTime.toTimeString()); // 05:10:09 UTC+0800
alert(aTime.toGMTString()); // Wed, 24 Feb 2010 21:10:09 GMT
alert(aTime.toUTCString()); // Wed, 24 Feb 2010 21:10:09 UTC
因为这些方法会根据环境的不同而显示不同的格式(甚至时间也不同),
所以不建议使用这些方法向用户显示日期时间信息。通常使用这些方法进
行调试。
(3)日期/时间组件方法
日期对象有一系列方法用于获取/设置年、月、日、时、分、秒、毫秒
和与 UTC 标准时间的时区差,还有与这些方法对应的UTC版本,详见本书。
4.RegExp类型
略(详见本书)。
5.Function类型
(1)JavaScript中的函数都是对象,每个函数都是 Function类型的实
例,都有自己的属性和方法。和其它引用类型一样,因为函数是对象,
函数的名称只是函数对象的指针,而不是只绑定在一个函数上。定义一
个函数有三种方式:
// 常规的声明函数定义方式
function sum (num1, num2)
{
return num1 + num2;
}
// 函数表达式定义函数方式
var sum = function(num1, num2)
{
return num1 + num2;
}; // 注意:结尾有分号
// 使用 Function构造函数定义函数方式 -- 不推荐
var sum = new Function("num1", "num2", "return 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
// 返回 anotherSum 所指向函数的定义
// function sum(num1, num2) { return num1 + num2; }
alert(anotherSum);
alert(sum); // null
alert(sum(10, 20)); // ERROR
(2)作为值的函数
一个函数可以作为另一个函数的参数,也可以作为另一个函数的返
回值。如:
function callSomeFunction(someFunction, someArgument)
{
return someFunction(someArgument);
}
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"
在上面的例子中,函数 callSomeFunction()的第一个参数为一个函
数,第二个参数也将作为第一个参数的参数。callSomeFunction()的返
回值是第一个参数执行的结果,而非函数。
下面的例子中,为了按照项的属性来排列数组 data中的项,data的
sort()方法将被传递一个函数 createComparisonFunction()作为其参数
。createComparisonFunction()的参数为 data数组中项的属性,返回值
为一个函数,用于依次比较 data数组中相邻两个项的属性:
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
(3)函数的内部对象
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的值,将函数指针保存在
了另一个位置。然后,factorial变量又重新指向一个返回0的函数。如果
像原来的 factorial()函数那样不使用 arguments.callee,调用
trueFactorial()函数就返回0。可是,在删除了函数体内的代码与函数名
的耦合状态之后,trueFactorial()函数仍然能够正常地计算阶乘,至于
fatorial(),它现在只是一个返回 0 的函数。
不过,在严格模式下,arguments.callee的使用是受到限制的,所以,
上面的递归方法在严格模式下会产生错误。下面的递归函数可以解决这个
问题:
var factorial = (function f(num)
{
if (num <= 1)
{
return 1;
} else
{
return num * f(num - 1);
}
});
this对象为函数内部的另一个特殊对象,它与 Java、C#中的 this类
似,但不完全相同。它是函数所据以执行的环境对象的引用,常被称为
this 值。当函数在全局网页内被调用时,this对象指向 window。this
的值是不确定的,直到函数被调用执行时 this的值才被确定。如:
window.color = "red";
var o = { color: "blue" };
function sayColor()
{
alert(this.color);
}
sayColor(); //"red"
o.sayColor = sayColor;
o.sayColor(); //"blue"
需要谨记的是,函数的名称只是包含指针的变量,因此,全局作用
域内的 sayColor()函数和 o.sayColor()是指向同一个函数,尽管它们
在不同的环境中执行。
caller是 ECMAScript 5中正式引入的一个函数对象属性,大多数浏
览器都支持,除了较早版本的 Opera。该属性包含一个对调用该函数的
函数的引用,如果函数是在全局作用域中被调用,则该属性的值为
null。例如:
function outer()
{
inner(); // 函数outer()调用了函数inner() -- 定义调用
}
function inner()
{ // 哪个函数调用 inner(),哪个函数就是 caller
alert(inner.caller); // 显示caller的源代码
}
outer(); // 函数outer()调用了函数inner() -- 执行调用
为了松耦合,也可以使用arguments.callee.caller访问同样的调用
信息:
function outer()
{
inner();
}
function inner()
{
alert(arguments.callee.caller)
}
outer();
当函数代码按严格模式执行时,试图访问 arguments.callee将产生
一个错误。ECMAScript 5也定义了一个 arguments.caller,在严格模式
下也将产生一个错误,在非严格模式下总是 undefined。这是为了消除
arguments.caller和函数的 caller属性之间的困惑。这些改变为语言增
加了安全性,所以,第三方代码不能够检查在同样环境中运行的代码。
严格模式也增加了一个限制:不可以将一个值分配给函数的 caller属性
,这样做将产生一个错误。
(4)函数的属性和方法
length属性表示函数所期望的命名参数(形参)的个数。
prototype属性是引用类型所有实例方法所在的实际位置,这意味着
像 toString()和 valueOf()这样的方法实际是存在于 prototype上,只
不过是通过对象的实例访问而已。这个属性对于自定义类型和继承是非
常重要的。在 ECMAScript 5中,prototype属性是不可枚举的,因此使
用 for-in语句将不能发现它。
apply()方法使用一个特定的 this值调用函数,实际上是设置函数体
内 this对象的值。apply()函数接受两个参数:作为 this值传递到函数
内的一个对象的和一个参数数组。第二个参数可以是一个 Array对象的
实例,也可以是arguments对象。例如:
function sum(num1, num2)
{
return num1 + num2;
}
function callSum1(num1, num2)
{ // 传递一个 arguments对象
return sum.apply(this, arguments);
}
function callSum2(num1, num2)
{ //传递一个数组
return sum.apply(this, [num1, num2]);
}
alert(callSum1(10, 10)); // 20
alert(callSum2(10, 10)); // 20
上面例子中,apply()中的参数 this都等同于 window,因为是在全
局作用域内被调用。但在严格模式下,无环境对象被调用函数的 this
值并不等同于 window,而是 undefined。除非显式地将函数附加到一
个对象上,或者使用 apply() 或 call()。
call()方法与 apply()的行为一样,第一个参数都是 this,但剩下
的参数必须逐个地指定,例如:
function sum(num1, num2)
{
return num1 + num2;
}
function callSum(num1, num2)
{
return sum.call(this, num1, num2);
}
alert(callSum(10, 10)); // 20
call()方法与 apply()最强大的方面是,它们可以将不同的对象作
为 this值传递给函数内部。例如:
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对象时所举例子的一个新的版
本,与之前的例子相比,上面的例子不需要将 sayColor()函数赋值
给对象 o,对象 o也不必知道 sayColor()函数内部的细节。
bind() 方法是 ECMAScript 5中引入的新方法,该方法接受一个参
数,并根据这个参数产生一个 this值被绑定到该参数的函数新实例。
有关该方法更详细的说明,请参考本书第22章中的Function Binding
和Function Currying。
支持该方法的浏览器有 IE 9+, Firefox 4+, Safari 5.1+,
Opera 12+, 和 Chrome。例如:
window.color = "red";
var o = { color: "blue" };
function sayColor()
{
alert(this.color);
}
sayColor(); // red
var objectSayColor = sayColor.bind(o);
objectSayColor(); // blue
函数所继承的方法 toLocaleString() 和 toString()总是显示函数
的代码,不同的浏览器输出格式不尽相同。valueOf()方法将返回函数
本身。这些方法适合调试,但不适合重要的、正式的情况。
6.基本类型的包装类
像Boolean、Number、String这样的基本类型都有与之对应的引用类
型,可以使用这些引用类型的构造函数像产生一个对象一样产生它们。
使用对象类型产生的基本类型具有一些与对象类似的性质,并且有一
些与它们对应的直接产生的基本类型不同的性质。使用对象类型产生
的基本类型若参与布尔运算,则自动转换为 true。
(1)Boolean类型
Boolean类型的引用包装与直接的基本类型一些区别如下:
// 参与布尔运算的区别
var falseObject = new Boolean(false);
var result = falseObject && true;
alert(result); // true
var falseValue = false;
result = falseValue && true;
alert(result); // false
// 参与typeof运算的区别
alert(typeof falseObject); // object
alert(typeof falseValue); // boolean
// 参与 instanceof运算的区别
alert(falseObject instanceof Boolean); // true
alert(falseValue instanceof Boolean); // false
Number和String类型的引用包装与它们对应的值变量有上面例子中
的类似的性质。
(2)Number类型
toFixed()、toExponential()、toPrecision()三个方法用于将数值
按照一定的格式输出。
toFixed()方法接受一个参数,将数值按照参数所指定的小数位数
的浮点格式输出。尽管有些浏览器支持更大的小数位数,但通常不应
超过 20位,否则,在另一些浏览器中会产生错误。如果指定的小数
位数小于数值的范围,将会四舍五入。但是,IE8- 对范围在
{(–0.94,–0.5], [0.5,0.94)}的数值,将输出 0,而不是 -1或者 1。
例如:
var num = 10.005;
alert(num.toFixed(2)); // 10.01
num = 10.004;
alert(num.toFixed(2)); // 10.00
toExponential()方法接受一个参数,将数值按照参数所指定的小
数位数的指数格式输出。例如:
var num = 10;
alert(num.toExponential(1)); // 1.0e+1
toPrecision()方法,也接受一个参数,它将根据参数将数值输出
为浮点格式或者指数格式,具体要看哪种格式更合适。参数决定的是
精确到的小数位数,取值范围是 [1, 21],且最好是整数。可以没有
参数,但不能是 0和负值。
例如:
var num = 99;
alert(num.toPrecision(1); // 1e+2
alert(num.toPrecision(2)); // 99
alert(num.toPrecision(3)); // 99.0
(3)String类型
charAt()方法用于获取字符串中指定索引处的一个字符,如:
var stringValue = "hello world";
alert(stringValue.charAt(1)); // e
charCodeAt()方法用于获取字符串中指定索引处一个字符的编码。
例如:
var stringValue = "hello world";
alert(stringValue.charCodeAt(1)); // 101
ECMAScript 5定义了可以通过方括号和索引数值来获取字符串中单
个字符的方式,这种方式被 IE8+和所有版本的Firefox, Safari,
Chrome, Opera等浏览器支持。IE 7-的结果将是 undefined。
例如:
var stringValue = "hello world";
alert(stringValue[1]); // e
concat()方法用于连接字符串,但通常 + 运算符要比这个方法好
用,例如:
var stringValue = "hello ";
var result = stringValue.concat("world");
alert(result); //"hello world"
alert(stringValue); //"hello"
可以连接多个字符串,例如:
var stringValue = "hello ";
var result = stringValue.concat("world", "!");
alert(result); //"hello world!"
alert(stringValue); //"hello"
slice(startIndex, endIndex),substring(startIndex, endIndex)
和substr(startIndex, length)三个方法用于获取一个字符串的子字符
串。前两个方法的获取子字符串的区间范围是[startIndex, endIndex)
,不包括第二个参数所指定的结束索引。第二个参数如果省略,则默
认到字符串的结尾。例如:
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()方法会将字符串的长度与负值相加作为新的参数。
nsubstring()方法将所有的负值参数转换为 0。
substr()方法将第一个负值参数的值与字符串的长度值相加作为新
的第一个参数;第二个负值参数将被转换为 0 作为新的第二个参数。
但是在 IE 8-中会返回整个字符串,IE 9修复了这个问题。
例如:
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)); // (empty string)
indexOf(substr, searchIndex) 和 lastIndexOf(substr,
searchIndex)方法用于定位子字符串在字符串中的索引。例如:
var stringValue = "hello world";
alert(stringValue.indexOf("o")); // 4
alert(stringValue.lastIndexOf("o")); // 7
alert(stringValue.indexOf("o", 6)); // 7
alert(stringValue.lastIndexOf("o", 6)); // 4
利用第二个参数,通过循环调用indexOf() 或 lastIndexOf()方法,
可以定位一个子字符串的所有实例,例如:
var stringValue =
"Lorem ipsum dolor sit amet, consectetur adipisicing elit";
var positions = new Array();
var pos = stringValue.indexOf("e");
while (pos > -1)
{
positions.push(pos);
pos = stringValue.indexOf("e", pos + 1);
}
alert(positions); //"3,24,32,35,52"
trim()方法是 ECMAScript 5中为字符串引入的方法,用于去除字符
串收尾的空白。支持的浏览器有 IE 9+, Firefox 3.5+, Safari 5+,
Opera 10.5+, 和Chrome。Firefox 3.5+, Safari 5+, 和 Chrome 8+ 也
支持非标准的 trimLeft() 和 trimRight()方法,分别用于移除字符串
首尾的空白。这些方法会产生源字符串的副本,源字符串不会改变。
例如:
var stringValue = " hello world ";
var trimmedStringValue = stringValue.trim();
alert(stringValue); //" hello world "
alert(trimmedStringValue); //"hello world"
toLowerCase(), toLocaleLowerCase(), toUpperCase(), 和
toLocaleUpperCase()是几个用于转换字符串大小写的转换函数。
例如:
var stringValue = "hello world";
alert(stringValue.toLocaleUpperCase()); //"HELLO WORLD"
alert(stringValue.toUpperCase()); //"HELLO WORLD"
alert(stringValue.toLocaleLowerCase()); //"hello world"
alert(stringValue.toLowerCase()); //"hello world"
作者:
JiayangShen
水平有限,文中错误不妥在所难免,欢迎批评指正建议评论。文章将不定期修改完善斧正。转载请注明出处,谢谢!