函数
对于学习任何程序设计语言来说,掌握函数都是非常重要的。对于Javascript更是如此,因为该语言中的很多功能,其灵活性以及表达能力都来自函数。例如:绝大部分语言都有自己专门的面向对象的语法,而Javascript没有:它是通过函数来实现面向对象特性的。我们首先要掌握以下内容:
- 如何定义和使用函数
- 如何向函数传递参数
- 了解我们可以“免费”调用那些预定义函数
- 了解Javascript中的变量作用域
- 理解“函数也是数据”的概念,并将函数视为一种特殊的数据类型
理解以上内容之后,我们就可以深入学习第二部分,也就可以看到一些有趣的函数应用:
- 匿名函数的调用
- 回调函数
- 即时(自调)函数
- 内嵌函数(在函数内部定义的函数)
- 以函数为返回值的函数
- 能重定义自身的函数
- 闭包
什么是函数
所谓函数,本质上是一种代码的分组形式。我们可以通过这种形式赋予某组代码一个名字,以便以后的调用。下面我们来示范一下函数的声明:
function sum(a,b) {
var c = a + b;
return c;
}
一般来说,函数声明通常由以下及部分组成:
- 关键词 function
- 函数名称,即这里的sum
- 函数所需的参数,即这里的a,b。一个函数通常都具有0个或多个参数。 参数之间用逗号分隔
- 函数需要执行的代码块,我们称之为函数体
- return子句。一个函数通常只能有一个返回值,如果某个函数没有显式的返回值就会默认它的返回值为
undefined
调用函数
如果我们需要使用一个函数,就必须去调用它。调用的方式很简单,只需要在函数名的后面加一对用以传递参数的括号即可。另外,对于“调用(to call)”这种操作,我们有时也可以将其称之为“请求(to invoke)”某个函数。
现在让我们来调用以下sum()
函数。。先将两个参数传递给该函数,然后再将函数的返回值付给变量result
。具体如下:
var result = sum(1,2)
console.log(result) //3
参数
在定义一个函数的同时,我们往往会设置该函数所需的调用参数。当然,你也可以不给它们设定参数,但如果你设定了,而又在调用时忘了传递相关的参数值,Javascript引擎就会自动将其设定为undefined
。例如在下面这个调用中,函数返回的是NaN
,因为这里试图将1
与undefined
相加。
sum(1)
// NaN
从技术角度来说,参数又可以分为形参(形式参数)和实参(实际参数)两种。但我们往往并不需要区分它们。形参是指定义函数时所用的那些参数,而实参则指的是在调用函数时所传递的那些参数。可以考虑以下的例子:
function sum(a,b){
retrun a + b
}
sum(1,2)
在这里,a,b是形参,而1,2是实参。
对于那些已经传递进来的参数,JS是来者不拒的。但是,即使我们向sum()传递再多的参数,多余的那部分参数也只会被默默的忽略掉。
sum(1,2,3,4,5)
// 3
实际上,我们还可以创建一些在参数数量方面更为灵活的函数。这得益于函数内部的arguments
变量,该变量为内建变量,每个函数中都能调用。它能返回函数所接收的所有参数。
function args() {
return arguments;
}
args();
// []
args(1,2,3,4,true,'ninja');
//[1,2,3,4,true,"ninja"]
通过变量arguments,我们可以进一步完善sum()函数的功能,使之能对任意的参数执行求和运算。
function sumOnSteroids() {
var i,
res = 0;
for(i = 0; i < arguments.length; i++){
res += arguments[i];
}
return res;
}
预定义函数
Javascript中有一组可供随时调用的内建函数。下面我们来了解一下这些函数。在这一过程中,我们会通过一系列的具体实践,来帮助你掌握这些函数的参数和返回值,以便最终实现熟练的应用。这些内建函数包括:
- parseInt();
- parseFloat();
- isNaN();
- isFinite();
- encodeURI();
- decodeURI();
- encodeURIComponent();
- decodeURIComponent();
- eval();
parseInt()
parseInt()
会试图将其收到的任何输入值(通常是字符串)转化成整数类型输出。如果转化失败则输出false
。
parseInt("123"); // 123
parseInt("abc123"); //NaN
parseInt("1abc123"); // 1
parseInt("123abc"); // 123
parseFloat()
parseFloat()函数的功能与ParseInt()基本相同,只不过它仅支持将输入值转化为十进制数。因此该函数只接收一个参数。
parseFloat("123); // 123
parseFloat("1.23"); // 1.23
parseFloat("1.23abc00"); // 1.23
parseFloat("a.bc1.23"); // NaN
与parseInt()相同的是,parseFloat()在遇到第一个异常字符时,就会放弃,无论剩余的那部分字符串是否可用。
parseFloat("a123.456"); // NaN
parseFloat("12a34.56"); // 12
//此外,parseFloat()还可以接收指数形式的数据(这点给你parseInt()不同)
parseFloat("123e-2"); // 1.23
parseFloat("1e10"); // 10000000000
parseInt("1e10"); // 1
isNaN()
通过isNaN()
,我们可以确定某个输入的值是否是一个可以参与算数运算的数字。因而,该函数也可以来检查parseInt()
和parseFloat()
的调用成功与否。
isNaN(NaN) // true
isNaN(123) // false
isNaN(1.23) // false
isNaN(parseInt('abc123')) // true
该函数也会始终试图将其所接收的输入转化成数组,例如:
isNaN("1.23") // false
isNaN("a1.23") // true
isFinite()
isFinite()可以用来检查输入是否是一个即非Infinity也非NaN的数字。
isFinite(Infinity) // false
isFinite(-Infinity) // false
isFinite(12) // true
isFinite(1e308) // true
isFinite(1e309) // false
Javascript中的最大数字为1.7976931348623157e308
URI的编码与反编码
在URL(统一资源定位符)或URI(统一资源标识符)中,有一些字符是具有特殊含义的。如果我们想“转意”这些字符,就可以去调用函数encodeURI()
或者encodeURIComponent()
。前者会返回一个可用的URL,而后者这回认为我们所传递的仅仅是URL的一部分。例如,对于下面这个查询字符串来说,这两个函数所返回的字符编码分别是:
var url = "http://www.packhub.com/scr ipt.php?q=this and that"
encodeURI(url);
//"http://www.packhub.com/scr%201ipt.php?q=this%and%that"
encodeURIComponent(url);
//"http%3A%2F%2Fwww.packhub.com%2Fscr%20ipt.php%3Fq%3Dthis%20and%20that"
encodeURI()
和encodeURIComponent()
分别都有各自对应的反编码函数decodeURI()
和decodeURIComponent()
。
另外,我们有时候还会在一些遗留代码中看到相似的编码函数和反编码函数escape()
和unescape()
,但我们并不赞成使用这些函数来执行相关的操作。它们的编码规则也不尽相同。
eval()
eval()会将其输入的字符串当作Javascript代码来执行。
eval('var ii = 2');
ii; // 2
所以,这里的eval('var ii= 2')
与表达式var ii = 2;
的执行结果是相同的。
Eval is evil(eval是魔鬼),是因为eval()是这样一种函数:
- 安全性方面——Javascript拥有的功能很强大,但这也意味着很大的确定性,如果你对放在eval()中的代码没有太多的把握,最好还是不要这样使用。
- 性能方面——它是一种由函数执行的“动态”代码,所以要比直接执行脚本要慢。