您的位置:首页 - Javascript
JS函数类型知识系统
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(num) { return num + 200; }
var result = addSomeNumber(100); //

// --> 等价于 <--

var addSomeNumber = function(num) { return num + 100; }
addSomeNumber = function(num) { return num + 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对象中还包含了其他一些与计算相关的方法,在此就不详细记录,遇到的时候再查阅资料就可以了。
本站导航
相关文章
柯乐义 Copyright© keleyi.com