5.1 Object 类型
new Object
我们可以通过new操作符后跟Object构造函数
var person = new Object();
person.name = "Nicholas";
person.age = 29;
对象字面量
对象字面量是对象定义的一种简写形式,目的在于简化创建包含大量属性的对象的过程。
var person = {
name : "Nicholas",
age : 29
};
左边的花括号({)表示对象字面量的开始,因为它出现在了表达式上下文(expression context)中。同理右边表示结束。在通过对象字面量定义对象时,实际上不会调用 Object 构造函数。,
在对象字面量中,使用逗号来分隔不同的属性,最后一个属性后面不要添加逗号(IE7 及更早版本和Opera 中导致错误)
表达式上下文
ECMAScript 中的表达式上下文指的是能够返回一个值(表达式)。
访问对象属性
访问对象属性时使用的都是点表示法,这也是很多面向对象语言中通用的语法。不过,在 JavaScript 也可以使用方括号表示法来访问对象的属性。
alert(person["name"]); //"Nicholas"
alert(person.name); //"Nicholas"
5.2 Array 类型
虽然 ECMAScript 数组与其他语言中的数组都是数据的有序列表,但与其他语言不同的是,ECMAScript 数组的每一项可以保存任何类型的数据。
Array 构造函数
当给构造函数传递一个值时,如果传递的是数值,则会按照该数值创建包含给定项数的数组;而如果传递的是其他类型的参数,则会创建包含那个值的只有一项的数组。
var colors = new Array(3); // 创建一个包含 3 项的数组
var names = new Array("Greg"); // 创建一个包含 1 项,即字符串"Greg"的数组
数组字面量表示法
数组字面量由一对包含数组项的方括号表示,多个数组项之间以逗号隔开,最后一项不要','。与对象一样,在使用数组字面量表示法时,也不会调用 Array 构造函数
var colors = ["red", "blue", "green"]; // 创建一个包含 3 个字符串的数组
var names = []; // 创建一个空数组
var values = [1,2,]; // 不要这样!这样会创建一个包含 2 或 3 项的数组
var options = [,,,,,]; // 不要这样!这样会创建一个包含 5 或 6 项的数组
数组的读写
要使用方括号并提供相应值的基于 0 的数字索引
var colors = ["red", "blue", "green"]; // 定义一个字符串数组
alert(colors[0]); // 显示第一项
colors[2] = "black"; // 修改第三项
colors[5] = "brown"; // 新增第6项,此时colors为["red", "blue", "green",undefined,undefined,"brown"]
如果设置某个值的索引超过了数组现有项数,如这个例子中的 colors[5]所示,数组就会自动增加到该索引值加 1 的长度。并且未被设置的索引对应的值为undefined。
数组的 length 属性
数组的 length 属性很有特点——它不是只读的。因此,通过设置这个属性,可以从数组的末尾移除项或向数组中添加新项。
var colors = ["red", "blue", "green"]; // 创建一个包含 3 个字符串的数组
colors.length = 2;
alert(colors[2]); //undefined
上述例子中,将其 length 属性设置为 2 会移除最后一项。如果将其 length 属性设置为大于数组项数的值,则新增的每一项都会取得 undefined 值,如下:
var colors = ["red", "blue", "green"]; // 创建一个包含 3 个字符串的数组
colors.length = 4;
alert(colors[3]); //undefined
利用 length 属性也可以方便地在数组末尾添加新项,如下所示:
var colors = ["red", "blue", "green"]; // 创建一个包含 3 个字符串的数组
colors[colors.length] = "black"; //(在位置 3)添加一种颜色
colors[colors.length] = "brown"; //(在位置 4)再添加一种颜色
5.2.1 检测数组
对于一个网页,或者一个全局作用域而言,使用 instanceof 操作符就能得到满意的结果:
if (value instanceof Array){
//对数组执行某些操作
}
instanceof 操作符的问题在于,它假定只有一个全局执行环境。如果网页中包含多个框架,那实 际上就存在两个以上不同的全局执行环境,从而存在两个以上不同版本的 Array 构造函数。如果你从 一个框架向另一个框架传入一个数组,那么传入的数组与在第二个框架中原生创建的数组分别具有各自 不同的构造函数。
Array.isArray()
这个方法的目的是最终确定某个值到底是不是数组,而不管它是在哪个全局执行环境中创建的。
if (Array.isArray(value)){
//对数组执行某些操作
}
5.2.2 转换方法
所有对象都具有 toLocaleString()、toString()和 valueOf()方法。其中,调用数组的 toString()方法会返回由数组中每个值的字符串形式拼接而成的一个以逗号分隔的字符串。而调用 valueOf()返回的还是数组。实际上,为了创建这个字符串会调用数组每一项的 toString()方法。来看下面这个例子。
var colors = ["red", "blue", "green"]; // 创建一个包含 3 个字符串的数组
console.log(colors.toString()); // red,blue,green
console.log(colors.valueOf()); // ["red", "blue", "green"]
console.log(colors); // red,blue,green
join()方法
join()方法只接收一个参数,即用作分隔符的字符串,然后返回包含所有数组项的字符串。如果不传,默认以","分隔。
var colors = ["red", "green", "blue"];
alert(colors.join(",")); //red,green,blue
alert(colors.join("||")); //red||green||blue
5.2.3 栈方法
栈是一种 LIFO(Last-In-First-Out,后进先出)的数据结构,也就是最新添加的项最早被移除。ECMAScript 为数组专门提供了 push()和 pop()方法,以便实现类似栈的行为。
push()
- 可选,可以接收任意数量的参数
- 把它们逐个添加到数组末尾
- 返回修改后数组的长度
- 会改变原数组
pop()
- 没有参数
- 从数组末尾移除最后一项,减少数组的 length 值
- 返回移除的项,如果数组为空,则返回undefined
- 会改变原数组
5.2.4 队列方法
队列数据结构的访问规则是 FIFO(First-In-First-Out,先进先出)结合使用 shift()和 push()方法,可以像使 用队列一样使用数组。
shift()
- 没有参数
- 移除数组第0项
- 返回被移除的元素,如果数组为空,则返回undefined
- 会改变原数组
5.2.5 重排序方法
reverse()
- 没有参数
- 反转数组中元素的位置
- 返回该数组的引用
- 会改变原数组
sort()
- 可选,参数compareFn,
- 不传参数,该方法会调用每个数组项的 toString()转型方法,然后比较得到的字符串,以确定如何排序
- compareFn(a, b),定义排序顺序的函数。返回值应该是一个数字,其正负性表示两个元素的相对顺序
- 返回经过排序的原始数组的引用
- 会改变原数组
要比较数字而非字符串,比较函数可以简单的用 a 减 b,如下的函数将会将数组升序排列(如果它不包含 Infinity 和 NaN):
function compareNumbers(a, b) {
return a - b;
}
5.2.6 操作方法
concat()
语法
concat()
concat(value0)
concat(value0, value1)
concat(value0, value1, /* … ,*/ valueN)
- 可选,valueN。如果省略了所有
valueN参数,则concat会返回调用此方法的现存数组的一个浅拷贝。 - 返回新的数组
- 不会改变原数组
slice()
语法
slice()
slice(start)
slice(start, end)
- start: 可选。
- 如果索引是负数,则从数组末尾开始计算——如果
start < 0,则使用start + array.length。 - 如果
start < -array.length或者省略了start,则使用0。 - 如果
start >= array.length,则不提取任何元素。
- 如果索引是负数,则从数组末尾开始计算——如果
- end:可选。
- 如果索引是负数,则从数组末尾开始计算——如果
end < 0,则使用end + array.length。 - 如果
end < -array.length,则使用0。 - 如果
end >= array.length或者省略了end,则使用array.length,提取所有元素直到末尾。 - 如果
end在规范化后小于或等于start,则不提取任何元素。
- 如果索引是负数,则从数组末尾开始计算——如果
- 返回一个含有被提取元素的新数组。
- 不改变原数组
splice()
语法
splice(start)
splice(start, deleteCount)
splice(start, deleteCount, item1)
splice(start, deleteCount, item1, item2, itemN)
-
start-
从 0 开始计算的索引,表示要开始改变数组的位置,它会被转换成整数。
-
负索引从数组末尾开始计算——如果
start < 0,使用start + array.length。 -
如果
start < -array.length,使用0。 -
如果
start >= array.length,则不会删除任何元素,但是该方法会表现为添加元素的函数,添加所提供的那些元素。 -
如果
start被省略了(即调用splice()时不传递参数),则不会删除任何元素。这与传递undefined不同,后者会被转换为0。
-
-
deleteCount可选-
一个整数,表示数组中要从
start开始删除的元素数量。 -
如果省略了
deleteCount,或者其值大于或等于由start指定的位置到数组末尾的元素数量,那么从start到数组末尾的所有元素将被删除。 -
如果你想要传递任何
itemN参数,则应向deleteCount传递Infinity值,以删除start之后的所有元素,因为显式的undefined会转换为0。 -
如果
deleteCount是0或者负数,则不会移除任何元素。在这种情况下,你应该至少指定一个新元素。
-
-
item1, …,itemN可选-
从
start开始要加入到数组中的元素。 -
如果不指定任何元素,
splice()将只从数组中删除元素。
-
-
返回一个数组,该数组中包含从原数组中删除的项,如果没有删除任何项,则为空数组。
var colors = ["red", "green", "blue"];
var removed = colors.splice(0,1); // 删除第一项
alert(colors); // green,blue
alert(removed); // red,返回的数组中只包含一项
removed = colors.splice(1, 0, "yellow", "orange"); // 从位置 1 开始插入两项
alert(colors); // green,yellow,orange,blue
alert(removed); // 返回的是一个空数组
removed = colors.splice(1, 1, "red", "purple"); // 插入两项,删除一项
alert(colors); // green,red,purple,orange,blue
alert(removed); // yellow,返回的数组中只包含一项
5.2.7 位置方法
indexOf()和 lastIndexOf()
indexOf()方法从数组的开头(位置 0)开始向后查找,lastIndexOf()方法则从数组的末尾开始向前查找。
indexOf(searchElement)
indexOf(searchElement, fromIndex)
lastIndexOf(searchElement)
lastIndexOf(searchElement, fromIndex)
- searchElement,必选。表示数组中要查找的元素。
- fromIndex,可选。开始搜索的索引
- 返回首个被找到的元素在数组中的索引位置; 若没有找到则返回 -1。
5.2.8 迭代方法
ECMAScript 5 为数组定义了 5 个迭代方法。每个方法都接收两个参数:要在每一项上运行的函数和(可选的)运行该函数的作用域对象——影响 this 的值。传入这些方法中的函数会接收三个参数:数组项的值、该项在数组中的位置和数组对象本身。
- every():对数组中的每一项运行给定函数,如果该函数对每一项都返回 true,则返回 true。
- filter():对数组中的每一项运行给定函数,返回该函数会返回 true 的项组成的数组。
- forEach():对数组中的每一项运行给定函数。这个方法没有返回值。
- map():对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组。
- some():对数组中的每一项运行给定函数,如果该函数对任一项返回 true,则返回 true。
以上方法都不会修改数组中的包含的值
5.2.9 归并方法
ECMAScript 5 还新增了两个归并数组的方法:reduce()和 reduceRight()。这两个方法都会迭代数组的所有项,然后构建一个最终返回的值。
reduce()
语法
reduce(callbackFn)
reduce(callbackFn, initialValue)
callbackFn(accumulator,currentValue,currentIndex,array)
-
accumulator表示上一次调用callbackFn的结果。在第一次调用时,如果指定了initialValue则为指定的值,否则为array[0]的值。 -
currentValue表示当前元素的值。在第一次调用时,如果指定了initialValue,则为array[0]的值,否则为array[1]。 -
currentIndex表示 currentValue 在数组中的索引位置。在第一次调用时,如果指定了initialValue则为0,否则为1。 -
array调用了reduce()的数组本身。
initialValue,可选。
- 第一次调用回调时初始化
accumulator的值。
5.3 Date 类型
ECMAScript 中的 Date 类型是在早期 Java 中的 java.util.Date 类基础上构建的。
要创建一个日期对象,使用 new 操作符和 Date 构造函数即可,如下所示:
var now = new Date();
Date类型有3个非实例的方法,Date.parse()、Date.now()、Date.UTC()
Date.parse()
语法
Date.parse(dateString)
- dateString,一个符合 RFC2822 或 ISO 8601 日期格式的字符串(其他格式也许也支持,但结果可能与预期不符)。
- 返回一个表示从 1970-1-1 00:00:00 UTC 到给定日期字符串所表示时间的毫秒数的数值。如果参数不能解析为一个有效的日期,则返回NaN
Date.now()
语法
var timeInMs = Date.now();
- 没有参数
- 返回一个 数字,表示自 UNIX 纪元开始(1970 年 1 月 1 日 00:00:00 (UTC))到当前时间的毫秒数。
Date.UTC() 语法
Date.UTC(year)
Date.UTC(year, month)
Date.UTC(year, month, day)
Date.UTC(year, month, day, hour)
Date.UTC(year, month, day, hour, minute)
Date.UTC(year, month, day, hour, minute, second)
Date.UTC(year, month, day, hour, minute, second, millisecond)
- 返回一个数字,表示从 1970 年 1 月 1 日 00:00:00 UTC 到给定时间的毫秒数。
5.3.1 继承的方法
Date 类型重写了 toLocaleString()、toString()和 valueOf()方法
Date 类型的 toLocaleString()方法会按照与浏览器设置的地区相适应的格式返回日期和时间。
Date 类型的toString()方法则通常返回带有时区信息的日期和时间,其中时间一般以军用时间(即小时的范围是 0 到 23)表示。
Date 类型的 valueOf()方法,则根本不返回字符串,而是返回日期的毫秒表示。
5.3.2 日期格式化方法
Date 类型还有一些专门用于将日期格式化为字符串的方法,这些方法如下:
toDateString()——以特定于实现的格式显示星期几、月、日和年;
toTimeString()——以特定于实现的格式显示时、分、秒和时区;
toLocaleDateString()——以特定于地区的格式显示星期几、月、日和年;
toLocaleTimeString()——以特定于实现的格式显示时、分、秒;
toUTCString()——以特定于实现的格式完整的 UTC 日期。
日期的格式化我们一般会选择一些函数库,如moment.js、date-fns等
5.3.3 日期/时间组件方法
以下都是Date实例的方法。
getTime() 返回表示日期的毫秒数;与valueOf()方法返回的值相同
setTime(毫秒) 以毫秒数设置日期,会改变整个日期
getFullYear() 取得4位数的年份(如2007而非仅07)
getUTCFullYear() 返回UTC日期的4位数年份
setFullYear(年) 设置日期的年份。传入的年份值必须是4位数字(如2007而非仅07)
setUTCFullYear(年) 设置UTC日期的年份。传入的年份值必须是4位数字(如2007而非仅07)
getMonth() 返回日期中的月份,其中0表示一月,11表示十二月
getUTCMonth() 返回UTC日期中的月份,其中0表示一月,11表示十二月
setMonth(月) 设置日期的月份。传入的月份值必须大于0,超过11则增加年份
setUTCMonth(月) 设置UTC日期的月份。传入的月份值必须大于0,超过11则增加年份
getDate() 返回日期月份中的天数(1到31)
getUTCDate() 返回UTC日期月份中的天数(1到31)
setDate(日) 设置日期月份中的天数。如果传入的值超过了该月中应有的天数,则增加月份
setUTCDate(日) 设置UTC日期月份中的天数。如果传入的值超过了该月中应有的天数,则增加月份
getDay() 返回日期中星期的星期几(其中0表示星期日,6表示星期六)
getUTCDay() 返回UTC日期中星期的星期几(其中0表示星期日,6表示星期六)
getHours() 返回日期中的小时数(0到23)
getUTCHours() 返回UTC日期中的小时数(0到23)
setHours(时) 设置日期中的小时数。传入的值超过了23则增加月份中的天数
setUTCHours(时) 设置UTC日期中的小时数。传入的值超过了23则增加月份中的天数
getMinutes() 返回日期中的分钟数(0到59)
getUTCMinutes() 返回UTC日期中的分钟数(0到59)
setMinutes(分) 设置日期中的分钟数。传入的值超过59则增加小时数
setUTCMinutes(分) 设置UTC日期中的分钟数。传入的值超过59则增加小时数
getSeconds() 返回日期中的秒数(0到59)
getUTCSeconds() 返回UTC日期中的秒数(0到59)
setSeconds(秒) 设置日期中的秒数。传入的值超过了59会增加分钟数
setUTCSeconds(秒) 设置UTC日期中的秒数。传入的值超过了59会增加分钟数
getMilliseconds() 返回日期中的毫秒数
getUTCMilliseconds() 返回UTC日期中的毫秒数
setMilliseconds(毫秒) 设置日期中的毫秒数
setUTCMilliseconds(毫秒) 设置UTC日期中的毫秒数
getTimezoneOffset() 返回本地时间与UTC时间相差的分钟数。例如,美国东部标准时间返回300。
在某地进入夏令时的情况下,这个值会有所变化
5.4 RegExp 类型
ECMAScript 通过 RegExp 类型来支持正则表达式。
var expression = / pattern / flags ;
其中的模式(pattern)部分可以是任何简单或复杂的正则表达式,可以包含字符类、限定符、分组、 向前查找以及反向引用。每个正则表达式都可带有一或多个标志(flags),用以标明正则表达式的行为。 正则表达式的匹配模式支持下列 3 个标志。
- g:表示全局(global)模式,即模式将被应用于所有字符串,而非在发现第一个匹配项时立即停止;
- i:表示不区分大小写(case-insensitive)模式,即在确定匹配项时忽略模式与字符串的大小写;
- m:表示多行(multiline)模式,即在到达一行文本末尾时还会继续查找下一行中是否存在与模式匹配的项。
因此,一个正则表达式就是一个模式与上述 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");
5.4.1 RegExp实例属性
RegExp 的每个实例都具有下列属性,通过这些属性可以取得有关模式的各种信息。
- global:布尔值,表示是否设置了 g 标志。
- ignoreCase:布尔值,表示是否设置了 i 标志。
- multiline:布尔值,表示是否设置了 m 标志。
- lastIndex:整数,表示开始搜索下一个匹配项的字符位置,从 0 算起。
- source:正则表达式的字符串表示,按照字面量形式而非传入构造函数中的字符串模式返回。
通过这些属性可以获知一个正则表达式的各方面信息,但却没有多大用处,因为这些信息全都包含在模式声明中。
5.4.2 RegExp实例方法
exec()
语法
regexObj.exec(str)
- str表示要匹配正则表达式的字符串。
- 如果匹配失败,exec() 方法返回 null并将正则表达式的 重置为 0。
- 如果匹配成功,exec() 方法返回一个数组,并更新正则表达式对象的 lastIndex 属性。完全匹配成功的文本将作为返回数组的第一项,从第二项起,后续每项都对应一个匹配的捕获组。
- 返回的数组具有以下属性:
- index:匹配到的字符位于原始字符串的基于 0 的索引值。
- input:匹配的原始字符串。
- groups:一个命名捕获组对象,其键是名称,值是捕获组。若没有定义命名捕获组,则
groups的值为 undefined
对于 exec()方法而言,即使在模式中设置了全局标志(g),它每次也只会返回一个匹配项。在不设置全局标志的情况下,在同一个字符串上多次调用 exec()将始终返回第一个匹配项的信息。而在设置全局标志的情况下,每次调用 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()
语法
regexObj.test(str)
- str表示要匹配正则表达式的字符串
- 返回一个布尔值,表示匹配成功或失败。
RegExp 实例继承的 toLocaleString()和 toString()方法都会返回正则表达式的字面量,与创建正则表达式的方式无关。
正则表达式的 valueOf()方法返回正则表达式本身。
5.4.3 RegExp构造函数属性
ECMAScript5定义的一些构造函数属性,目前mdn已经不建议使用了。
5.4.4 模式的局限性
ECMAScript 中的正则表达式功能还是比较完备的,但仍然缺少某些语言(特别是 Perl)所支 持的高级正则表达式特性。我们只要关注ECMAScript所支持的功能即可,不用关于不支持的特性。
5.5 Function 类型
函数实际上是对象。每个函数都是 Function 类型的实例,而且都与其他引用类型一样具有属性和方法。由于函数是对象,因此函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定。
5.5.1 没有重载(深入理解)
将函数名想象为指针,也有助于理解为什么 ECMAScript 中没有函数重载的概念。
function addSomeNumber(num){
return num + 100;
}
function addSomeNumber(num) {
return num + 200;
}
var result = addSomeNumber(100); //300
这个例子中声明了两个同名函数,而结果则是后面的函数覆盖了前面的函数。以上代码实际 上与下面的代码没有什么区别。
var addSomeNumber = function (num){
return num + 100;
};
addSomeNumber = function (num) {
return num + 200;
};
var result = addSomeNumber(100); //300
通过观察重写之后的代码,很容易看清楚到底是怎么回事儿——在创建第二个函数时,实际上覆盖 了引用第一个函数的变量 addSomeNumber。
5.5.2 函数声明与函数表达式
解析器在向执行环境中加载数据时,对函数声明和函数表达式并非一视同仁。 解析器会率先读取函数声明,并使其在执行任何代码之前可用(可以访问);至于函数表达式,则必须等到解析器执行到它所在的代码行,才会真正被解释执行。
function sum(num1, num2){
return num1 + num2;
}
alert(sum(10,10));
上述代码相当于:
alert(sum(10,10));
function sum(num1, num2){
return num1 + num2;
}
5.5.3 作为值的函数
因为 ECMAScript 中的函数名本身就是变量,所以函数也可以作为值来使用。也就是说,不仅可以像传递参数一样把一个函数传递给另一个函数,而且可以将一个函数作为另一个函数的结果返回。
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"
5.5.4 函数内部属性
在函数内部,有两个特殊的对象:arguments 和 this。
arguments是一个类数组对象,包含着传入函数中的所有参数。它身上还有一个属性callee ,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)
}
}
函数内部的另一个特殊对象是 this,其行为与 Java 和 C#中的 this 大致类似。
this引用的是函数据以执行的环境对象——或者也可以说是 this 值。
caller
ECMAScript 5 也规范化了另一个函数对象的属性caller,这个属性着调用当前函数的函数的引用,如果是在全局作用域中调用当前函数,它的值为 null。
该属性的常用形式 arguments.callee.caller 替代了被废弃的 arguments.caller.
5.5.5 函数属性和方法
ECMAScript 中的函数是对象,因此函数也有属性和方法。每个函数都包含两个属性: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 属性
对于ECMAScript 中的引用类型而言,prototype 是保存它们所有实例方法的真正所在。诸如 toString()和 valueOf()等方法实际上都保存在 prototype 名下,只不过是通过各自对象的实例访问罢了。在构造函数Function的prototype上定义了apply()和 call()以及bind()。
apply()
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()
call()方法与 apply()方法的作用相同,它们的区别仅在于接收参数的方式不同。第一个参数是 this 值没有变化,变化的是其余参数都直接传递给函数。在使用call()方法时,传递给函数的参数必须逐个列举出来。
function sum(num1, num2){
return num1 + num2;
}
function callSum(num1, num2){
return sum.call(this, num1, num2);
}
alert(callSum(10,10)); //20
bind()
bind()方法的使用跟call()一样,只是该方法返回的是改变this之后的函数。
5.6 基本包装类型
ECMAScript 还提供了 3 个特殊的引用类型:Boolean、Number 和String。这些类型与本章介绍的其他引用类型相似,但同时也具有与各自的基本类型相应的特殊行为。实际上,每当读取一个基本类型值的时候,后台就会创建一个对应的基本包装类型的对象,从而让我们能够调用一些方法来操作这些数据。来看下面的例子。
var s1 = "some text";
var s2 = s1.substring(2);
读取模式中访问字符串时,后台都会自动完成下列处理。
- 创建 String 类型的一个实例;
- 在实例上调用指定的方法;
- 销毁这个实例
自动创建的基本包装类型的对象,则只存在于一行代码的执行瞬间,然后立即被销毁。
5.6.1 Boolean类型
Boolean 类型是与布尔值对应的引用类型。创建 Boolean 对象,可以像下面这样调用 Boolean 构造函数并传入 true 或 false 值。
var booleanObject = new Boolean(true);
基本类型与引用类型的布尔值还有两个区别。首先,typeof 操作符对基本类型返回"boolean", 而对引用类型返回"object"。其次,由于 Boolean 对象是 Boolean 类型的实例,所以使用 instanceof操作符测试 Boolean 对象会返回 true,而测试基本类型的布尔值则返回 false
var falseObject = new Boolean(false);
var result = falseObject && true;
alert(result); //true
var falseValue = false;
result = falseValue && true;
alert(result); //false
alert(typeof falseObject); //object
alert(typeof falseValue); //boolean
alert(falseObject instanceof Boolean); //true
alert(falseValue instanceof Boolean); //false
5.6.2 Number类型
Number 是与数字值对应的引用类型。要创建 Number 对象,可以在调用 Number 构造函数时向其中传递相应的数值。
var numberObject = new Number(10);
Number 类型也重写了 valueOf()、toLocaleString()和 toString()方法。重写后的 valueOf()方法返回对象表示的基本类型的数值,另外两个方法则返回字符串形式的数值。
toFixed()
按照指定的小数位返回数值的字符串表示
var num = 10;
alert(num.toFixed(2)); //"10.00"
toExponential()
该方法返回以指数表示法(也称 e 表示法)表示的数值的字符串形式。
var num = 10;
alert(num.toExponential(1)); //"1.0e+1"
与 Boolean 对象类似,typeof 操作符对基本类型返回"number",而对引用类型返回"object"。
5.6.3 String类型
String 类型是字符串的对象包装类型,可以像下面这样使用 String 构造函数来创建。
var stringObject = new String("hello world");
继承的 valueOf()、toLocaleString()和 toString()方法,都返回对象所表示的基本字符串值。
1. 字符方法
- charAt()方法以单字符字符串的形式返回给定位置的那个字符
- charCodeAt()方法返回字符编码
- 用下标访问个别字符
2. 字符串操作方法
- concat(),用于将一或多个字符串拼接起来,返回拼接得到的新字符串。
- slice()、substr()和 substring()
3. 字符串位置方法
- indexOf()
- lastIndexOf()
4. trim()方法
这个方法会创建一个字符串的副本,删除前置及后缀的所有空格,然后返回结果。
5. 字符串大小写转换方法
- toLowerCase()
- toLocaleLowerCase()
- toUpperCase()
- toLocaleUpperCase()
6. 字符串的模式匹配方法
match(),只接受一个参数,要么是一个正则表达式,要么是一个 RegExp 对象。match()方法返回了一个数组;
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
search(),只接受一个参数,要么是一个正则表达式,要么是一个 RegExp 对象。search()方法返回字符串中第一个匹配项的索引;如果没有找到匹配项,则返回-1。而且,search()方法始终是从字符串开头向后查找模式。
replace(),第一个参数是正则表达式或 RegExp 对象;第二个参数是字符串,同时可以使用一些特殊的字符串序列,将正则表达式操作得到的值插入到结果字符串中。
$$
就是$符号
$&
匹配整个模式的子字符串。与RegExp.lastMatch的值相同
$'
匹配的子字符串之前的子字符串。与RegExp.leftContext的值相同
$`
匹配的子字符串之后的子字符串。与RegExp.rightContext的值相同
$n
匹配第n个捕获组的子字符串,其中n等于0~9。例如,$1是匹配第一个捕获组的子字符串,$2是匹配第二个捕获组的子字符串,以此类推。如果正则表达式中没有定义捕获组,则使用空字符串
$nn
匹配第nn个捕获组的子字符串,其中nn等于01~99。例如,$01是匹配第一个捕获组的子字符串,$02是匹配第二个捕获组的子字符串,以此类推。如果正则表达式中没有定义捕获组,则使用空字符串
通过这些特殊的字符序列,可以使用最近一次匹配结果中的内容
var text = "cat, bat, sat, fat";
result = text.replace(/(.at)/g, "word ($1)");
alert(result); //word (cat), word (bat), word (sat), word (fat)
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,具体的 值同样要视实现而定)。
8. fromCharCode()方法
这是String 构造函数本身的静态方法。这个方法的任务是接收一或 多个字符编码,然后将它们转换成一个字符串。从本质上来看,这个方法与实例方法 charCodeAt()执行的是相反的操作。来看一个例子:
alert(String.fromCharCode(104, 101, 108, 108, 111)); //"hello"
5.7 单体内置对象
ECMA-262 对内置对象的定义是:“由 ECMAScript 实现提供的、不依赖于宿主环境的对象,这些对象在 ECMAScript 程序执行之前就已经存在了。”
- Object
- Array
- String
- Global
- Math
5.7.1 Global对象
事实上,没有全局变量或全局函数;所有在全局作用域中定义的属性和函数,都是 Global 对象的属性。本书前面介绍过的那些函数,诸如 isNaN()、isFinite()、parseInt()以及 parseFloat(),实际上全都是 Global对象的方法。除此之外,Global 对象还包含其他一些方法。
1. URI 编码方法
Global 对象的 encodeURI()和 encodeURIComponent()方法可以对 URI(Uniform Resource Identifiers,通用资源标识符)进行编码,以便发送给浏览器。
encodeURI()编码后的结果是除了空格之外的其他字符都原封不动,只有空格被替换成了%20
encodeURIComponent()方法则会使用对应的编码替换所有非字母数字字符。
encodeURI()和 encodeURIComponent()方法对应的两个方法分别是 decodeURI()和
decodeURIComponent()。其中,decodeURI()只能对使用 encodeURI()替换的字符进行解码
2. eval()方法
eval()
方法就像是一个完整的 ECMAScript 解析器,它只接受一个参数,即要执行的 ECMAScript(或 JavaScript)字符串。
eval("alert('hi')");
// 这行代码的作用等价于下面这行代码:
alert("hi");
当解析器发现代码中调用 eval()方法时,它会将传入的参数当作实际的 ECMAScript 语句来解析,然后把执行结果插入到原位置。
3. Global 对象的属性
所有原生引用类型的构造函数,像Object 和 Function,也都是 Global 对象的属性。
ECMAScript 5 明确禁止给 undefined、NaN 和 Infinity 赋值,这样做即使在非严格模式下也会导致错误。
4. window 对象
ECMAScript 虽然没有指出如何直接访问 Global 对象,但 Web 浏览器都是将这个全局对象作为
window 对象的一部分加以实现的。因此,在全局作用域中声明的所有变量和函数,就都成为了 window对象的属性。也就是说window对象包含了Global对象的实现。
5.7.2 Math对象
1. Math 对象的属性
Math 对象包含的属性大都是数学计算中可能会用到的一些特殊值
2. min()和 max()方法
min()和 max()方法用于确定一组数值中的最小值和最大值。这两个方法都可以接收任意多个数值参数
var max = Math.max(3, 54, 32, 16);
alert(max); //54
var min = Math.min(3, 54, 32, 16);
alert(min); //3
3. 舍入方法
-
Math.ceil()执行向上舍入,即它总是将数值向上舍入为最接近的整数;
-
Math.floor()执行向下舍入,即它总是将数值向下舍入为最接近的整数;
-
Math.round()执行标准舍入,即它总是将数值四舍五入为最接近的整数(这也是我们在数学课上学到的舍入规则)。
4. random()方法
Math.random()方法返回大于等于 0 小于 1 的一个随机数。
获取指定范围的任意随机数:值 = Math.floor(Math.random() * 可能值的总数 + 第一个可能的值)