《高级程序设计》5 引用类型
引用类型的值(对象)是引用类型的而一个实例。在javascript中,引用类型是一种数据结构,用于将数据和功能组织在一起。它也常被称为类,但这种称呼并不妥当。尽管javascript从技术上讲是一门面向对象的语言,但它不具备传统的面向对象语言所支持的类和接口等基本结构。引用类型有时候也被称为对象定义,因为它们描述的是一类对象所具有的属性和方法。
虽然引用类型与类看起来相似,但它们并不是相同的慨念。
对象是某个特定引用类型的实例。新对象是使用new操作符后跟一个构造函数来创建的。
1、Object类型
到目前为止,我们看到的大多数引用类型值都是Objec类型的实例;而且Object也是ECMAScript中使用最多的一个类型。
创建Object实例的方式有两种:
// 1.第一种是使用new操作符后跟Object构造函数 // var person = new Object(); // person.name = "Nicholas"; // person.age = 29; // 2.1另一种方式是对象字面量表示法 // 左边花括号表示对象字面量的开始,因为它出现在了表达式上下文中。 // EMACScript中的表达式上下文指能够返回一个值(表达式)。赋值操作符表示后面是一个值,所以左花括号在这里表示一个表达式的开始。 // 同样的花括号,如果出现在一个语句上下文中,例如跟在if语句条件后面,则表示一个 语句块的开始。 // var person = { // name: "Nicholas", // age: 29 // } // 2.2使用对象字面量语法时,属性名也可以使用字符串,如: // var person={ // "name":"Nicholas", // "age":29 // } // 2.3使用对象字面量语法时,如果留空其花括号,则可以定义只包含默认属性和方法的对象 // var person={}; //与 new Object()相同 // person.name="Nicholas"; // person.age=29; // 在通过对象字面量定义对象时,实际上并不会调用Object构造函数 // 开发人员更青睐对象字面量语法,对象字面量也是向函数传递大量可以选参数的首选方式。 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 }); // Name:Nicholas // Age:29 displayInfo({ name: "Greg" }); // Name:Greg // 这种传递参数的模式最适合需要向函数传入大量可选参数的情形。一般来讲,命名参数虽然容易处理,但在有多个可选参数的情况下 // 就会显示不够灵活。最好的做法是对那些必需值使用命名参数,而使用对象字面量来封装多个可选参数
一般来说,访问对象属性时使用的都是点表示法,但也可以使用方括号表示法来访问对象的属性。
var person={ "name":"Nicholas", "age":29, "first name":"Li" } alert(person.name); //"Nicholas" alert(person["name"]); //"Nicholas" // 使用方括号访问属性的优点: // 1、可以通过变量来访问属性 var propertyName="name"; alert(person[propertyName]); //"Nicholas" // 2、如果属性名中包含会导致语法错误的字符,或者属性名使用的是关键字或保留字 alert(person["first name"]); //"Li" // 属性名中是可以包含非字母非数字的,这时候就可以使用方括号表示法来访问它们。注意:(错误的字符)属性名要用字符串
除非必须使用变量来访问属性,否则我们建议使用点表示法。
2、Array类型
除了Object之外,Array类型恐怕是ECMAScript中最常用的类型了。虽然ECMAScript数组与其他语言中的数组都是数据的有序列表,但与其他语言不同的是,ECMAScript数组的每一项可以保存任何类型的数据。而且ECMAScript数组的大小时可以动态调整的,即可以随着数据的添加自动增长以容纳新增的数据。
创建数组的基本方式有两种:
// 1、使用Array构造函数 var colors = new Array(); // or 给构造函数传递数量,该数量会自动变成length属性的值 var colors = new Array(20); // or 向Array构造函数传递数组中应该包含的项 var colors = new Array("red", "blue", "green"); // 另外也可以省略new操作符 var colors = Array(3); //创建一个包含3项的数组 var names = Array("Greg"); //创建一个包含1项,即字符串"Greg"的数组 // 2、使用数组字面量。数组字面量由一对包含数组项的方括号表示,多个数组项之间以逗号隔开: var colors = ["red", "blue", "green"]; //创建一个包含三个字符串的数组 var names = []; //创建一个空数组 // 与对象一样,在使用数组字面量表示法时,也不会调用Array构造函数。
// 在读取和设置数组的值时,要使用方括号并提供相应值的基于0的数字索引。 // var colors = ["red", "blue", "green"]; // alert(colors[0]); //"red" // alert(colors[3]); //undefined // colors[2]="black"; // colors[3]="brown"; // alert(colors[2]); //"black" // alert(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 // 添加项 // 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"; // alert(colors.length); //5 // alert(colors[colors.length-1]); //"brown" // 当把一个值放在超出当前数组大小的位置上时,数组就会重新计算其长度值,即长度值等于最后一项的索引加1,如: var colors = ["red", "blue", "green"]; colors[99]="black"; alert(colors.length); //100 // 其它位置3到位置98都是不存在的,访问它们都将返回undefined。
数组最多可以包含4 294 967 295个项,这几乎已经能够满足任何编程需求了。如果想要添加的项数超过这个上限值,就会发生异常。而创建一个初始大小与这个上限值接近的数组,则可能会导致运行时间超长的脚步错误。
1)检测数组
对于一个网页或者一个全局作用域而言,用instanceof操作符就能得到满意的结果
if(value instanceof Array){
// 对数组执行某些操作
}
但是,如果网页中包含多个框架,实际上就存在两个以上不同的全局执行环境,从而存在两个以上不同版本Array构造函数。如果从一个框架向另一个框架传入一个数组,那么传入的数组与在第二个框架中原生创建的数组分别具有各自不同的构造函数。
为了解决这个问题,ECMAScript5新增了Array.isArray()方法。这个方法的目的是最终确定某个值到底是不是数组,而不管它是在哪个全局执行环境中创建的。
if(Array.isArray(value)){
// 对数值执行某些操作
}
支持Array.isArray()方法的浏览器有:IE9+,Firefox4+,safari5+,Opera10.5+和Chrome。
2)转换方法
所有对象都具有toLocaleString(),toString()和valueOf()方法。其中调用数组的toString()和valueOf()方法会返回相同的值,即由数组中每个值的字符串形式拼接而成的一个以逗号分隔的字符串。
var colors = ["red", "blue", "green"]; alert(colors.toString()); //red,blue,green alert(colors.valueOf()); //red,blue,green alert(colors); //red,blue,green
另外,数组的toLocaleString()方法经常也会返回与toString()和valueOf()方法相同的值,但也总不是如此:
var person1={ toLocaleString:function(){ return "Nikolaos" }, toString:function(){ return "Nikolaos" } }; var person2={ toLocaleString:function(){ return "Grigorios" }, toString:function(){ return "Greg" } }; var people=[person1,person2]; alert(people); //Nikolaos,Greg alert(people.toString()); //Nikolaos,Greg alert(people.toLocaleString()); //Nikolaos,Grigorios
数组继承的toLocaleString(),toString()和valueOf()方法,在默认情况下都会以逗号分隔的字符串的形式返回数组项。而使用join方法,则可以使用不同的分隔符来构建这个字符串。join()方法只接收一个参数。如果不给join()方法传入任何值,或者给它传入undefined,则使用逗号作为分隔符。IE7几更早版本会错误的使用字符串“undefined”作为分隔符。
var colors=["red","blue","green"]; alert(colors.join(",")); //red,blue,green alert(colors.join("||")); //red||blue||green
如果数组中的某一项的值是null或者undefined,那么该值在join(),toLocaleString(),toString()和valueOf()方法返回的结果中以空字符串表示。
3)栈方法
ECMAScript数组也提供了一种让数组的行为类似于其他数据结构的方法。
具体来说,数组可以表现的就像栈一样。栈是一种LIFO(Last-In-First-Out,后进先出)的数据结构,也就是最新添加的项最早被移除。而栈中项的插入(叫做推入)和移除(叫做弹出),只发生在一个位置——栈的顶部。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); //3 // var item=colors.pop(); // alert(item); //"black // alert(colors.length); //2 var colors=["red","blue"]; colors.push("brown"); colors[3]="black"; alert(colors.length); //4 var item=colors.pop(); alert(item); //"black"
4)队列方法
栈的数据结构的访问规则是LIFO(后进先出),而队列的数据结构的访问规则是FIFO(First-In-First-Out,先进先出)。队列在列表的末端添加项,从列表的前端移除项。
shift()方法它能够移除数组中的第一个项并返回该项,同时将数组长度减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
ECMAScript还为数组提供了一个unshift()方法。它能在数组前端添加任意个项并返回新数组的长度。
var colors=new Array(); var count=colors.push("red","green"); alert(count); //2 count=colors.unshift("black"); alert(count); //3 var item=colors.pop(); alert(item); //"green" alert(colors.length); //2
IE7及更早版本会对javascript的实现中存在一个偏差,其unshift()方法总是返回undefined而不是数组的新长度。IE8在非兼容模式下回返回正确的长度值。
5)重排序方法
数组中已经存在两个可以用来重排序的方法:reverse()和sort();
reverse()方法会反转数组项的顺序。
sort()方法按升序排列数组项——由于该方法比较的是字符串(即使数组中的每一项都是数值),调用每个数组项的toString()转型方法,然后比较得到的字符串。
// var values = [1, 2, 3, 4, 5]; // values.reverse(); // alert(values); //5,4,3,2,1 var values=[0,1,5,10,15]; values.sort(); alert(values); //0,1,10,15,5 // 由于sort()比较的是字符串"10"位于"5"的前面。
sort()方法可以接收一个比较函数作为参数,以便我们指定哪个值位于哪个值的前面。
// <!--适用于大多数数据类型--> // function compare(value1,value2){ // if(value1<value2){ // return -1; // }else if(value1>value2){ // return 1; // }else{ // return 0; // } // } // 或者 对于数值类型或者其valueOf()方法会返回数值类型的对象类型,可以使用一个更简单的比较函数 function compare(value1,value2){ return value1-value2; } var values=[0,1,5,10,15]; values.sort(compare); alert(values); //0,1,5,10,15 // 如果是降序,则改变return的值,或者升序后使用reverse()方法得到。
6)操作方法
contact()方法:先创建当前数组一个副本,然后将接收到的参数添加到这个副本的末尾,最后返回新构建的数组。
在没有给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); var colors4=colors.slice(0); var colors5=colors.slice(-2,1); var colors6=colors.slice(3,6); var colors7=colors.slice(-2,-1); //等价于slice(3,4); alert(colors2); //green,blue,yellow,purple alert(colors3); //green,blue,yellow alert(colors4); //red,green,blue,yellow,purple alert(colors5); //空数组 alert(colors6); //yellow,purple alert(colors7); //yellow // 如果slice()方法的参数中有一个负数,则用数组长度加上该数来确定相应的位置。
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()方法始终都会返回一个数组,该数组中包含从原始数组中删除的项 (如果没有删除任何项,则返回一个空数组)。
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"); // 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
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 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
使用indexOf()和lastIndexOf()方法查找特定项在数组中的位置非常简单,支持它们的浏览器包括IE9+,Firefox2+,Safari3+,Opera9.5+,Chorme。
8)迭代方法
EMACScript为数值定义了5个迭代方法。每个方法都接收两个参数:要在每一项上运行的函数和(可选的)运行该函数的作用域对象——影响this的值。传入这些方法中的函数会接收三个参数:数组项的值,该项在数组中的位置和数组对象本身。
根据使用的方法不同,这个函数执行后的返回值可能会也可能不会影响访问的返回值。以下是这5个迭代方法的作用:
- every():对数组中的每一项运行给函数,如果该函数对每一项都返回true,则返回true;
- filter():对数组中的每一项运行给函数,返回该函数会返回true的项组成的数组;
- forEach():对数组中的每一项运行给定函数。这个方法没有返回值;
- map():对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组;
- some():对数组中的每一项运行给定函数,如果该函数对任一项返回true,则返回true。
以上方法都不会修改数组中的包含的值。
// every()跟some(),都用于查询数组中的项是否满足某个条件。 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 // filter()用于查询符合某些条件的所有数组项 var filterResult=numbers.filter(function(item,index,array){ return(item>2); }); alert(filterResult); // 3,4,5,4,3 // map()适合创建包含的项与另一个数组一一对应的数组 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){ // 执行某些操作 });
支持这些迭代方法的浏览器有IE9+,Firefox2+,Safari3+,Opera9.5+,Chorme。
9)缩小方法
ECMAScript5还新增了两个缩小数组的方法:reduce()和reduceRight()。这两个方法都会迭代数组的所有项,然后构建一个最终返回的值。其中,reduce()方法从数组的第一项开始,逐个遍历到最后。而reduceRight()则从数组的最后一项开始,向前遍历到第一项。
这两个方法接收两个参数:一个在每一项上调用的函数和(可选的)作为缩小基础的初始值。传给reduce()和reduceRright()的函数接收4个参数:前一个值,当前值,项的索引和数组对象。这个函数返回的任何值都会作为第一个参数自动传给下一项。第一次迭代发生在数组的第二项上,因此第一个参数是数组的第一项,第二个参数就是数组的第二项。
// 使用reduce()方法可以执行求数组中所有值之和的操作 var values = [1, 2, 3, 4, 5]; var sum = values.reduce(function (prev, cur, index, array) { return prev + cur; }); var sum1 = values.reduce(function (prev, cur, index, array) { return prev + cur; }); alert(sum); //15 alert(sum1); // 使用reduce()还是reduceRight(),注意取决于要从哪头开始遍历数组。
支持这两个缩小函数的浏览器有IE9+,Firefox3+,Safari4+,Opera10.5+,Chorme。
3、Date类型
Date类型使用自UTC(Coordinated Universal Time,国际协调时间)1970年1月1日午夜(零时)开始经过的毫秒数来保存日期。在使用这种数据存储格式的条件下,Date类型保存的日期能够精确到1970年1月1日之前或之后的285616年。
var now=new Date(); alert(now); // Fri Feb 13 2015 11:44:50 GMT+0800 // 在调用Date构造函数而不传递参数的情况下,新创建的对象自动获得当前日期和时间。
如果想根据特定的日期和时间创建日期对象,必须传入表示该日期的毫秒数(即从UTC时间起至该日期止经过的毫秒数)。为了简化这一计算过程,ECMAScript提供了两个方法:Date.parse()和Date.UTC()。
Date.parse()只接收一个表示日期的字符串参数,然后尝试根据这个字符串返回相应日期的毫秒数。将地区设置为美国的浏览器通常都接收下列日期格式:
- ”月/日/年“,如6/13/2004;
- ”英文月名 日,年“如January 12,2004;
- ”英文星期几 英文月名 日 年 时:分:秒 时区",如 Tue May 25 2004 00:00:00 GMT-0700。
- ISO 8601扩展格式 YYYY-MM-DDTHH:mm:ss.sssZ(例如 2004-05-25T00:00:00)。只有兼容ECMAScript5的实现支持这种格式。
var someDate=new Date(Date.parse("6/13/2004")); // var someDate1=new Date(Date.parse("May 25,2004")); // // 如果直接将表示日期的字符串传递给Date构造函数,也会在后台调用Date.parse()。 // 上面等价于 var someDate1=new Date("May 25,2004"); var someDate2=new Date(Date.parse("25,2004")); alert(someDate); //Sun Jun 13 2004 00:00:00 GMT+0800 alert(someDate1); //Tue May 25 2004 00:00:00 GMT+0800 alert(someDate2); //Invalid Date // 如果传入的字符串不能表示日期,那么它会返回NaN?
Date.UTC()方法同样也返回表示日期的毫秒数,但它与Date.parse()在构建值时使用不同的信息。Date.UTC()的参数分别是年份、基于0的月份(一月是0、二月是1,以此类推)、月中的哪一天(1到31)、小时数(0到23)、分钟、秒、以及毫秒数。在这些参数中,只有前两个参数(年和月)是必需的。如果没有提供月中的天数,则假设为1;如果省略其他参数,则统统假设为0。
// var y2k=new Date(Date.UTC(2000,0)); // 等价于 var y2k=new Date(2000,0); // var allFives=new Date(Date.UTC(2005,4,5,17,55,55)); // 等价于 var allFives=new Date(2005,4,5,17,55,55); alert(y2k); //Sat Jan 01 2000 08:00:00 GMT+0800 alert(allFives); //Fri May 06 2005 01:55:55 GMT+0800
ECMAScript5添加了Date.now()方法,返回表示调用这个方法时的日期和时间的毫秒数。这个方法简化了使用Date对象分析代码的工作,如:
// var start=Date.now(); //// 执行for循环 // var array=new Array(); // for(var i=0;i<1000000;i++){ // array.push(i); // } // var stop=Date.now(); // var result=stop-start; //得到执行上面for循环的时间毫秒数 // alert(result); // 支持Date.now()方法的浏览器包括IE9+,Firefox3.5+,Safari3+,Opera10.5和Chrome。在不支持它的浏览器中,使用+操作符把Date转换成字符串,也可以达到同样的目的。 var start=+new Date(); // 执行for循环 var array=new Array(); for(var i=0;i<1000000;i++){ array.push(i); } var stop=+new Date(); var result=stop-start; alert(result); //21左右毫秒,同上
1)继承的方法
与其他引用类型一样,Date类型也重写了toLocaleString()、toString()和valueOf()方法;但这些方法返回的值与其他类型中的方法不同。
Date类型的toLocaleString()方法会按照与浏览器设置的地区相适宜的格式返回日期和时间。格式中包含AM或PM,但不会包含时区信息(因浏览器而异);
toString()方法则通常返回带有时区信息的日期和时间,其中时间一般以军用时间(即小时的范围是0到23)表示。
// var date1=new Date(); // console.log(date1); // console.log(date1.toLocaleString()); // console.log(date1.toString()); // console.log(date1.valueOf()); // Firefox // Date {Fri Feb 13 2015 15:08:23 GMT+0800} // 2015/2/13 下午3:08:23 // Fri Feb 13 2015 15:08:23 GMT+0800 // 1423811303484 // Chrome // Fri Feb 13 2015 15:06:18 GMT+0800 (中国标准时间) index.html:15 // 2015年2月13日 下午3:06:18 index.html:16 // Fri Feb 13 2015 15:06:18 GMT+0800 (中国标准时间) index.html:17 // 1423811178645 // IE // Fri Feb 13 15:12:06 UTC+0800 2015 // 2015年2月13日 15:12:06 // Fri Feb 13 15:12:06 UTC+0800 2015 // 1423811526720 // toLocaleString()和toString()在不同的浏览器中返回的日期和时间的格式可谓大相近庭。 // 事实上,两者的差别仅在调试代码时比较有用,而在显示日期和时间时没有什么价值 // valueOf()方法可以方便使用比较操作符(小于或大于)来比较日期值 var date1=new Date(2007,0,1); var date2=new Date(2007,1,1); alert(date1<date2); //true alert(date1>date2); //false
2)日期格式化方法
Date类型还有一些专门用于将日期格式化为字符串的方法,这些方法如下。
- toDateString()——以特定于实现的格式显示星期几、月、日和年;
- toTimeString()——以特定于实现的格式显示时、分、秒和时区;
- toLocaleDateSring()——以特定于地区的格式显示星期几、月、日和年;
- toLocaleTimeString()——一特定于实现的格式显示时、分、秒;
- toUTCString()——以特定于实现的格式完整的UTC日期。
以上这些字符串格式方法的输出也是因浏览器而异的,因此没有哪一个方法能够用来在用户界面中显示一致的信息。
var date1=new Date(); console.log(date1); console.log(date1.toDateString()); console.log(date1.toTimeString()); console.log(date1.toLocaleDateString()); console.log(date1.toLocaleTimeString()); console.log(date1.toUTCString()); // Firefox // Date {Fri Feb 13 2015 15:33:47 GMT+0800} // Fri Feb 13 2015 // 15:33:47 GMT+0800 // 2015/2/13 // 下午3:33:47 // Fri, 13 Feb 2015 07:33:47 GMT // Chrome // Fri Feb 13 2015 15:35:36 GMT+0800 (中国标准时间) // Fri Feb 13 2015 // 15:35:36 GMT+0800 (中国标准时间) // 2015年2月13日 // 下午3:35:36 // Fri, 13 Feb 2015 07:35:36 GMT // IE // Fri Feb 13 15:39:09 UTC+0800 2015 // Fri Feb 13 2015 // 15:39:09 UTC+0800 // 2015年2月13日 // 15:39:09 // Fri, 13 Feb 2015 07:39:09 UTC
3)日期/时间组件方法
到目前为止,剩下还未介绍的Date类型的方法(如下表示),都是直接取得和设置日期值中特定部分的方法了。需要注意的是,UTC日期指的是在没有时区偏差的情况下(将日期转换为GMT时间)的日期值。
方法 | 说明 |
getTime() | 返回表示日期的毫秒数;与valueOf()方法返回的值相同 |
setTime(毫秒) | 以毫秒数设置日期,会改变整个日期 |
getFullYear() | 取得4位数的年份(如2007而非仅07) |
getUTCFullYear() | 返回UTC日期的4位数年份 |
setFullYear(年) | 设置日期的年份。传入的年份值必须是4为数字(如2007而非仅07) |
setUTCFullYear(年) | 设置UTC日期的年份,传入的年份值必须是4为数字(如2007而非仅07) |
getMoth() | 返回日期中的月份,其中0表示一月,11表示十二月 |
getUTCMoth() | 返回UTC日期中的月份,其中0表示一月,11表示十二月 |
setMoth(月) | 设置日期的月份。传入的月份值必须大于0,超过11则增加年份 |
setUTCMoth(月) | 设置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.在某地进入夏令时的情况下,这个值会有所变化 |
var date1=new Date("Fri Feb 13 2015 15:06:18 GMT+0800"); console.log(date1.toLocaleString()); // 2015/2/13 下午3:06:18 console.log(date1.getTime()); //1423811178000 date1.setTime(1423815174090); console.log(date1.toLocaleString()); // 2015/2/13 下午4:12:54 console.log(date1.getFullYear()); //2015 date1.setFullYear(2014); console.log(date1.toLocaleString()); // 2014/2/13 下午4:12:54 // 月份是0到11 console.log(date1.getMonth()); //1 注意:2月获取的月份是1 date1.setMonth(4); console.log(date1.toLocaleString()); //2014/5/13 下午4:12:54 date1.setMonth(15); //超过11则增加年份 console.log(date1.toLocaleString()); //2015/4/13 下午4:12:54 // 天数是1到31,根据具体月份数的天数 console.log(date1.getDate()); //13 date1.setDate(23); console.log(date1.toLocaleString()); //2015/4/23 下午4:12:54 date1.setDate(50); //15年4月有30天 console.log(date1.toLocaleString()); //2015/5/20 下午4:12:54 // 0表示周日,1表示周一,6表示周六 console.log(date1.getDay()); //3 星期三 // 小时是0到23 console.log(date1.getHours()); //16 date1.setHours(8); console.log(date1.toLocaleString()); //2015/5/20 上午8:12:54 date1.setHours(24); console.log(date1.toLocaleString()); //2015/5/21 上午12:12:54 注意setHours(0)就是12 date1.setHours(25); console.log(date1.toLocaleString()); //2015/5/22 上午1:12:54 // 分钟是0到59 console.log(date1.getMinutes()); // 12 date1.setMinutes(66); console.log(date1.toLocaleString()); //2015/5/22 上午2:06:54
4、RegExp类型
ECMAScript通过RegExp类型来支持正则表达式。使用下面类似Perl的语法,就可以创建一个正则表达式。
var expression=/ pattern / flags;
其中的模式(pattern)部分可以是任何简单或复杂的正则表达式,可以包含字符类、限定符、分组、向前查找以及反向引用。每个正则表达式都可带有一或多个标志(flags),用以标明正则表达式的行为。正则表达式的匹配模式支持下列3个标志。
- g:表示全局(global)模式,即模式将被应用于所有字符串,而非在发现第一个匹配项时就立即停止;
- i:表示不区分大小写(case-insensitive)模式,即在确定匹配项时忽略模式与字符串的大小写;
- m:表示多行(multiline)模式,即在到达一行文本末尾时还会继续查找下一行中是否存在与模式匹配的项。
与其他语言中的正则表达式类似,模式中使用的所有元字符都必须转义。正则表达式中的元字符包括:
( [ { \ ^ $ | ) ? * + . ] }
// 对象字面量和构造函数定义 // 匹配字符串中所有“at”的实例 var pattern1 = /at/g; // 匹配第一个“bat”或“cat”,不区分大小写 var pattern2 = /[bc]at/i; var pattern21=new RegExp("[bc]at","i"); // 匹配所有以“at”结尾的3个字符的组合,不区分大小写 var patter3 = /.at/gi; // 使用转义字符 // 匹配第一个“[bc]at”,不区分大小写 var pattern21 = /\[bc\]at/i; // 匹配所有“.at”, 不区分大小写 var patter31 = /\.at/gi;
由于RegExp构造函数的模式参数是字符串,所以在某些情况下要对字符串进行双重转义。
/\[bc\]at/ “\\[bc\\]at”
/\.at/ "\\.at"
/name\/age/ "name\\age"
/\d.\d{1,2}/ "\\d.\\d{1,2}"
/\w\\hello\\123/ "\\w\\\\hello\\\\123"
注意:在ECMAScirpt 3中,正则表达式字面量始终会共享同一个RegExp实例,而使用构造函数创建的每一个新RegExp实例都是一个新实例。
ECMAScript明确规定,使用正则表达式字面量必须像直接调用RegExp构造函数一样,每次都创建新的RegExp实例。IE9+,Firefox 4+和Chrome都据此做出了修改。
1)RegExp实例属性:
- global:布尔值,表示是否设置了g标志
- ignoreCase:布尔值,表示是否设置了i标志
- lastIndex:整数,表示开始搜索下一个匹配项的字符位置,从0算起
- multiline:布尔值,表示是否设置了m标志
- source:正则表达式的字符串表示,按照字面量形式而非传入构造函数中的字符串模式返回
var pattern1 = /\[bc\]at/i; alert(pattern1.global); //false alert(pattern1.ignoreCase); //true alert(pattern1.multiline); //false alert(pattern1.lastIndex); //0 alert(pattern1.source); //"\[bc\]at" var pattern2=new RegExp("\\[bc\\]at","i"); alert(pattern2.global); //false alert(pattern2.ignoreCase); //true alert(pattern2.multiline); //false alert(pattern2.lastIndex); //0 alert(pattern2.source); //"\[bc\]at"
2)RegExp实例方法
exec();接受一个参数,即要应用模式的字符串,然后返回包含第一个匹配项信息的数组;或者在没有匹配项的情况下返回null。
返回的数组虽然是Array实例,但包含两个额外的属性:index和input。其中,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" var text1 = "cat, bat, sat, fat"; var pattern1 = /.at/; var matches1 = pattern1.exec(text1); alert(matches1.index); //0 alert(matches1.input); //"cat, bat, sat, fat" alert(matches1[0]);//"cat" alert(pattern1.lastIndex); //0 matches1 = pattern1.exec(text1); alert(matches1.index); //0 alert(matches1[0]); //"cat" alert(pattern1.lastIndex); //0 var pattern2 = /.at/g; var matches2 = pattern2.exec(text1); alert(matches2.index); //0 alert(matches2.input); //"cat, bat, sat, fat" alert(matches2[0]); //"cat" alert(pattern2.lastIndex); //3 matches2 = pattern2.exec(text1); alert(matches2.index); //5 alert(matches2[0]); //"bat" alert(pattern2.lastIndex); //8
test();接受一个字符串参数。在模式与该参数匹配的情况下返回ture,否则,返回false。
var text = "000-00-0000"; var pattern = /\d{3}-\d{2}-\d{4}/; if (pattern.test(text)) { alert("The pattern was matched."); } var pattern1 = new RegExp("\\[bc\\]at", "gi"); alert(pattern1.toString()); // /\[bc\]at/gi alert(pattern1.toLocaleString()); // /\[bc\]at/gi alert(pattern1.valueOf()); // /\[bc\]at/gi //即使通过RegExp构造函数创建,当toLocaleString()和toString()方法仍然会像它是以字面量形式创建的一样显示其字符串表示 // 正则表达式的valueOf()方法返回正则表达式本身
3)RegExp构造函数属性 page107
5、Function类型
函数是对象,每个函数都是Function类型的实例。函数名是指针。
// 函数声明语法 定义 function sum(num1,num2){ return num1+num2; } // 函数表达式定义 var sum=function(num1,num2){ return num1+num2; }
1)没有重载(深入理解)
将函数名想象成指针,也有助于理解为什么ECMAScript中没有函数重载的慨念
2)函数声明和函数表达式的区别:
解析器在向执行环境中加载数据时,会率先读取函数声明,并使其在执行任何代码之前可用(可以访问到);至于函数表达式,则必须等到解析器执行到它所在的代码行,才会真正被解释执行。
alert(sum(10, 10)); //20 function sum(num1, num2) { return num1 + num2; } alert(sum1(10,20)); //TypeError: sum1 is not a function var sum1=function(num1,num2){ return num1+num2; }
3)作为值的函数
function callSomeFunction(someFunction,someArgument){ return someFunction(someArgument); } function add10(num){ return num+10; } var result1=callSomeFunction(add10,30); alert(result1); //40 function getGreeting(name){ return "Hello "+name; } var result2=callSomeFunction(getGreeting,"Nicholas"); alert(result2); //Hello Nicholas
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; } // 当value1和value2为数值的时候可以直接减操作,当为字符串的时候不能用操作符“-”,返回结果为NaN // alert(value1<value2); } } var data = [ {name: "Zachary", age: 28}, {name: "Nicholas", age: 29}, {name: "wuxianning", age: 10} ]; data.sort(createComparisonFunction("name")); alert(data[0].name); //Nicholas data.sort(createComparisonFunction("age")); alert(data[0].name); //wuxianning
4)函数内部属性
在函数内部有两个特殊的对象:arguments和this。
虽然arguments的主要用途是保存函数参数,但这个对象还有一个名叫callee的属性,该属性是一个指针,指向拥有这个arguments对象的函数。
// function factorial(num){ // if(num<=1){ // return 1; // }else{ // return num*factorial(num-1); // } // } // 使用callee function factorial(num){ if(num<=1){ return 1; }else{ return num*arguments.callee(num-1); } } var trueFactorial=factorial; factorial=function(){ return 0; } alert(trueFactorial(5)); //120 alert(factorial(5)); //0
this引用的是函数据以执行的环境对象——或者也可以说是this值(当在网页的全局作用域中调用函数时,this对象引用的就是window)
window.color = "red"; var o = {color: "blue"} function sayColor(){ alert(this.color); } sayColor(); o.sayColor=sayColor; //"red" o.sayColor(); //"blue"
caller。
这个属性保存着调用当前函数的函数的引用,如果是在全局作用域中调用当前函数,它的值为null。
5)函数属性和方法
每个函数都包含两个属性: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
对于ECMAScript中的引用类型而言,prototype是保存它们所有实例方法的真正所在。在创建自定义引用类型以及实现继承时,prototype属性的作用是极为重要的。
每个函数都包含两个非继承而来的方法:apply()和call()。这两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内this对象的值。
apply():接收两个参数,一个是在其中运行函数的作用域,另一个是参数数组。其中,第二个参数可以是Array的实例,也可以是arguments对象。
function sum(num1, num2) { return num1 + num2; } function callSum(num1,num2){ return sum.call(this,num1,num2); } function callSum1(num1, num2) { return sum.apply(this, arguments); } function callSum2(num1, num2) { return sum.apply(this, [num1, num2]); } alert(callSum(10,10)); //20 alert(callSum1(5, 10)); //15 alert(callSum2(10, 8)); //18 // callSum1()在执行sum()函数时传入了this作为this值(因为是在全局作用域中调用的,所以传入的就是window对象)
call()方法和apply()方法的作用相同,区别仅在于接收参数的方式不同。
call()和apply()真正强大的地方是能够扩充函数赖以运行的作用域。
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
6、基本包装类型
ECMAScript提供了3个特殊的引用类型:Boolean,Number和String。
var s1 = new String("some text"); s1.color = "red"; var s2 = "some text"; //s2本身是一个基本数据类型而不是对象,基本数据类型不应该有方法,但它们确实有方法比如:s2.substring(2) // 实际上执行了三个步骤:1)创建String类型的一个实例;2)在实例上调用指定的方法;3)销毁这个实例 // var a1 = "some text"; // var a2 = a1.substring(2); // // 相当于 // var a1 = new String("some text"); // var a2 = a1.substring(2); // a1 = null; s2.color = "blue"; alert(typeof s1); //object alert(typeof s2); //string alert(s1.color); //"red" alert(s2.color); //undefined
使用new调用基本包装类型的构造函数,与直接调用同名的转型函数是不一样的。
所有基本包装类型的对象都会被转换为布尔值true。
1)Boolean类型
var falseObject = new Boolean(false); var result = falseObject && true; alert(result); //true var falseValue = false; result1 = falseValue && true; alert(result1); //false alert(typeof falseObject); //object alert(typeof falseValue); //boolean alert(falseObject instanceof Boolean); //true alert(falseValue instanceof Boolean); //false
基本类型与引用类型的布尔值有两个区别:首先,typeof操作符对基本类型返回“boolean”,而对引用类型返回“object”。其次,由于Boolean对象是Boolean类型的实例,所以使用instanceof操作符测试Boolean对象会返回true,而测试基本类型的布尔值则返回false。
2)Number类型:toString()
toFixed(); 按照指定的小数位返回数值的字符串表示
toExPonential();返回以指数表示法(也称e表示法)表示的数值的字符串形式。
toPrecision();可能会返回固定大小(fixed)格式,也可能返回指数(exponential)格式;具体规则是看那种格式最合适
var num=10; var num1=10.005; var num2=99; alert(num.toFixed(2)); //10.00 alert(num1.toFixed(2)); //10.01 //自动舍入的特性,注意IE8不能正确舍入范围{(-0.94,-0.5],[0.5,0.94)}之间 alert(num.toExponential(1)); //1.0e+1 alert(num2.toPrecision(1)); //1e+2 alert(num2.toPrecision(2)); //99 alert(num2.toPrecision(3)); //99.0
type跟instanceof同Boolean类型。
3) String类型
①字符方法
charAt(); 用于访问字符串中特定字符的方法
charCodeAt(); 得到字符编码。
var stringValue = "hello world"; alert(stringValue.charAt(1)); //"e" alert(stringValue.charCodeAt(1)); //"101" alert(stringValue[1]); "e" //IE8及Firefox、Safari、Chorme、Opera所以版本的支持,IE7版本返回undefined
②字符串操作方法
concat();专门用来拼接字符串的方法,可接受任意多个参数,但实践中使用更多的还是加号操作符(+)。而且,使用加号操作符在大多数情况下都比使用concat()方法要简便易行(特别是拼接多个字符串的情况下)
var stringValue = "hello "; var result = stringValue.concat("world"); alert(result); //"hello world" alert(stringValue); //"hello " var result1 = stringValue.concat("world", "!"); alert(result1); //"hello world!" alert(stringValue); //"hello "
基于子字符串创建新字符串的方法,和concat()方法一样,对原始字符串没有任何影响:
slice(); substr(); substring();
这三个方法都会返回被操作字符串的一个子字符串,而且也都接受一或两个参数。第一个参数指定子字符串的开始位置,第二个参数(在指定的情况下)表示字符串到哪里结束。具体来说slice()和substring()的第二个参数指定的是子字符串最后一个字符后面的位置。而substr()的第二个参数指定的则是返回的字符个数。如果没有给这些方法传递第二个参数,则将字符串的长度作为结束位置。
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" // 当为负数的情况 alert(stringValue.length); //11 alert(stringValue.slice(-3)); //"rld" alert(stringValue.substring(-3)); //"hello world" //把所有负数参数都转换为0 alert(stringValue.substr(-3)); //"rld" //将负的第一个参数加上字符串的长度,将负的第二个参数转换为0 alert(stringValue.slice(3,-4)); //"lo w" alert(stringValue.substring(3,-4)); //"hel" 相当于substring(0,3); alert(stringValue.substr(3,-4)); //""(空字符串)
注:IE的javascript实现在处理向substr()传递负值的情况时存在问题,它会返回原始的字符串。IE9修复了这个问题。
③字符串位置方法
indexof(); 从字符串的开头向后搜索字符串
lastIndexof(); 从字符串的末尾向前搜索字符串
也可以接收两个参数,表示从字符串中的哪个位置开始搜索
var stringValue="hello world"; alert(stringValue.indexOf("o")); //4 alert(stringValue.lastIndexOf("o")); //7 alert(stringValue.indexOf("o",6)); //7 从指定的位置6向后搜索 alert(stringValue.lastIndexOf("o",6)); //4 从指定的位置6向前搜索 // 实例 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()方法
创建一个字符串的副本,删除前置及后缀的所有空格,然后返回结果。
var stringValue = " hello world "; var trimmedStringValue = stringValue.trim(); alert(trimmedStringValue); //"hello world" alert(stringValue); //" hello world " // 支持trim()方法的浏览器有IE9+,Firefox 3.5+,Safari 5+,Opera 10.5+和Chrome。 // 此外,Firefox 3.5+,Safari 5+和Chrome 8+还支持非标准的trimLeft()和trimRight()。
⑤字符串大小写转换方法
var stringValue = "hello world"; alert(stringValue.toLocaleUpperCase()); //"HELLO WORLD" //针对特定地区实现 alert(stringValue); //"hello world" alert(stringValue.toUpperCase()); //"HELLO WORLD" alert(stringValue.toLocaleLowerCase()); //"hello world" alert(stringValue.toLowerCase()); //"hello world" // 一般来说,在不知道自己的代码将在哪种语言环境中运行的情况下,还是使用针对地区的方法更稳妥一些
⑥字符串模式匹配方法
match();只接受一个参数,要么是一个正则表达式,要么是一个RegExp对象。
var text = "cat, bat, sat, fat"; var pattern=/.at/; var matches=text.match(pattern); // 等同于 // var matches=pattern.exec(text); alert(matches.length); //1 alert(matches.index); //0 alert(matches[0]); //"cat" alert(matches.lastIndex); //0
search();参数与match()方法的参数相同。返回字符串中第一个匹配项的索引。如果没有找到匹配项,则返回-1;
var text = "cat, bat, sat, fat"; var pos=text.search(/bat/); alert(pos); //5 var pos1=text.search(/dat/); alert(pos1); //-1
replace();接收两个参数,第一个参数可以是一个RegExp对象或者一个字符串(这个字符串不会被转换为正则表达式),第二个参数可以是一个字符串或者一个函数。如果第一个参数是字符串,那么只会替换第一个子字符串。要想替换所有子字符串,唯一的办法就是提供一个正则表达式,而且要指定全局(g)标志。
var text = "cat, bat, sat, fat"; var result = text.replace("at", "ond"); alert(result); //"cond, bat, sat, fat" var result1 = text.replace(/at/g, "ond"); alert(result1); //"cond, bond, sond, fond"
var text = "cat, bat, sat, fat"; result = text.replace(/(.at)/g, "word ($1)"); result1 = text.replace(/(.a)t/g, "word ($1)"); alert(result); //word (cat), word (bat), word (sat), word (fat) alert(result1); //word (ca), word (ba), word (sa), word (fa)
split();基于指定的分隔符将一个字符串分割成多个子字符串,并将结果放在一个数组中。
var colorText = "red,blue,green,yellow"; var colors1 = colorText.split(","); alert(colors1.length); //4 alert(colors1 instanceof Array); //true alert(colors1); //red,blue,green,yellow var colors2 = colorText.split(",", 2); alert(colors2); //red,blue var colors3 = colorText.split(/[^\,]+/) //一次或多次匹配任何不在,的内容。 alert(colors3); //,,,,,,,
alert(colors3[0]); //" "
alert(colors3[1]); //","
alert(colors3[2]); //","
alert(colors3[3]); //","
alert(colors3[4]); //" "
//第一项和最后一项是两个空字符串。之所以这样,是因为通过正则表达式指定的分隔符出现在了字符串的开头(即字符串“red”)和末尾(即字符串“yellow”)
7、单体内置对象
8、小结