数据类型
JavaScript中有5种简单数据类型(也称为基本数据类型):Undefined、Null、Boolean、Number和String。还有1种复杂数据类型——Object,Object本质上是由一组无序的名值对组成的。
typeof操作符
介于JavaScript是松散类型的,因此需要有一种手段来检测给定变量的数据类型——typeof就是负责提供者方面信息的操作符。对一个值使用typeof操作符可能返回下列某个字符串:
● "undefined"——如果这个值未定义;
● "boolean"——如果这个值是布尔值;
● "string"——如果这个值是字符串;
● "number"——如果这个值是数值;
● "object"——如果这个值是对象或null;
● "function"——如果这个值是函数;
Undefined类型
Undefined类型只有一个值,即特殊的undefined。在使用var声明变量但未对其加以初始化时,这个变量的值就是undefined,例如:
var message;
alert(message == undefined) //true
Null类型
Null类型是第二个只有一个值的数据类型,这个特殊的值是null。从逻辑角度来看,null值表示一个空对象指针,而这也正是使用typeof操作符检测null时会返回"object"的原因,例如:
var car = null;
alert(typeof car); // "object"
如果定义的变量准备在将来用于保存对象,那么最好将该变量初始化为null而不是其他值。这样一来,只要直接检测null值就可以知道相应的变量是否已经保存了一个对象的引用了,例如:
if(car != null)
{
//对car对象执行某些操作
}
实际上,undefined值是派生自null值的,因此ECMA-262规定对它们的相等性测试要返回true。
alert(undefined == null); //true
尽管null和undefined有这样的关系,但它们的用途完全不同。无论在什么情况下都没有必要把一个变量的值显式地设置为undefined,可是同样的规则对null却不适用。换句话说,只要意在保存对象的变量还没有真正保存对象,就应该明确地让该变量保存null值。这样做不仅可以体现null作为空对象指针的惯例,而且也有助于进一步区分null和undefined。
Boolean类型
该类型只有两个字面值:true和false。这两个值与数字值不是一回事,因此true不一定等于1,而false也不一定等于0。
虽然Boolean类型的字面值只有两个,但JavaScript中所有类型的值都有与这两个Boolean值等价的值。要将一个值转换为其对应的Boolean值,可以调用类型转换函数Boolean(),例如:
var message = 'Hello World';
var messageAsBoolean = Boolean(message);
在这个例子中,字符串message被转换成了一个Boolean值,该值被保存在messageAsBoolean变量中。可以对任何数据类型的值调用Boolean()函数,而且总会返回一个Boolean值。至于返回的这个值是true还是false,取决于要转换值的数据类型及其实际值。下表给出了各种数据类型及其对象的转换规则。
这些转换规则对理解流控制语句(如if语句)自动执行相应的Boolean转换非常重要,例如:
var message = 'Hello World';
if(message)
{
alert("Value is true");
}
运行这个示例,就会显示一个警告框,因为字符串message被自动转换成了对应的Boolean值(true)。由于存在这种自动执行的Boolean转换,因此确切地知道在流控制语句中使用的是什么变量至关重要。
Number类型
这种类型用来表示整数和浮点数值,还有一种特殊的数值,即NaN(非数值 Not a Number)。这个数值用于表示一个本来要返回数值的操作数未返回数值的情况(这样就不会抛出错误了)。例如,在其他编程语言中,任何数值除以0都会导致错误,从而停止代码执行。但在JavaScript中,任何数值除以0会返回NaN,因此不会影响其他代码的执行。
NaN本身有两个非同寻常的特点。首先,任何涉及NaN的操作(例如NaN/10)都会返回NaN,这个特点在多步计算中有可能导致问题。其次,NaN与任何值都不相等,包括NaN本身。例如,下面的代码会返回false。
alert(NaN == NaN); //false
JavaScript中有一个isNaN()函数,这个函数接受一个参数,该参数可以使任何类型,而函数会帮我们确定这个参数是否"不是数值"。isNaN()在接收一个值之后,会尝试将这个值转换为数值。某些不是数值的值会直接转换为数值,例如字符串"10"或Boolean值。而任何不能被转换为数值的值都会导致这个函数返回true。例如:
alert(isNaN(NaN)); //true
alert(isNaN(10)); //false(10是一个数值)
alert(isNaN("10")); //false(可能被转换为数值10)
alert(isNaN("blue")); //true(不能被转换为数值)
alert(isNaN(true)); //false(可能被转换为数值1)
有3个函数可以把非数值转换为数值:Number()、parseInt()和parseFloat()。第一个函数,即转型函数Number()可以用于任何数据类型,而另外两个函数则专门用于把字符串转换成数值。这3个函数对于同样的输入会返回不同的结果。
Number()函数的转换规则如下:
● 如果是Boolean值,true和false将分别被替换为1和0
● 如果是数字值,只是简单的传入和返回
● 如果是null值,返回0
● 如果是undefined,返回NaN
● 如果是字符串,遵循下列规则:
○ 如果字符串中只包含数字,则将其转换为十进制数值,即"1"会变成1,"123"会变成123,而"011"会变成11(前导的0被忽略)
○ 如果字符串中包含有效的浮点格式,如"1.1",则将其转换为对应的浮点数(同样,也会忽略前导0)
○ 如果字符串中包含有效的十六进制格式,例如"0xf",则将其转换为相同大小的十进制整数值
○ 如果字符串是空的,则将其转换为0
○ 如果字符串中包含除了上述格式之外的字符,则将其转换为NaN
● 如果是对象,则调用对象的valueOf()方法,然后依照前面的规则转换返回的值。如果转换的结果是NaN,则调用对象的toString()方法,然后再依次按照前面的规则转换返回的字符串值。
var num1 = Number("Hello World"); //NaN
var num2 = Number(""); //0
var num3 = Number("000011"); //11
var num4 = Number(true); //1
由于Number()函数在转换字符串时比较复杂而且不够合理,因此在处理整数的时候更常用的是parseInt()函数。parseInt()函数在转换字符串时,更多的是看其是否符合数值模式。它会忽略字符串前面的空格,直至找到第一个非空格字符。如果第一个字符串不是数字字符或者负号,parseInt()会返回NaN;也就是说,用parseInt()转换空字符串会返回NaN。如果第一个字符是数字字符,praseInt()会继续解析第二个字符,知道解析完所有后续字符或者遇到了一个非数字字符。例如,"1234blue"会被转换为1234,"22.5"会被转换为22,因为小数点并不是有效的数字字符。
如果字符串中的第一个字符是数字字符,parseInt()也能够识别出各种整数格式(即十进制、八进制、十六进制)。为了更好的理解parseInt()函数的转换规则,下面给出一些例子
var num1 = parseInt("1234blue"); //1234
var num2 = parseInt(""); //NaN
var num3 = parseInt("0xA"); //10(十六进制)
var num4 = parseInt("22.5"); //22
var num5 = parseInt("070"); //56(八进制)
var num6 = parseInt("70"); //70
var num7 = parseInt("10",2); //2(按二进制解析)
var num8 = parseInt("10",8); //8(按八进制解析)
var num9 = parseInt("10",10); //10(按十进制解析)
var num10 = parseInt("10",16); //16(按十六进制解析)
var num11 = parseInt("AF"); //56(八进制)
var num12 = parseInt("AF",16); //175
与parseInt()函数类似,parseFloat()也是从第一个字符(位置0)开始解析每个字符。而且也是一直解析到字符串末尾,或者解析到遇见一个无效的浮点数字字符为止。也就是说,字符串中的第一个小数点是有效的,而第二个小数点就是无效的了,因此它后面的字符串将被忽略。例如,"22.34.5"将会被转换成22.34。
parseFloat()和parseInt()的第二个区别在于它始终都会忽略前导的零。由于parseFloat()值解析十进制值,因此它没有用第二个参数指定基数的用法。
var num1 = parseFloat("1234blue"); //1234
var num2 = parseFloat("0xA"); //0
var num3 = parseFloat("22.5"); //22.5
var num4 = parseFloat("22.34.5"); //22.34
var num5 = parseFloat("0908.5"); //908.5
String类型
String类型用于表示由零或多个16位Unicode字符组成的字符序列,即字符串。字符串可以由单引号(')或双引号(")表示。
var str1 = "Hello";
var str2 = 'Hello';
任何字符串的长度都可以通过访问其length属性取得
alert(str1.length); //输出5
要把一个值转换为一个字符串有两种方式。第一种是使用几乎每个值都有的toString()方法。
var age = 11;
var ageAsString = age.toString(); //字符串"11"
var found = true;
var foundAsString = found.toString(); //字符串"true"
数值、布尔值、对象和字符串值都有toString()方法。但null和undefined值没有这个方法。
多数情况下,调用toString()方法不必传递参数。但是,在调用数值的toString()方法时,可以传递一个参数:输出数值的基数。
var num = 10;
alert(num.toString()); //"10"
alert(num.toString(2)); //"1010"
alert(num.toString(8)); //"12"
alert(num.toString(10)); //"10"
alert(num.toString(16)); //"a"
通过这个例子可以看出,通过指定基数,toString()方法会改变输出的值。而数值10根据基数的不同,可以在输出时被转换为不同的数值格式。
在不知道要转换的值是不是null或undefined的情况下,还可以使用转型函数String(),这个函数能够将任何类型的值转换为字符串。String()函数遵循下列转换规则:
● 如果值有toString()方法,则调用该方法(没有参数)并返回相应的结果
● 如果值是null,则返回"null"
● 如果值是undefined,则返回"undefined"
var value1 = 10;
var value2 = true;
var value3 = null;
var value4;
alert(String(value1)); //"10"
alert(String(value2)); //"true"
alert(String(value3)); //"null"
alert(String(value4)); //"undefined"
Object类型
对象其实就是一组数据和功能的集合。对象可以通过执行new操作符后跟要创建的对象类型的名称来创建。而创建Object类型的实例并为其添加属性和(或)方法,就可以创建自定义对象。
var o = new Object();
Object的每个实例都具有下列属性和方法:
● constructor——保存着用于创建当前对象的函数
● hasOwnProperty(propertyName)——用于检查给定的属性在当前对象实例中(而不是在实例的原型中)是否存在。其中,作为参数的属性名(propertyName)必须以字符串形式指定(例如:o.hasOwnProperty("name"))
● isPrototypeOf(object)——用于检查传入的对象是否是另一个对象的原型
● propertyIsEnumerable(propertyName)——用于检查给定的属性是否能够使用for-in语句来枚举
● toString()——返回对象的字符串表示
● valueOf()——返回对象的字符串、数值或布尔值表示。通常与toString()方法的返回值相同。
Object类型
Object类型是JavaScript中使用最多的一种类型。虽然Object的实例不具备多少功能,但对于在应用程序中存储和传输数据而言,它确实是非常理想的选择。
创建Object实例的方式有两种,第一种是使用new操作符后跟Object构造函数。
var person = new Object();
person.name = "tt";
person.age = 12;
另一种方式是使用对象字面量表示法。
var person = {
name : 'tt',
age : 12
}
另外,使用对象字面量语法时,如果留空其花括号,则可以定义值包含默认属性和方法的对象。
var person = {}; //与new Object()相同
person.name = "tt";
person.age = 12;
虽然可以使用前面介绍的任何一种方法来定义对象,但开发人员更青睐第二种方法(对象字面量语法),因为这种语法要求的代码量少,而且能给人封装数据的感觉。实际上,对象字面量也是向函数传递大量可选参数的首选方式,例如:
function showInfo(args)
{
if(args.name != undefined)
{
alert(args.name);
}
if(args.age != undefined)
{
alert(args.age);
}
}
showInfo({
name:'name',
age:12
});
showInfo({name:'name'});
一般来说,访问对象属性时使用的都是点表示法,这也是很多面向对象语言中通用的语法。不过,在JavaScript也可以使用方括号表示法来访问对象的属性。例如:
alert(person.name);
alert(person['name']);
从功能上看,这两种访问对象属性的方法没有任何区别。但方括号语法的主要优点是可以通过变量来访问属性。
var propertyName = 'name';
alert(person[propertyName]);
通常,除非必须使用变量来访问属性,否则我们建议使用点表示法。
Array类型
JavaScript中的数组与其他多数语言中的数组有着相当大的区别。虽然JavaScript数组与其他语言中的数组都是数据的有序列表,但与其他语言不同的是,JavaScript数组的每一项可以保持任何类型的数据。也就是说,可以用数组的第一个位置来保存字符串,用第二个位置来保存数值,用第三个位置来保存对象。而且,JavaScript数组的大小是可以动态调整的,即可以随着数据的添加自动增长以容纳新增数据。
创建数组的基本方式有两种。第一种是使用Array构造函数。
var colors1 = new Array();
var colors2 = new Array(20);
var colors3 = new Array('red','blue','yellow');
创建数组的第二种基本方式是使用数组字面量表示法。
var colors1 = [];
var colors2 = ['red','blue','yellow'];
在读取和设置数组的值时,要使用方括号并提供相应值的基于0的数字索引。
var colors = ['red','blue','yellow']; //定义一个字符串数组
alert(colors[0]); //显示第一项
colors[2] = 'green'; //修改第三项
colors[3] = 'black'; //新增第四项
数组的长度保存在其length属性中,这个属性始终会返回0或更大的值。
var colors = ['red','blue','yellow'];
var names = [];
alert(colors.length); //3
alert(names.length); //0
数组的length属性很有特点——它不是只读的。因此,通过设置这个属性,可以从数组的末尾移除项或想数组中添加新项。
var colors = ['red','blue','yellow'];
colors.length = 2;
alert(colors[2]); //undefined
这个例子中的数组colors一开始有3个值。将其length属性设置为2会移除最后一项,结果再访问colors[2]就会显示undefined了。
利用length属性也可以方便地在数组末尾添加新项。
var colors = ['red','blue','yellow'];
colors[colors.length] = 'green'; //在位置3添加一种颜色
colors[colors.length] = 'black'; //再在位置4添加一种颜色
由于数组最后一项的索引始终是length-1,因此下一个新项的位置就是length。
转换方法
所有对象都具有toLocaleString()、toString()和valueOf()方法。其中,调用数组的toString()和valueOf()方法会返回相同的值,即由数组中每个值的字符串形成拼接而成的一个以逗号分隔的字符串。实际上,为了创建这个字符串会调用数组每一项的toString()方法。
var colors = ['red','blue','yellow'];
alert(colors.toString()); //red,blue,yellow
alert(colors.valueOf()); //red,blue,yellow
alert(colors); //red,blue,yellow
我们首先显式地调用了toString()和valueOf()方法,以便返回数组的字符串表示,每个值的字符串表示拼接成了一个字符串,中间以逗号分隔。最后一行代码直接将数组传递给了alert()。由于alert()要接收字符串参数,所有它会在后台调用toString()方法,由此会得到与直接调用toString()方法相同的结果。
另外,toLocaleString()方法经常也会返回与toString()和valueOf()方法相同的值,但也不总是如此。当调用数组的toLocaleString()方法时,它也会创建一个数组值的以逗号分隔的字符串。而与前两个方法唯一的不同之处在于,这一次为了取得每一项的值,调用的是每一项的toLocaleString()方法,而不是toString()方法。例如:
var person1 = {
toLocaleString : function(){
return "person1 : toLocaleString";
},
toString : function(){
return "person1 : toString";
}
};
var person2 = {
toLocaleString : function(){
return "person2 : toLocaleString";
},
toString : function(){
return "person2 : toString";
}
};
var people = [person1,person2];
alert(people); //person1 : toString,person2 : toString
alert(people.toString()); //person1 : toString,person2 : toString
alert(people.toLocaleString()); //person1 : toLocaleString,person2 : toLocaleString
数组继承的toLocaleString()、toString()和valueOf()方法,在默认情况下都会以逗号分隔的字符串的形式返回数组项。而如果使用join()方法,则可以使用不同的分隔符来构建这个字符串。
var colors = ['red','blue','yellow'];
alert(colors.join(',')); //red,blue,yellow
alert(colors.join('||')); //red||blue||yellow
注意:如果数组中的某一项的值是null或者undefined,那么该值在join()、toString()、toLocaleString()和valueOf()方法返回的结果中以空字符串表示。
栈方法
JavScript数组也提供了一种让数组的行为类似于其他数据结构的方法。具体来说,数组可以表现得就像栈一样,后者是一种可以限制插入和删除项的数据结构。栈是一种后进先出后进先出的数据结构。而栈中项的插入(叫做推入)和移除(叫做弹出),只发生在一个位置——栈的顶部。JavaScript提供了push()和pop()方法,以便实现类似的栈行为。
push()方法可以接收任意数量的参数,把它们逐个添加到数组末尾,并返回修改后数组的长度。而pop()方法则从数组末尾移除最后一项,减少数组的length值,然后返回移除的项。
var colors = new Array(); //创建一个数组
var count = colors.push('red','blue'); //推入两项
alert(count); //2
count = colors.push('yellow'); //再推入一项
alert(count); //3
var item = colors.pop(); //取得最后一项
alert(item); //yellow
alert(colors.length); //2
队列方法
队列数据结构的访问规则是先进先出。队列在列表的末端添加项,从列表的前端移除项。由于push()是向数组末端添加项的方法,因此要模拟队列只需一个从数组前端取得项的方法。实现这一操作的数组方法就是shift(),它能够移除数组中的第一个项并返回该项,同时将数组长度减1。结合使用shift()和push()方法,可以像使用队列一样使用数组:
var colors = new Array(); //创建一个数组
var count = colors.push('red','blue'); //推入两项
alert(count); //2
count = colors.push('yellow'); //再推入一项
alert(count); //3
var item = colors.shift(); //取得第一项
alert(item); //red
alert(colors.length); //2
JavaScript还为数组提供了一个unshift()方法。顾名思义,unshift()与shift()的用途相反:它能在数组前端添加任意个项并返回新数组的长度。因此,同时使用unshift()和pop()方法,可以从反方向来模拟队列,即在数组的前端添加项,从数组的末端移除项,例如:
var colors = new Array(); //创建一个数组
var count = colors.unshift('red','blue'); //推入两项
alert(count); //2
count = colors.unshift('yellow'); //再推入一项
alert(count); //3
var item = colors.pop(); //取得第一项
alert(item); //blue
alert(colors.length); //2
注意:IE对JavaScript的实现中存在一个偏差,其unshift()方法总是返回undefined而不是数组的新长度。
重排序方法
数组中已经存在两个可以直接用来重排序的方法:reverse()和sort(),reverse()方法会反转数组项的顺序。
var values = [1,2,3,4,5];
values.reverse();
alert(values); //5,4,3,2,1
在默认情况下,sort()方法按升序排列数组项——即最小的值位于最前面,最大的值排在最后面。为了实现排序,sort()方法会调用每个数组项的toString()转型方法,然后比较得到的字符串,以确定如何排序。即使数组中的每一项都是数值,sort()方法比较的也是字符串,如下所示:
var values = [0,1,5,10,15];
values.sort();
alert(values); //0,1,10,15,5
可见,即使例子中值的顺序没有问题,但sort()方法也会根据测试字符串的结果改变原来的顺序。因为数值5虽然小于10,但在进行字符串比较时,"10"则位于"5"的前面。因此sort()方法可以接收一个比较函数作为参数,以便我们指定哪个值位于哪个值的前面。
function compare(value1,value2){
if(value1 < value2){
return 1;
} elseif(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;
}
操作方法
JavaScript对操作数组提供了很多方法。其中,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()方法返回从该参数指定位置开始到当前数组末尾的所有项。如果有两个参数,该方法返回起始和结束位置之前的项——但不包括结束位置的项。
var colors = ['red','green','blue','yellow','black','brown'];
var colors2 = colors.slice(1);
var colors3 = colors.slice(1,4);
alert(colors2); //green,blue,yellow,black,brown
alert(colors3); //green,blue,yellow
下面我们来介绍splice()方法,这个方法恐怕要算是最强大的数组方法了,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'。
var colors = ['red','green','blue'];
var removed = colors.splice(0,1); //删除第一项
alert(colors); //green,blue
alert(removed); //red
removed = colors.splice(1,0,'yellow','black'); //从位置1开始插入两项
alert(colors); //green,yellow,black,blue
alert(removed); //返回一个空数组
removed = colors.splice(1,1,'red','brown'); //插入两项,删除一项
alert(colors); //green,red,brown,black,blue
alert(removed); //yellow
Date类型
JavaScript中的Date类型是在早期Java中的java.util.Date类基础上构建的。为此,Date类型使用自UTC 1970年1月1日零时开始经过的毫秒数来保存日期。在使用这种数据存储格式的条件下,Date类型保存的日期能够精确到1970年1月1日之前或之后的285 616年。
要创建一个日期对象,使用new操作符和Date构造函数即可。
var now = new Date();
在调用Date构造函数而不传递参数的情况下,新创建的对象自动获得当前日期和时间。如果想根据特定的日期和时间创建日期对象,必须传入表示该日期的毫秒数。为了简化这一计算过程,JavaScript提供了两个方法:Date.parse()和Date.UTC()。
其中,Date.parse()方法接收一个表示日期的字符串参数,然后尝试根据这个字符串返回相应日期的毫秒数。JavaScript没有定义Date.parse()应该支持哪种格式,因此这个方法的行为因实现而异,而且通常是因地区而异。将地区设置为美国的浏览器通常都接受下列日期格式:
● "月/日/年",如:6/13/2204
● "英文月名日,年",如:January 12,2004
● "英文星期几英文月名日年时:分:秒时区",如:Tue May 25 2004 00:00:00 GMT-0700
例如,要为2004年5月25日创建一个日期对象,可以使用下面的代码:
var someDate = new Date(Date.parse("May 25 , 2004"));
如果传入Date.parse()方法的字符串不能表示日期,那么它会返回NaN。实际上,如果直接将表示日期的字符串传递给Date构造函数,也会在后台调用Date.parse()。换句话说,下面的代码与前面的例子是等价的:
var someDate = new Date('May 25 , 2004');
Date.UTC()方法同样也返回表示日期的毫秒数,但它与Date.parse()在构建值时使用不同的信息。Date.UTC()的参数分别是年份、基于0的月份(一月是0,二月是1,以此类推)。月中的哪一天(1到31)、小时数(0到23)、分钟、秒以及毫秒数。在这些参数中,只有前两个参数(年和月)是必需的。如果没有提供月中的天数,则假设天数为1;如果省略其他参数,则统统假设为0。
//GMT时间2000年1月1日零时
var y2k = new Date(Date.UTC(2000, 0));
//GMT时间2005年5月5日下午5:55:55
var allFives = new Date(Date.UTC(2005,4,5,17,55,55));
如同模仿Date.parse()一样,Date构造函数也会模仿Date.UTC(),但有一点明显不同:日期和时间都基于本地时区而非GMT来创建的。可以将前面的例子重写如下:
//本地时间2000年1月1日零时
var y2k = new Date(2000,0);
//本地时间2005年5月5日下午5:55:55
var allFives = new Date(2005,4,5,17,55,55);
Date类型还有一些专门用于将日期格式化为字符串的方法,这些方法如下:
● toDateString()——以特定于实现的格式显示星期几、月、日和年
● toTimeString()——以特定于实现的格式显示时、分、秒和时区
● toLocaleDateString()——以特定于地区的格式显示星期几、月、日和年
● toLocaleTimeString()——以特定于实现的格式显示时、分、秒
● toUTCString()——以特定于实现的格式完整的UTC日期
以上这些字符串格式方法的输出也是因浏览器而异的,因此没有哪一个方法能够用来在用户界面中显示一致的日期信息。
以下是Date类型的所有方法:
方法 | 描述 |
Date() | 返回当日的日期和时间。 |
getDate() | 从 Date 对象返回一个月中的某一天 (1 ~ 31)。 |
getDay() | 从 Date 对象返回一周中的某一天 (0 ~ 6)。 |
getMonth() | 从 Date 对象返回月份 (0 ~ 11)。 |
getFullYear() | 从 Date 对象以四位数字返回年份。 |
getYear() | 请使用 getFullYear() 方法代替。 |
getHours() | 返回 Date 对象的小时 (0 ~ 23)。 |
getMinutes() | 返回 Date 对象的分钟 (0 ~ 59)。 |
getSeconds() | 返回 Date 对象的秒数 (0 ~ 59)。 |
getMilliseconds() | 返回 Date 对象的毫秒(0 ~ 999)。 |
getTime() | 返回 1970 年 1 月 1 日至今的毫秒数。 |
getTimezoneOffset() | 返回本地时间与格林威治标准时间 (GMT) 的分钟差。 |
getUTCDate() | 根据世界时从 Date 对象返回月中的一天 (1 ~ 31)。 |
getUTCDay() | 根据世界时从 Date 对象返回周中的一天 (0 ~ 6)。 |
getUTCMonth() | 根据世界时从 Date 对象返回月份 (0 ~ 11)。 |
getUTCFullYear() | 根据世界时从 Date 对象返回四位数的年份。 |
getUTCHours() | 根据世界时返回 Date 对象的小时 (0 ~ 23)。 |
getUTCMinutes() | 根据世界时返回 Date 对象的分钟 (0 ~ 59)。 |
getUTCSeconds() | 根据世界时返回 Date 对象的秒钟 (0 ~ 59)。 |
getUTCMilliseconds() | 根据世界时返回 Date 对象的毫秒(0 ~ 999)。 |
parse() | 返回1970年1月1日午夜到指定日期(字符串)的毫秒数。 |
setDate() | 设置 Date 对象中月的某一天 (1 ~ 31)。 |
setMonth() | 设置 Date 对象中月份 (0 ~ 11)。 |
setFullYear() | 设置 Date 对象中的年份(四位数字)。 |
setYear() | 请使用 setFullYear() 方法代替。 |
setHours() | 设置 Date 对象中的小时 (0 ~ 23)。 |
setMinutes() | 设置 Date 对象中的分钟 (0 ~ 59)。 |
setSeconds() | 设置 Date 对象中的秒钟 (0 ~ 59)。 |
setMilliseconds() | 设置 Date 对象中的毫秒 (0 ~ 999)。 |
setTime() | 以毫秒设置 Date 对象。 |
setUTCDate() | 根据世界时设置 Date 对象中月份的一天 (1 ~ 31)。 |
setUTCMonth() | 根据世界时设置 Date 对象中的月份 (0 ~ 11)。 |
setUTCFullYear() | 根据世界时设置 Date 对象中的年份(四位数字)。 |
setUTCHours() | 根据世界时设置 Date 对象中的小时 (0 ~ 23)。 |
setUTCMinutes() | 根据世界时设置 Date 对象中的分钟 (0 ~ 59)。 |
setUTCSeconds() | 根据世界时设置 Date 对象中的秒钟 (0 ~ 59)。 |
setUTCMilliseconds() | 根据世界时设置 Date 对象中的毫秒 (0 ~ 999)。 |
toSource() | 返回该对象的源代码。 |
toString() | 把 Date 对象转换为字符串。 |
toTimeString() | 把 Date 对象的时间部分转换为字符串。 |
toDateString() | 把 Date 对象的日期部分转换为字符串。 |
toGMTString() | 请使用 toUTCString() 方法代替。 |
toUTCString() | 根据世界时,把 Date 对象转换为字符串。 |
toLocaleString() | 根据本地时间格式,把 Date 对象转换为字符串。 |
toLocaleTimeString() | 根据本地时间格式,把 Date 对象的时间部分转换为字符串。 |
toLocaleDateString() | 根据本地时间格式,把 Date 对象的日期部分转换为字符串。 |
UTC() | 根据世界时返回 1970 年 1 月 1 日 到指定日期的毫秒数。 |
valueOf() | 返回 Date 对象的原始值。 |
Function类型
JavaScript中什么最有意思,我想那莫过于函数了——而有意思的根源,则在于函数实际上时对象。每个函数都是Function类型的实例,而且都与其他引用类型一样具有属性和方法。由于函数是对象,因此函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定。
函数通常是使用函数声明语法定义的,如下面例子所示:
function sum(num1,num2)
{
return num1 + num2;
}
这与下面使用函数表达式定义函数的方式几乎相差无几:
var sun = function(num1,num2){
return num1 + num2;
};
以上代码定义了变量sum并将其初始化为一个函数。function关键字后面没有函数名,这是因为在使用函数表达式定义函数时,没有必要使用函数名——通过变量sum即可引用函数。另外,还要注意函数末尾有一个分号,就像声明其他变量时一样。
最后一种定义函数的方式是使用Function构造函数。Function构造函数可以接收任意数量的参数,但最后一个参数始终都被看成是函数体,而前面的参数则枚举出了新函数的参数。
var sum = 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
注意:使用不带括号的函数名是访问函数指针,而非调用函数。
函数声明与函数表达式
目前为止,我们一直没有对函数声明和函数表达式加以区别。而实际上,解析器在向执行环境中加载数据时,对函数声明和函数表达式并非一视同仁。解析器会率先读取函数声明,并使其在执行任何代码之前可用(可以访问);至于函数表达式,则必须等到解析器执行到它所在的代码行,才会真正被解释执行。
alert(sum(10,10));
function sum(num1,num2)
{
return num1 + num2;
}
以上代码完全可以正常运行。因为在代码开始执行之前,解析器就已经读取函数声明并将其添加到执行环境中了。如果像下面例子所示,把上面的函数声明改为变量初始化方式,就会在执行期间导致错误。
alert(sum(10,10));
var sum = function(num1,num2)
{
return num1 + num2;
}
作为值的函数
因为JavaScript中的函数名本身就是变量,所以函数也可以作为值来使用。也就是说,不仅可以像传递参数一样把一个函数传递给另一个函数,而且可以将一个函数作为另一个函数的结果返回。
function callSomeFunction(someFunction , someArgument)
{
return someFunction(someArgument);
}
这个函数接受两个参数,第一个参数应该是一个函数,第二个参数应该是要传递给该函数的一个值。然后,就可以像下面的例子一样传递函数了:
function add(num)
{
return num + 10;
}
var result = callSomeFunction(add,10);
alert(result); //20
当然,可以从一个函数中返回另一个函数,而且这也是极为有用的一种技术。
function createSumFunction()
{
returnfunction(num1,num2){
return num1 + num2;
};
}
var sumFunction = createSumFunction();
alert(sumFunction(10,10)); //20
函数内部属性
在函数内部,有两个特殊的对象:arguments和this。其中,arguments是一个类数组对象,包含着传入函数中的所有参数,而且可以使用length属性来确定传递进来多少个参数。
function sayHi()
{
alert(arguments.length); //2
alert(arguments[0] + ',' + arguments[1]); //hello,world
}
sayHi('hello','world');
虽然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是函数在执行时所处的作用域(当在网页的全局作用域中调用函数时,this对象引用的就是window)。看下面的例子:
window.color = 'red';
var o = {color:'blue'};
function sayColor()
{
alert(this.color);
}
sayColor(); //red
o.sayColor = sayColor;
o.sayColor(); //blue
上面这个函数sayColor()是在全局作用域中定义的,它引用了this对象。由于在调用函数之前,this的值并不确定,因此this可能会在代码执行过程中引用不同的对象。当在全局作用域中调用sayColor()时,this引用的是全局对象 window;换句话说,对this.color求值会转换成对window.color求值,于是结果就是'red'。而当把这个函数赋给对象o并调用o.sayColor()时,this引用的是对象o,因此对this.color求值会转换成对o.color求值,结果就是'blue'。
函数属性和方法
因为JavScript中的函数是对象,因此函数也有属性和方法。每个函数都包含两个属性:length和prototype。其中,length属性表示函数希望接收的命名参数的个数。
function sayName(name)
{
alert(name);
}
function sayHi()
{
alert('hi');
}
alert(sayName.length); //1
alert(sayHi.length); //0
在JavaScript中最耐人寻味的就要数prototype属性了。对于引用类型而言,prototype是保存它们所有实例方法的真正所在。诸如toString()和valueOf()等方法实际上都是保存在prototype名下,只不过是通过各自对象的实例访问罢了。在创建自定义引用类型以及实现继承时,prototype属性的作用是极为重要的(这里就不对prototype属性做详细介绍了)。
每个函数都包含两个非继承而来的方法:apply()和call()。这两个方法的用途是在特定的作用域中调用函数,实际上等于设置函数体内this对象的值。首先,apply()方法接受两个参数:一个是在其中运行函数的作用域,另一个是参数数组。其中,第二个参数可以是Array的实例,也可以是arguments对象。例如:
function sum(num1,num2)
{
return num1 + num2;
}
function callSum1(num1,num2)
{
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
在上面例子中,callSum1()在执行sum()函数时传入了this作为作用域(因为是在全局作用域中调用的,所以传入的就是window对象)和arguments对象。而callSum2同样也调用了sum()函数,但它传入的则是this和一个参数数组。
call()方法与apply()方法的作用相同,它们的区别仅在于接收参数的方式不同。对于call()方法而言,第一个参数是作用域没有变化,变化的只是其余的参数都是直接传递给函数的。
function callSum2(num1,num2)
{
return sum.call(this,num1,num2);
}
alert(callSum2(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
在上面的例子中,当运行sayColor.call(o)时,函数的执行环境就不一样了,因为此时函数体内的this对象指向了o,于是结果显示"blue"。
注意:每个函数都有一个非标准的caller属性,该属性指向调用当前函数的函数。一般是在一个函数的内部,通过arguments.callee.caller来实现对调用栈的追溯。目前,IE、FireFox、Chrome都支持该属性,但建议将该属性用于调试目的。
内置对象
JavaScript中有两个内置对象:Global和Math。
Global对象
Global(全局)对象可以说是JavaScript中最特别的一个对象了,因为不管你从什么角度上看,这个对象都是不存在的。JavaScript中的Global对象在某种意义上是作为一个终极的"兜底儿对象"来定义的。换句话说,不属于任何其他对象的属性和方法,最终都是它的属性和方法。事实上,没有全局变量或全局函数;所有在全局作用域定义的属性和函数,都是Global对象的属性。诸如isNaN()、parseInt()以及parseFloat(),实际上全都是Global对象的方法,Global对象还包含其他一些方法。
URI编码方法
Global对象的encodeURI()和encodeURIComponent()方法可以对URI进行编码,以便发送给浏览器。有效的URI中不能包含某些字符,例如空格。而这两个URI编码方法就可以对URI进行编码,它们用特殊的UTF-8编码替换所有无效的字符,从而让浏览器能够接受和理解。
其中,encodeURI()主要用于整个URI(例如:http://www.test.com/test value.html),而encodeURIComponent()主要用于对URI中的某一段(例如前面URI中的test value.html)进行编码。它们主要区别在于,encodeURI()不会对本身属于URI的特殊字符进行编码,例如冒号、正斜杠、问好和井号;而encodeURIComponent()则会对它发现的任何非标准字符进行编码。
var uri = "http://www.test.com/test value.html#start";
//"http://www.test.com/test%20value.html#start"
alert(encodeURI(uri));
//"http%3A%2F%2Fwww.test.com%2Ftest%20value.html%23start"
alert(encodeURIComponent(uri));
一般来说,使用encodeURIComponent()方法的时候要比使用encodeURI()更多,因为在实践中更常见的是对查询字符串参数而不是对基础URI进行编码。
与encodeURI()和encodeURIComponent()方法对应的两个方法分别是decodeURI()和decodeURIComponent()。其中,decodeURI()只能对encodeURI()替换的字符进行解码,同样,decodeURIComponent()只能对encodeURIComponent()替换的字符进行解码。
eval()方法
eval()方法大概是JavaScript中最强大的一个方法了,eval()方法就像是一个完整的JavaScript解析器,它只接受一个参数,即要执行的字符串。看下面的例子:
eval("alert('hi')");
这行代码的作用等价于下面这行代码:
alert('hi');
当解析器发现代码中调用eval()方法时,它会将传入的参数当做实际的JavaScript语句来解析,然后把执行结果插入到原位置。通过eval()执行的代码被认为是包含该次调用的执行环境的一部分,因此被执行的代码具有与该执行环境相同的作用域链。这意味着通过eval()执行的代码可以引用在包含环境中定义的变量,例如:
var msg = 'hello world';
eval('alert(msg)'); //hello world
可见,变量msg是在eval()调用的环境之外定义的,但其中调用的alert()仍然能够显示"hello world"。这是因为上面第二行代码最终被替换成了一行真正的代码。同样地,我们也可以在eval()调用中定义一个函数,然后再在该调用的外部代码中引用这个函数:
eval("function sayHi(){alert('hi')}");
sayHi();
注意:能够解释代码字符串的能力非常强大,但也非常危险。因此在使用eval()时必须极为谨慎,特别是在用它执行用户输入数据的情况下。否则,可能会有恶意用户输入威胁你的站点或应用程序安全的代码(即所谓的代码注入)。
Math对象
与我们在JavaScript直接编写的计算功能相比,Math对象提供的计算功能执行起来要快得多。Math对象还提供了辅助完成这些计算的属性。
属性 | 描述 |
E | 返回算术常量 e,即自然对数的底数(约等于2.718)。 |
LN2 | 返回 2 的自然对数(约等于0.693)。 |
LN10 | 返回 10 的自然对数(约等于2.302)。 |
LOG2E | 返回以 2 为底的 e 的对数(约等于 1.414)。 |
LOG10E | 返回以 10 为底的 e 的对数(约等于0.434)。 |
PI | 返回圆周率(约等于3.14159)。 |
SQRT1_2 | 返回返回 2 的平方根的倒数(约等于 0.707)。 |
SQRT2 | 返回 2 的平方根(约等于 1.414)。 |
Math对象包含的方法如下:
方法 | 描述 |
abs(x) | 返回数的绝对值。 |
acos(x) | 返回数的反余弦值。 |
asin(x) | 返回数的反正弦值。 |
atan(x) | 以介于 -PI/2 与 PI/2 弧度之间的数值来返回 x 的反正切值。 |
atan2(y,x) | 返回从 x 轴到点 (x,y) 的角度(介于 -PI/2 与 PI/2 弧度之间)。 |
ceil(x) | 对数进行上舍入。 |
cos(x) | 返回数的余弦。 |
exp(x) | 返回 e 的指数。 |
floor(x) | 对数进行下舍入。 |
log(x) | 返回数的自然对数(底为e)。 |
max(x,y) | 返回 x 和 y 中的最高值。 |
min(x,y) | 返回 x 和 y 中的最低值。 |
pow(x,y) | 返回 x 的 y 次幂。 |
random() | 返回 0 ~ 1 之间的随机数。 |
round(x) | 把数四舍五入为最接近的整数。 |
sin(x) | 返回数的正弦。 |
sqrt(x) | 返回数的平方根。 |
tan(x) | 返回角的正切。 |
toSource() | 返回该对象的源代码。 |
valueOf() | 返回 Math 对象的原始值。 |