Function类型
ECMAScript中的函数实际上也是对象。每个函数都是Function
类型的实例,并且与其它引用类型一样也具有属性和方法。所以,函数名称实际上也是一个指向函数对象的指针。
这也就不难理解函数的另一种定义方法,实际就是在声明一个变量:
var sum = function(num1, num2) {
return num1 + num2;
}
实际上,函数也能使用构造方法来定义,不过这种方法是不推荐使用的:
// 最后一个参数会被当成函数体
var sum = new Function("num1", "num2", "return num1 + num2");
没有重载(深入理解)
将函数名想象为指针,也就可以理解为什么ECMAScript当中没有函数重载的概念。
function addSomeNumber(num) { return num + 100; }
function addSomeNumber(num1, num2) { return num1 + 200; }
var result = addSomeNumber(100); // 300
// --> 等价于 <--
var addSomeNumber = function(num) { return num + 100; }
addSomeNumber = function(num1, num2) { return num1 + 200; }
var result = addSomeNumber(100); // 300
可以看到,当声明两个同名函数时,实际上是第一个函数的变量被后面的所覆盖了,也就无法构成重载。
函数声明与函数表达式
在解析器向执行环境加载数据时,对待函数声明和函数表达式是有区别的。解析器会率先读取函数声明,并使其可以在执行任何代码之前可用,而对于函数表达式,则必须等到解析器执行到它所在的代码行,才会真正被执行。
// 运行正确
alert(sum(10,10));
function sum(num1, num2){
return num1 + num2;
}
// 运行出错
alert(sum(10,10));
var sum = function(num1, num2){
return num1 + num2;
};
由于第一段代码使用了函数声明,所以解析器会在运行代码之前将其添加到执行环境中,所以可以正确运行。而第二段代码因为是函数表达式,所以在执行第一句的时候,实际上sum
函数还未被定义,导致运行出错。
作为值的函数
因为ECMAScript当中的函数名本身就是变量,所以函数也可以被当成参数来使用,也可以作为返回值从一个函数中返回。
函数内部属性
在函数的内部,有两个特殊的属性:arguments
和this
,这两个属性本身也都是引用类型。
arguments
除了保存函数参数的作用,还有一个callee
的属性,该属性是一个指针,指向拥有这个arguments
对象的函数。
function factorial(num){
if (num <=1) {
return 1;
} else {
return num * factorial(num-1)
}
}
// <-- 等价于 -->
function factorial(num){
if (num <=1) {
return 1;
} else {
return num * arguments.callee(num-1)
}
}
this
引用的是执行该函数的环境对象。
window.color = "red";
var o = { color: "blue" };
function sayColor(){
alert(this.color);
}
sayColor(); //"red"
o.sayColor = sayColor;
o.sayColor(); //"blue"
当在全局作用域中调用函数时,this
引用的是全局对象window
。而使用对象调用时,则this
指向的是该对象本身。
关于this
以后还会进行详细的讨论。
ECMAScript 5中还定义了另一个函数属性的对象:caller
,这个属性保存了调用当前函数的函数的引用,如果是在全局使用域内,则它的值为null
。
函数属性和方法
因为函数也是对象,所以函数也有属性和方法。每个函数都有两个属性: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中面向对象一个十分重要的属性,关于这个属性之后会更加详细地讨论。
除了上面两个属性,每个函数还有两个非继承而来的方法:apply()
和call()
,使用这两个方法可以设置函数内this
对象的值。
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()
方法与apply()
方法类似,区别只在于第二个参数不同,对于call()
方法,所以参数都是直接指定,而不是传递数组。
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
ECMAScript 5还定义了一个bind()
方法,这个方法会创建一个函数的实例,其this
值会被绑定到传给bind()
函数的值。
window.color = "red";
var o = { color: "blue" };
function sayColor(){
alert(this.color);
}
var objectSayColor = sayColor.bind(o);
objectSayColor(); //blue
而对于toString()
、toLocaleString()
和valueOf()
方法,都会返回函数的代码。
基本包装类型
我们之前说过,只有引用类型的数据才能添加属性,但是我们却经常对字符串调用各种方法。这是怎么回事呢?实际上,每当我们读取一个基本类型值的时候,后台就会创建一个对应的基本包装类型的对象,从而我们能够调用一些方法来操作这些数据。
基本包装类型只存在于一行代码的执行瞬间,然后立即被销毁。这也就解释了为什么我们不能为基本类型添加属性和方法,因为对基本包装类型添加的属性,在下一行代码执行的时候就被销毁了。
我们应该避免显式地创建基本包装类型对象。
Boolean类型
Boolean
类型是与布尔值对应的引用类型。Boolean
类型的实例重写了valueOf()
方法,返回基本类型值的true
或false
。重写了toString()
方法,返回字符串true
和false
。
Number类型
Number
是与数字值对应的引用类型。Number
的valueOf()
方法也会返回基本类型的数值,而toString()
和toLocaleString()
方法会返回数值对应的字符串。
Number
类型还提供了一些用于数值格式化为字符串的方法。
toFixed()
方法会按照指定的小数返回数值的字符串表示:
var num = 10;
alert(num.toFixed(2)); //"10.00"
toExponential()
方法会返回以指数表示法表示的数值的字符串形式。
var num = 10;
alert(num.toExponential(1)); //"1.0e+1"
对于一个数值来说,toPrecision()
方法可能会返回固定大小(fixed)格式,也可能返回指数格式(exponential),具体看哪种格式最合适。
var num = 99;
alert(num.toPrecision(1)); //"1e+2"
alert(num.toPrecision(2)); //"99"
alert(num.toPrecision(3)); //"99.0"
String类型
String
类型是字符串的对象包装类型。对于String
类型的对象,三个继承的方法都直接返回对象所表示的基本字符串值。String
类型每个实例都有一个length
属性,表示字符串中的字符个数。要注意的是,即使字符串中包含双字节字符,每个字符也仍然算一个字符。
String
类型提供了很多方法,以提供字符串的操作和解析。
字符方法
charAt()
和charCodeAt()
用于访问字符串中的特定字符。这两个方法都接收一个参数,即基于0的字符位置。charAt()
以字符形式返回给定位置的字符,而charCodeAt()
会以字符编码的形式返回。
var stringValue = "hello world";
alert(stringValue.charAt(1)); //"e"
alert(stringValue.charCodeAt(1)); // 输出"101"
在ECMAScript 5当中,还可以使用方括号加数字索引来访问字符串,与数组的取值方法类似。
字符串操作方法
concat()
方法用于将一或多个字符串拼接起来,返回拼接得到的新字符串。
var stringValue = "hello ";
var result = stringValue.concat("world", "!");
alert(result); //"hello world!"
alert(stringValue); //"hello"
实际上,在大多数情况下,字符串拼接使用更多的还是加号操作符(+)。
ECMAScript还提供了三个用于获取子字符串的方法: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"
在传递参数为负值的情况下,slice() 方法会将传入的负值与字符串的长度相加, substr() 方法将负的第一个参数加上字符串的长度,而将负的第二个参数转换为 0。最后, substring() 方法会把所有负值参数都转换为 0。
var stringValue = "hello world";
alert(stringValue.slice(-3)); //"rld"
alert(stringValue.substring(-3)); //"hello world"
alert(stringValue.substr(-3)); //"rld"
alert(stringValue.slice(3, -4)); //"lo w"
alert(stringValue.substring(3, -4)); //"hel"
alert(stringValue.substr(3, -4)); //"" (空字符串)
字符串位置方法
indexOf()
和lastIndexOf()
可以用于从字符串中查找子字符串,并返回子字符串的位置(如果没有找到,则返回-1)。两个方法区别只在于搜索的起始位置。并且两个方法都可以接受第二个参数,用于指定从哪个位置可以搜索。
trim()方法
ECMAScript 5为所有字符串字义了trim()
方法。这个方法会创建一个字符串副本,删除前置和后缀的所有空格,然后返回结果。
var stringValue = " hello world ";
var trimmedStringValue = stringValue.trim();
alert(stringValue); //" hello world "
alert(trimmedStringValue); //"hello world"
字符串大小写转换方法
toLowerCase()
、toLocalceLowerCase()
、toUpperCase()
和toLocaleUpperCase()
用于转换字符串的大小写。
字符串模式匹配方法
String
类型定义了几个用于在字符串中匹配模式的方法。
match()
与RegExp
的exec()
方法本质上是一样的。match()
方法接收一个参数,要么是正则字面量,或一个RegExp
对象。
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()
方法用于在字符串中查找符合特定模式的子字符串,并返回子字符串的位置,它接收的参数与match()
一样。
var text = "cat, bat, sat, fat";
var pos = text.search(/at/);
alert(pos); //1
String类型还提供了一个用于替换子字符串。这个方法可以接收两个参数:第一个参数可以是一个RegExp对象或者一个字符串(不会被转换为正则表达式),第二个参数可以是一个字符串或者一个函数。如果第一个参数是字符串,则只会替换第一个匹配的子字符串。
var text = "cat, bat, sat, fat";
var result = text.replace("at", "ond");
alert(result); //"cond, bat, sat, fat"
result = text.replace(/at/g, "ond");
alert(result); //"cond, bond, sond, fond"
第二个参数如果指定一个函数,传递给函数的参数依次是模式的匹配项、各个捕获组的匹配项、匹配项的位置和原始字符串。
最后一个与模式匹配有关的方法是split()
,这个方法可以基本指定的分隔符将一个字符串分割成多个子字符串,并将结果放在一个数组中。分隔符可以是一个字符串(不会被转化为正则表达式),也可以是一个RegExp
对象。这个方法也可以接收第二个参数,用于指定数组的大小。
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(/[^\,]+/); //["", ",", ",", ",", ""]
localeCompare()方法
这个方法比较两个字符串,根据字符编码进行比较返回正数、负数或者0。
fromCharCode()方法
String
构造函数本身还有一个静态方法:fromCharCode()
,这个方法接收一个或多个字符编码,然后将它们转换成一个字符串。
alert(String.fromCharCode(104, 101, 108, 108, 111)); //"hello"
单体内置对象
除了上面介绍的内置对象,ECMA-262还定义了两个单体内置对象:Global
和Math
。
Global对象
所以在全局作用域中定义的属性和函数,都是Global对象的属性。在web浏览器当中,这个对象被当作window对象的一部分来实现。Global
对象还包含其他一些方法:
URI编码方法
encodeURI()
和encodeURIComponent()
方法可以对URI进行编码,它们会以UTF-8编码替换掉无效的字符。其中,encodeURI()
用于整个URI,而encodeURIComponent()
只用于URI中某一段进行编码。与其对应的两个方法是decodeURI()
和decodeURIComponent()
。
eval()方法
eval()
方法可以将传入的字符串当作ECMAScript语句来执行。通过eval()
执行的代码被认为是包含该次调用的执行环境的一部分,因为被执行的代码具有与该执行环境相同的作用域链。
eval("function sayHi() { alert('hi'); }");
sayHi();
eval("var msg = 'hello world'; ");
alert(msg); //"hello world"
Math对象
ECMAScript对保存数学公式和信息提供了一个公共位置,即Math对象。
Math对象的属性
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的平方根 |
min()和max()方法
这两个方法可以接收任意多个数值参数,并返回所有值的最大值或最小值。
var max = Math.max(3, 54, 32, 16);
alert(max); //54
var min = Math.min(3, 54, 32, 16);
alert(min); //3
舍入方法
Math.ceil()
向上取整,Math.floor()
向下取整,Math.round()
执行四舍五入方法。
alert(Math.ceil(25.9)); //26
alert(Math.ceil(25.5)); //26
alert(Math.ceil(25.1)); //26
alert(Math.round(25.9)); //26
alert(Math.round(25.5)); //26
alert(Math.round(25.1)); //25
alert(Math.floor(25.9)); //25
alert(Math.floor(25.5)); //25
alert(Math.floor(25.1)); //25
random()方法
Math.random()
方法返回大于等于0小于1的一个随机数。可以利用这个方法从某个整数范围内随机选择一个值。
值 = Math.floor(Math.random() * 可能值的总数 + 第一个可能的值)
// 通用函数
function selectFrom(lowerValue, upperValue) {
var choices = upperValue - lowerValue + 1;
return Math.floor(Math.random() * choices + lowerValue);
}
其他方法
Math
对象中还包含了其他一些与计算相关的方法,在此就不详细记录,遇到的时候再查阅资料就可以了。