5 引用类型

本章内容

  • 使用对象
  • 创建并操作数组
  • 理解基本的 JavaScript 类型
  • 使用基本类型和基本包装类型

引用类型的值(对象)是引用类型的一个实例。

虽然引用类型与类看起来相似,但它们并不是相同的概念。为避免混淆,本书将不使用类这个概念。

新对象是使用new操作符跟一个构造函数来创建的。

var person = new Object();

这行代码创建了Object引用类型的一个新实例,然后把该实例保存在了变量person中。使用的构造函数是Object,它只为新对象定义了默认的属性和方法。

5.1 Object 类型

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

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

另一种是使用对象字面量表示法。

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

在使用对象字面量语法时,属性名也可以使用字符串。

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

这个例子会创建一个对象,包含三个属性:nameage5。但这里的数值属性会自动转换为字符串。
另外,使用对象字面语法时,如果留空其花括号,则可以定义只包含默认属性和方法的对象。

var person = {};  //与new Object()相同
person.name = "Nicholas";
person.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
});

displayInfo({
  name: "Greg"
});

这种传递参数的模式最适合需要向函数传入大量可选参数的情形。

一般来说,访问对象属性时使用的都是点表示法,这也是很多面向对象语言中通用的语法。在JavaScript也可以使用方括号表示来访问对象的属性。使用方括号语法时,应该将要访问的属性以字符串形式放在方括号中。

alert(person["name"]);  //"Nicholas"
alert(person.name);  //"Nicholas"

从功能上看,这两种访问对象属性的方法没有任何区别。但方括号语法的主要优点是可以通过变量来访问属性。

var propertyName = "name";
alert(person[propertyName]);  //"Nicholas"

通常,除非必须使用变量来访问属性,否则我们建议使用点表示法。

5.2 Array 类型

ECMAScript 中的数组与其他多数语言中的数组有着相当大的区别。虽然 ECMAScript 数组与其他语言中的数组都是数据的有序列表,但与其他语言不同的是,ECMAScript 数组的每一项可以保存任何类型的数据。而且,ECMAScript 数组的大小是可以动态调整的,即可以随着数据的添加自动增长以容纳新增数据。
创建数组的基本方式有两种。第一种是使用Array构造函数。

var colors = new Array();

如果预先知道数组要保存的项目数量,也可以给构造函数传递该数量,而该数量会自动变成length属性的值。

var colors = new Array(20);

也可以向Array构造函数传递数组中应该包含的项。

var colors = new Array("red", "blue", "green");

传递一个值也可以创建数组。

var colors = new Array(3);  //创建一个包含3项的数组
var names = new Array("Greg");  //创建一个包含1项,即字符串“Greg”的数组

创建数组的第二种基本方式是使用数组字面量表示法。

var colors = ["red", "blue", "green"];
var names = [];

与对象一样,在使用数组字面量表示法时,也不会调用Array构造函数。

var colors = ["red", "blue", "green"];
alert(colors[0]);
colors[2] = "black";  //修改第三项
colors[3] = "brown";  //新增第四项

数组的项数保存在其length属性中,这个属性始终返回0或更大的值。

var colors = ["red", "blue", "green"];  
var names = [];
alert(colors.length);  //3
alert(names.length);  //0

数组的length属性不是只读的。通过设置这个属性,可以从数组的末尾移除或向数组中添加新项。

var colors = ["red", "blue", "green"];
colors.length = 2;
alert(colors[2]);  //undefined

利用length属性也可以方便地在数组末尾添加新项。

var colors = ["red", "blue", "green"];
colors[colors.length] = "black";
colors[colors.length] = "brown";

当把一个值放在超出当前数组大小的位置上时,数组就会重新计算其长度值,即长度值等于最后一项的索引加 1。

var colors = ["red", "blue", "green"];
colors[99] = "black";
alert(colors.length);  //100

数组位置 3 到位置 98 实际上都是不存在的,所以访问它们都将返回undefined

数组最多可以包含 4294967295 个项,这几乎已经能够满足任何编程需求了。

5.2.1 检测数组

ECMAScript 5 新增了Array.isArray()方法。这个方法的目的是最终确定某个值到底是不是数组,而不管它是在哪个全局执行环境中创建的。

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

5.2.2 转换方法

所有方法都具有toLocalString()toString()valueOf()方法。其中,调用数组的toString()方法会返回由数组中每个值得字符串形式拼接而成的一个以逗号分隔的字符串。而调用valueOf()返回的还是数组。实际上,为了创建这个字符串会调用数组每一项的toString()方法。

var colors = ["red", "blur", "green"];
alert(colors.toString());  //red,blue,green
alert(colors.valueOf());  //red,blue,green
alert(colors);  //red,blue,green

最后一行代码直接将数组传递给了alert()。由于alert()要接收字符串参数,所以它会在后台调用toString()方法,由此会得到与直接调用toString()方法相同的结果。

另外,toLocaleString()方法经常也会返回前两个方法相同的值。与前两个方法唯一不同之处在于,这一次为了取得每一项的值,调用的是每一项的toLocaleString()方法,而不是toString()方法。

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()方法,则可以使用不同的分隔符来构建这个字符串。join()方法只接收一个参数,即用作分隔符的字符串,然后返回包含所有数组项的字符串。如果不给join()方法传入任何值,或者给它传入undefined,则使用逗号作为分隔符。

var colors = ["red", "green", "blue"];
alert(colors.join(","));  //red,green,blue
alert(colors.join("||"));  //red||green||blue

如果数组中的某一项的值是null或者undefined,那么该值在join()toLocaleString()toString()valueOf()方法返回的结果以空白字符串表示。

5.2.3 栈方法

ECMAScript 为数组专门提供了push()pop()方法,以便实现类似栈的行为。
push()方法可以接收任意数量的参数,把它们逐个添加到数组末尾,并返回修改后数组的长度。而pop()方法则从数组末尾移除最后一项,减少数组的length值,然后返回移除的项。

var colors = new Array();
var count = colors.push("red", "green");
alert(count);  //2
count = colors.push("black");
alert(count);
var item = colors.pop();
alert(item);  //"black"
alert(colors.length);  //2

5.2.4 队列方法

栈数据结构的访问规则是LIFO(后进先出),而队列数据结构的访问规则是FIFO(先进先出)。shift()能够移除数组中的第一个项并返回该项,同时将数组长度减1。结合使用shift()push()方法,可以像使用队列一样使用数组。

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

ECMAScript还为数组提供了一个unshift()方法。它能在数组前端添加任意个项并返回新数组的长度。因此,同时使用unshift()pop()方法,可以从相反的方向来模拟队列,即在数组的前端添加项,从数组末端移除项。

var colors = new Array();
var count = colors.unshift("red", "green");
alert(count);  //2
count = colors.unshift("black");
alert(count);  //3
var item = colors.pop();
alert(item);  //"green"
alert(colors.length);  //2

IE7及更早版本中其unshift()方法总是返回undefined值。

5.2.5 重排序方法

数组中已经存在两个可以直接用来重排序的方法:reverse()sort()

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

在默认情况下,sort()方法按升序排列数组项。sort()方法会调用每个数组项的toString()转型方法,然后比较得到的字符串,以确定如何排序。

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

可见,即使例子中值得顺序没有问题,但sort()方法会根据测试字符串的结果改变原来的顺序。字符串比较时,“10”位于“5”的前面。sort()方法可以接收一个比较函数作为参数,以便我们指定那个值位于那个值的前面。
以下就是一个简单的比较函数:

function compare(value, value) {
  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) {
  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);  //15,10,5,1,0

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

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

5.2.6 操作方法

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

var colors = ["red", "green", "blue"];
var colors2 = 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

如果slice()方法的参数中有一个负数,则用数组长度加上该数来确定相应的位置。例如,在一个包含5项的数组上调用slice(-2,-1)与调用slice(3,4)的到的结果相同。如果结束位置小于起始位置,则返回空数组。

splice()方法的主要用途是向数组的中部插入项,但使用这种方法的方式则有如下3种。

  • 删除: 可以删除任意数量的项,只需指定2个参数:要删除的第一项的位置和要删除的项数。例如,splice(0,2)会删除数组中的前两项。
  • 插入:可以向指定位置插入任意数量的项,只需提供3个参数:起始位置、0(要删除的项数)和要插入的项。如果要插入多个项,可以再传入第四、第五,以至任意多个项。例如,splice(2,0,"red","green")会从当前数组的位置2开始插入字符串“red”和“green”。
  • 替换: 可以向指定位置插入任意数项的项,且同时删除任意数量的项,只需指定3个参数:起始位置、要删除的项数和要插入的任意数量的项。插入的项数不必与删除的项数相等。例如,splice(2,1,"red","green")会删除当前数组位置2的项,然后自从位置2开始插入字符串“red”和“green”。

splice方法始终都会返回一个数组,该数组中包含从原始数组中删除的项(如果没有删除任何项,则返回一个空数组)。下面的代码展示了上述3种使用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,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 位置方法

ECMAScript5 为数组实例添加了两个位置方法: indexOf()lastIndexOf()。这两个方法都接收两个参数:要查找的项和(可选的)表示查找起点位置的索引。indexOf()方法从数组的开头(位置0)开始向后查找。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

var person = {name: "Nicholas"};
var people = [{name: "Nicholas"}];
var morePeople = [person];
alert(people.indexOf(person));  //-1
alert(morePeople.indexOf(person));  //0

5.2.8 迭代方法

ECMAScript5 为数组定义了5个迭代方法。每个方法都接收两个参数:要在每一项上运行的函数和(可选的)运行该函数的作用域对象——影响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) {
  return (item > 2);
});
alert(everyResult);  //false
var someResult = numbers.some(function(item, index, array) {
  return (item > 2);
});
alert(someResult);  //true
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]
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循环迭代数组一样。

var numbers = [1,2,3,4,5,4,3,2,1];
numbers.forEach(function(item, index, array) {
  //执行某些操作
});

5.2.9 缩小方法

ECMAScript 5 新增了俩缩小数组的方法: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
var values = [1,2,3,4,5];
var sum = values.reduceRight(function(prev, cur, index, array) {
  return prev + cur;
});
alert(sum);  //15

5.3 Date 类型

Date类型使用自 UTC 1970 年 1 月 1 日 零时开始经过的毫秒数来保存日期。
使用new操作符和Date构造函数即可。

var now = new Date();

如果想根据特定的日期和时间创建日期对象,必须传入表示该日期的毫秒数。为了简化计算过程,提供了俩方法:Date.parse()Date.UTC()
Date.parse()方法接收一个表示日期的字符串参数,然后尝试根据这个字符串返回相应日期的毫秒数。
如果传入的字符串不能表示日期,那么它会返回NaN。实际上,如果直接将表示日期的字符串传递给Date构造函数,也会在后台调用Date.parse()

var someDate = new Date("May 25, 2004");

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

var y2k = new Date(Date.UTC(2000, 0));

var allFives = new Date(Date.UTC(2005, 4, 5, 17, 55, 55));

Date构造函数也会模仿Date.UTC(),但有一点明显不同:日期和时间都基于本地时区而非GMT来创建。
ECMAScript 5 添加了Date.now()方法,返回表示调用这个方法时的日期和时间的毫秒数。这个方法简化了使用Date对象分析代码的工作。

var start = Date.now();

doSomething();

var stop = Date.now(),
result = stop - start;

5.3.1 继承的方法

与其他引用类型一样,Date类型也重写了toLocaleString()toString()valueOf()方法;但这些方法返回的值与其他类型中的方法不同。Date类型的 toLocaleString()方法会按照与浏览器设置的地区相适应的格式返回日期和时间。而toString()方法则通常会返回带有时区信息的日期和时间,其中时间一般以军用时间(0到23)表示。
至于Date类型的valueOf()方法,则根本不返回字符串,而是返回日期的毫秒表示。因此,可以方便使用比较操作符来比较日期值。

var date1 = new Date(2007, 0, 1);
var date2 = new Date(2007, 1, 1);

alert(date1 < date2); //true
alert(date1 > date2);  //false

5.3.2 日期格式化方法

Date类型还有一些专门用于将日期格式化为字符串的方法。

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

以上这些字符串格式方法的输出也是因浏览器而异的,因此没有哪一个方法能够用来在用户界面中显示一致的日期信息。

5.3.3 日期/时间组件方法

方法 说明
getTime() 返回表示日期的毫秒数;与valueOf()方法返回的值相同
setTime(毫秒) 以毫秒数设置日期,会改变整个日期
getFullYear() 取的4位数的年份
getUTCFullYear() 返回UTC日期的4位数年份
getMonth() 返回日期的月份,其中0表示一月
getUTCMonth() 。。。
setMonth(月) 设置日期的月份,必须大于0,超过11增加年份
setUTCMonth(月)
getDate() 返回日期月份中的天数
getUTCDate()
setDate(日) 设置日期月份中的天数。如果传入的值超过了该月中应有的天数,则增加月份
setUTCDate()
getDay() 返回日期中星期的星期几(其中0表示星期日,6表示星期六)
getUTCDay()
getHours() 返回日期中的小时数(0到23)
getUTCHours()
setHours(时) 设置日期中的小时数。传入的值超过了23则增加月份中的天数
setUTCHours(时)
getMinutes() 返回日期中的分钟数
getUTCMinutes()
setMinutes(分) 设置日期中的分钟数。传入的值超过59则增加小时数
setUTCMinutes(分)
getSeconds() 返回日期中的秒数
getUTCSeconds()
setSeconds(秒) 设置日期中的秒数。传入的值超过了59会增加分钟数
setUTCSeconds(秒)
getMilliseconds() 返回日期中的毫秒数
getUTCMilliseconds()
setMilliseconds(毫秒) 设置日期中的毫秒数
setUTCMilliseconds(毫秒)
getTimezoneOffset() 返回本地时间与UTC时间相差的分钟数。

5.4 RegExp 类型

ECMAScript通过RegExp类型来支持正则表达式。

var expression = / pattern / flags ;

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

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

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

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

与其他元字符在正则表达式中都有一或多种特殊用途,因此如果想要匹配字符串中包含的这些字符,就必须对它们进行转义。

另一种创建正则表达式的方式是使用RegExp构造函数,它接收两个参数:一个是要匹配的字符串模式,另一个是可选的标志字符串。

var pattern1 = /[bc]at/i;

var pattern2 = new RegExp("[bc]at", "i");

要注意的是,传递给RegExp构造函数的两个参数都是字符串。在某些情况下要对字符进行双重转义。所有元字符都必须双重转义,那些已经转义的字符也是如此,例如\n(字符\在字符串中通常被转义为\,而在正则表达式字符串中就会变成\\)。下表给出了一些模式。

字面量模式 等价的字符串
/[bc]at/ "\[bc\]at"
/.at/ "\.at"
/name/age/ "name\/age"
/\d.\d{1,2}/ "\d.\d{1,2}"
/\w\hello\123/ "\w\\hello\\123"

使用正则表达式字面量和使用RegExp构造函数创建的表达式不一样。
ECMAScript 5 明确规定,使用正则表达式字面量必须像直接调用RegExp构造函数一样,每次都创建新的RegExp实例。IE9+、Firefox 4+ 和 Chrome 都据此做出了修改。

5.4.1 RegExp 实例属性

RegExp的每个实例都具有下列属性,通过这些属性可以取得有关模式的各种信息。

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

5.4.2 RegExp 实例方法

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

var text = "mom and dad and baby";
var pattern = /mom( and dad( and baby)?)?/gi;

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

对于exec()方法而言,即使在模式中设置了全局标志(g),他每次也只会返回一个一个匹配项。在设置全局标志的情况下,每次调用exec()则都会在字符串中继续查找新匹配项,如下面的例子所示。

正则表达式的第二个方法是test(),它接受一个字符串参数。在模式与该参数匹配的情况下返回true;否则,返回false
RegExp实例继承的toLocaleString()toString()方法都会返回正则表达式的字面量,于创建方式无关。

5.4.3 RegExp 构造函数属性

具体属性略。
还有多达9个用于存储捕获组的构造函数属性。访问这些属性的语法是RegExp.$1、RegExp.$2...RegExp.$9, 分别用于存储第一,第二......第九个匹配的捕获组。在调用exec()test()方法时,这些属性会被自动填充。

var text = "this has been a short summer";
var pattern = /(..)or(.)/g;

if(pattern.test(text)) {
  alert(RegExp.$1);  //sh
  alert(RegExp.$2);  //t
}

5.4.4 模式的局限性

下面列出了 ECMAScript 正则表达式不支持的特性。

  • 匹配字符串开始和结尾的\A 和 \Z (但支持以插入符号^和美元符号$来匹配字符串的开始和结尾)
  • 向后查找(但完全支持向前查找)
  • 并集和交集类
  • 原子组
  • Unicode 支持
  • 命名的捕获组(但支持编号的捕获组)
  • s(单行)和 x(无间隔)匹配模式
  • 条件匹配
  • 正则表达式注释

5.5 Function 类型

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

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

这与下面使用函数表达式定义函数的方式几乎相差无几

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

最后一种定义函数的方式是使用Function构造函数,不推荐。

由于函数名仅仅是指向函数的指针,因此函数名与包含对象指针的其他变量没有什么不同。

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

5.5.1 没有重载(深入理解)

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

5.5.2 函数声明与函数表达式

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

alert(sum(10, 10));
function sum(num1, num2) {
  return num1 + num2;
}

以上代码正常运行。因为在代码执行之前,解析器就已经通过一个名为函数声明提升的过程,读取并将函数声明添加到执行环境中。

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

以上代码在运行期间会产生错误,原因在于函数位于一个初始化语句中,而不是一个函数声明。

作为值的函数

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

function callSomeFunction(someFunction, someArgument) {
  return someFunction(someArgument);
}
function add10(num) {
  return num + 10;
}

var result = callSomeFunction(add10, 10);
alert(result);  //20

当然,可以从一个函数中返回另一个函数,而且这也是极为有用的一种技术。

function createComparisonFunction(propertyName) {
  return function(object1, object2) {
    var value1 = object1[propertyName];
    var value2 = object2[propertyName];
    
    if (value1 < value2) {return -1;} else if (value1 > value2) {return1;} 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

5.5.4 函数内部属性

在函数内容中,有两个特殊的对象:argumentsthis。其中,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

函数内部的另一个特殊对象是this,引用的是函数据以执行的环境对象——或者可以说是this值(挡在网页的全局作用域中调用函数时,this对象引用的就是window)。

window.color = "red";
var o = {color: "blue"};

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

请牢记,函数的名字仅仅是一个包含指针的变量而已。因此,即使是在不同的环境中执行,全局的sayColor()函数与o.sayColor()指向的仍然是同一个函数。

ECMAScript 5 也规范化了另一个函数对象的属性:caller。这个属性中保存着调用当前函数的函数的引用,如果是在全局作用域中调用当前函数,它的值为null

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

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

严格模式下,arguments.callee会导致错误。

5.5.5 函数属性和方法

ECMAScript 中的函数是对象,因此函数也有属性和方法。每个函数包含两个属性:lengthprototype。其中,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

对于ECMAScript 中的引用类型而言,prototype是保存它们所有实例方法的真正所在。换句话说,诸如toString()valueOf()等方法实际上都保持在prototype名下,只不过是通过各自对象的实例访问罢了。在创建自定义引用类型以及实现继承时,prototype属性的作用是极为重要的。
每个函数都包含两个非继承而来的方法:apply()call()。这两个方法的用途都是在特定的作用域中调用函数。实际上等于设置函数体内this对象的值。首先,apply()方法接收两个参数:一个是在其中运行函数的作用域,另一个是参数数组。其中,第二个参数可以是Array的实例,也可以是arguments对象。

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

function callSum1(num1, num2) {
  return sum.call(this, arguments);  //传入arguments 对象
}

function callSum2(num1, num2) {
  return sum.apply(this, [num1, num2]);  //传入参数
}

alert(callSum1(10,10));  //20
alert(callSum2(10,10));  //20

callSum1()在执行sum()函数时传入了this作为this值(因为是在全局作用域中调用的,所以传入的就是window对象)和arguments对象。而callSum2同样也调用了sum()函数,但它传入的则是this和一个参数数组。这两个函数都会正常执行并返回正确的结果。

在严格模式下,未指定环境对象而调用函数,则this值不会转型为window。除非明确把函数添加到某个对象或者调用apply()call(),否则this值将是undefined

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来扩充作用域的最大好处,就是对象不需要与方法有任何耦合关系。
ECMAScript 5 还定义了一个方法:bind()。这个方法会创建一个函数的实例,其this值会被绑定到传给bind()函数的值。

window.color = "red";
var o = {color: "blue"};

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

每个函数继承的toLocaleString()toString()方法始终都返回函数的代码。返回代码的格式则因浏览器而异——有的返回的代码与源代码中的函数代码一样,而有的则返回函数代码的内部表示,即由解析器删除了注释并对某些代码作了改动后的代码。由于存在这些差异,我们无法根据这两个方法返回的结果来实现任何重要功能;不过,这些信息在调试代码时倒是很有用。另外一个继承的valueOf()方法同样也只有返回函数代码。

5.6 基本包装类型

为了便于操作基本类型值,ECMAScript 还提供了 3 个特殊的引用类型:Boolean,NumberString。这些类型与本章介绍的其他引用类型相似,但同时也具有与各自的基本类型相应的特殊行为。实际上,每当读取一个基本类型值的时候,后台就会创建一个对应的基本包装类型的对象,从而让我们能够调用一些方法来操作这些数据。

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

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

  1. 创建String类型的一个实例;
  2. 在实例上调用指定的方法;
  3. 销毁这个实例。

可以将以上三个步骤想象成是执行了下列ECMAScript代码。

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

经过此番处理,基本的字符串值就变得跟对象一样了。而且,上面这三个步骤也分别适用于BooleanNumber类型对应的布尔值和数字值。
引用类型与基本包装类型的主要区别就是对象的生存期。使用new操作符创建的引用类型的实例,在执行流离开当前作用域之前都一直保存在内存中。而自动创建的基本包装类型的对象,则只存在于一行代码的执行瞬间,然后立即销毁。这意味着我们不能在运行时为基本类型添加属性和方法。

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

问题的原因在于第二行创建的String对象在执行第三行代码时已经被销毁了。第三行代码又创建自己的String对象,而该对象没有color属性。
当然,可以显示地调用BooleanNumberString来创建基本包装类型的对象。应该尽量避免这样做,因为这种方法做很容易让人分不清自己是在处理基本类型还是引用类型的值。对基本包装类型的实例调用typeof会返回object,而且所有基本包装类型的对象都会被转换为布尔值true
Object构造函数也会像工厂方法一样,根据传入值的类型返回相应基本包装类型的实例。

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

把字符串传给Object构造函数,就会创建String的实例;其他类似。
要注意的是,使用new调用基本包装类型的构造函数,与直接调用同名的转型函数是不一样的。

var value = "25";
var number = Number(value);  //转型函数
alert(typeof number);  //"number"

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

5.6.1 Boolean 类型

Boolean类型是与布尔值对应的引用类型。

var booleanObject = new Boolean(true);

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

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

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

在布尔运算中,false && true等于false。但是布尔表达式中的所有对象都会被转换为true,因此falseObject对象在布尔表达式中代表的是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

建议永远不要使用Boolean对象

5.6.2 Number 类型

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

var numberObject = new Number(10);

Number类型也重写了valueOf()toLocaleString()toString()方法。重写后的valueOf()方法返回对象表示的基本类型的数值,另外两个方法则返回字符串形式的数值。在第 3 章中介绍过,可以为toString()方法传递一个表示基数的参数,告诉它返回几进制的字符串形式。

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

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

如果数值本身包含的小数位比指定的还多,那么接近的最大小数位的值就会舍入。

var num = 10.005;
alert(num.toFixed(2));  //"10.01"

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

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"

不建议直接实例化Number类型,其他与Boolean对象一样。

5.6.3 String 类型

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

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

String对象的方法也可以在所有基本的字符串值中访问到。其中,继承的valueOf()toLocaleString()toString()方法,都返回对象所表示的基本字符串值。
String类型的每个实例都有一个length属性,表示字符串中包含多个字符。

var stringValue = "hello world";
alert(stringValue.length);  //11

即使字符串中包含双字节字符(不是占一个字节的 ASCII 字符),每个字符也仍然算一个字符。

  1. 字符方法
    两个用于访问字符串中特定字符的方法是:charAt()charCodeAt()。这俩方法都接收一个参数,即基于0的字符位置。其中,charAt()方法以单字符字符串的形式返回给定位置的那个字符。
var stringValue = "hello world";
alert (stringValue.charAt(1));  //"e"

如果你想得到的不是字符而是字符编码。

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

在支持的浏览器中,可以使用方括号加数字索引来访问字符串中的特定字符。

var stringValue = "hello world";
alert(stringValue[1]);  //"e"
  1. 字符串操作方法
    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"

虽然concat()是专门用来拼接字符串的方法,但实践中使用更多的还是加号操作符(+)。
ECMAScript 提供了三个基于子字符串创建新字符串的方法:slice()substr()substring()。这三个方法都会返回被操作字符串的一个子字符串,而且也都接受一或两个参数。第一个参数指定子字符串的开始位置,第二个参数(在指定的情况下)表示子字符串到哪里结束。具体来说,slice()substring()的第二个参数指定的是字符串最后一个字符后面的位置。而substr()的第二个参数指定的则是返回的字符个数。如果没有传递第二个参数,则将字符串的长度作为结束位置。这些方法同样也不会修改字符串本身的值。

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

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

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

IE 的 JavaScript 实现在处理向 substr()方法传递负值的情况时存在问题,它会返回原始的字符串。IE9 修复了这个问题。

  1. 字符串位置方法
    由两个可以从字符串中查找子字符串的方法:indexOf()lastIndexOf()。这两个方法都是从一个字符串中搜索给定的子字符串,然后返回子字符串的位置(如果没有找到,则返回 -1)。这俩方法区别在于:indexOf()方法从字符串的开头向后搜索子字符串,而lastIndexOf()方法是从字符串的末尾向前搜索子字符串。
var s = "hello world";
alert(s.indexOf("o"));  //4
alert(s.lastIndexOf("o"));  //7

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

var s = "hello world";
alert(s.indexOf("o", 6));  //7
alert(s.indexOf("o", 6));  //4

在使用第二个参数的情况下,可以通过循环调用indexOf()lastIndexOf()来找到所有匹配的子字符串。

var s = "asjfjsjf;sjfjsdfjsdljfsafjsaf";
var position = new Array();
var pos = s.indexOf("a");
while (pos > -1) {
  positions.push(pos);
  pos = s.indexOf("a", pos + 1);
}
alert(positions);
  1. trim() 方法
    这个方法会创建一个字符串的副本,删除前置及后缀的所有空格,然后返回结果。
var s = "  hello world "
var trimmed = s.trim();
alert(s);  //"  hello world "
alert(trimmed);  //"hello world"

先进浏览器还支持非标准的trimLeft()trimRight()方法,分别用于删除字符串开头和末尾的空格。

  1. 字符串大小写转换方法
    ECMAScript 中涉及字符串大小写转换的方法有 4 个:toLowerCase()toLocaleLowerCase()toUpperCase()toLocaleUpperCase()

  2. 字符串的模式匹配方法
    第一个方法就是match(),在字符串上调用这个方法,本质上与调用RegExpexec()方法相同。match()方法只接受一个参数,要么是一个正则表达式,要么是一个RegExp对象。

var text = "cat, bat, sat, fat";
var pattern = /.at/;
var matches = text.match(pattern);
alert(matches.index);  //0  
alert(matches[0]);  //"cat"
alert(pattern.lastIndex);  //0

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

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

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

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

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

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

最后一个与模式匹配有关的方法是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 = colorsText.split(/[^\,]+/);  //["", ",", ",",",", ""] 
  1. localeCompare() 方法
    与操作字符串有关的最后一个方法是localeCompare(),这个方法比较两个字符串,并返回下列值中的一个:
  • 如果字符串在字母表中应该排在字符串参数之前,则返回一个负数(大多数情况下是 -1, 具体的值要视实现而定);
  • 如果字符串等于字符串参数,则返回 0;
  • 如果字符串在字母表中应该排在字符串参数之后,则返回一个正数(大多数情况下是 1,具体的值同样要视实现而定)。
var s = "yellow";
alert(s.localeCompare("brick"));  //1
alert(s.localeCompare("yellow"));  //0
alert(s.localeCompare("zoo"));  //-1

最好采用函数判断并规定返回数值。

  1. fromCharCode() 方法
    String构造函数本身还有一个静态方法:fromCharCode()。这个方法的任务是接收一或多个字符编码,然后将他们转换成一个字符串。从本质上看,这个方法与实例方法charCodeAt()执行的是相反的操作。
alert(String.fromCharCode(104, 101, 108, 108, 111));  //"hello"
  1. HTML 方法
    尽量不使用这些方法。故不列出。

5.7 单体内置对象

5.7.1 Global 对象

不属于任何其他对象的属性和方法,最终都是它的属性和方法。

  1. URI 编码方法
    encodeURI()encodeURIComponent()方法可以对 URI 进行编码,以便发送给浏览器。有效的 URI 中不能包含某些字符,例如空格。
    其中,encodeURI()主要用于整个URI(例如,http://www.baidu.com/illegal value.htm),而encodeURIComponent()主要用于对某一段(例如illegal value.htm)进行编码。它们的主要区别在于,encodeURI()不会对本身属于 URI 的特殊字符进行编码,例如冒号,正斜杠,问号和井字号;而encodeURIComponent()则会对它发现的任何非标准字符进行编码。
    对应的方法为decodeURI()decodeURIComponent()

一般来说,使用encodeURIComponent()方法更多,因为在实践中更常见的是对查询字符串参数而不是对基础 URI 进行编码。

  1. eval() 方法
    非常危险,略。
  2. Global 对象的属性
    下表列出了所有属性
属性 说明
undefined 特殊值
NaN 特殊值
Infinity 特殊值
Object 构造函数
Array 构造函数
Function 构造函数
Boolean 构造函数
String 构造函数
Number 构造函数
Date 构造函数
RegExp 构造函数
Error 构造函数
EvalError 构造函数
RangeError 构造函数
ReferenceError 构造函数
SyntaxError 构造函数
TypeError 构造函数
URIError 构造函数
  1. window 对象
    ECMAScript 虽然没有指出如何直接访问Global对象,但Web浏览器都是将这个全局对象作为window对象的一部分加以实现的。因此,在全局作用域中声明的所有变量和函数,就都成为了widnow对象的属性。

5.7.2 Math 对象

Math对象提供了辅助完成数学计算的属性和方法。
1.Math 对象的属性

属性 说明
Math.E 自然对数的底数,即常量e的值
Math.LN10 10的自然对数
Math.LN2 2的自然对数
Math.LOG2E 以2为底e的对数
Math.LOG10E 以10为底e的对数
Math.PI π的值
Math.SQRT1_2 1/2 的平方根(即2的平方根的倒数)
Math.SQRT2 2的平方根

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

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

var value = [1, 2, 3, 4, 5, 6];
var max = Math.max.apply(Math, values);

这个技巧的关键是把Math对象作为apply()的第一个参数,从而正确地设置this值。然后,可以将任何数组作为第二个参数。

3.舍入方法
将小数值舍入为整数的几个方法:Math.ceil()Math.floor()Math.round()
这三个方法分别遵循下列舍入规则:

  • Math.ceil()执行向上舍入
  • Math.floor()执行向下舍入
  • Math.round()执行标准舍入,即四舍五入

4.random()方法
Math.random()方法返回介于 0 和 1 之间随机一个随机数,不包括 0 和 1。
Math.random()从某个整数范围内随机选择一个值。

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

如果想要选择一个介于 2 到 10 之间的值

var num = Math.floor(Math.random() * 9 +2);

多数情况下,可以通过一个函数来计算可能值的总数和第一个可能的值。

function selectFrom(lowerValue, upperValue) {
  var choices = upperValue - lowerValue + 1;
  return Math.floor(Math.random() * choices + lowerValue);
}
var num = selectFrom(2, 10);
alert(num);  //介于 2 和 10 之间 (包括 2 和 10)的一个数值

5.其他方法
下表列出了没有介绍到的方法

方法 说明
Math.abs(num) 返回绝对值
Math.exp(num) 返回Math.E的num次幂
Math.log(num) 返回num的自然对数
Math.pow(num, power) 返回num的power次幂
Math.sqrt(num) 返回num的平方根
Math.acos(x) 返回x的反余弦值
Math.asin(x) 返回x的反正弦值
Math.atan(x) 返回x的反正切值
Math.atan2(y,x) 返回y/x的反正切值
Math.cos(x) 返回x的余弦值
Math.sin(x) 返回x的正弦值
Math.tan(x) 返回x的正切值

5.8 小结

对象在 JavaScript 中被称为引用类型的值,而且有一些内置的引用类型可以用来创建特定的对象。

  • 引用类型与传统面向对象程序设计中的类相似,但实现不同;
  • Object是一个基础类型,其他所有类型都从Object继承了基本的行为;
  • Array类型是一组值的有序列表,同时还提供了操作和转换这些值得功能;
  • Date类型提供了有关日期和时间的信息,包括当前日期和时间以及相关计算功能;
  • RegExp类型提供了最基本的和一些高级的正则表达式功能。

函数实际上是Function类型的实例,因此函数也是对象;由于函数是对象,所以函数也拥有方法。
因为有了基本包装类型,所以JavaScript中的基本类型可以被当作对象来访问。三种基本包装类型分别是:BooleanNumberString。以下是它们共同的特征:

  • 每个包装类型都映射到同名的基本类型;
  • 在读取模式下访问基本类型值时,就会创建对应的基本包装类型的一个对象,从而方便了数据操作;
  • 操作基本类型值的语句一经执行完毕,就会立即销毁新创建的包装对象。

在所有代码执行之前,作用域中就已经存在两个内置对象:GlobalMath。Web 浏览器实现了承担该角色的window对象。全局变量和函数都是Global对象的属性。Math对象提供了很多属性和方法,用于辅助完成复杂的数学计算任务。

推荐阅读更多精彩内容