什么是函数(Function)
function sum(a,b){
return a+b;
}
其实通俗的说就是一个有名称的代码段,方便重用。
要注意的是:
1.Javascript 的函数语法,因为Javascript本身就是区分大小写的,所以function不能写作Function或FUNCTION.
2.sum是函数的名称,这个并不是必须的,等以后我们会说到。
3.return是返回,如果不写的话,函数的返回是undefined.如果要返回多个值,可以返回个数组或者对象(以后再说)
函数的调用
下面我们讲函数的基本调用。
var result = sum(1,2)
函数的参数
不好意思的说,这个才是本篇文章的重点。
实例一,参数少于 实际定义参数的数目
var result = sum(1);
结果result 为NaN,这就说明了,参数b的值为undefined,但并不会报错,这就无形中制造了bug.
实例二,参数多于 实际定义参数的数目
sum(1,2,3,4,5)
结果为3.发现多于的参数被忽略了。
实例三,没有参数的函数
function args(){return arguments;}
每个函数里都有一个默认的数组那就是arguments .它就是每个函数默认的参数为[] 。如果我们调用函数如下
args(1,2,3,4,5,6);
会发现arguments的值为[1,2,3,4,5,6]。这下容易了,我们可以改造下上面的sum方法
sum(1,2,3,4);function sum(){
var res= 0;
for(i=0;ires+=arguments[i];
}
return res;
}
Functions are data
这一篇是函数里比较重要的概念,那就是函数是一个数据。看个例子
function f(){return 1;}
var f=function(){return 1;}
这两个函数定义都是相同的。
typeof f;
f的值为"function",所以说Javascript 的 函数是个数据类型。它有比较两个重要的特点
1.它包含了代码
2.能被执行
看个例子
var sum = function(a,b){return a+b;}
var add = sum;
sum=undefined;
typeof sum;
typeof add;
add(1,2);
我们把函数sum做为一个值赋给了add,发现删除了sum不影响add的调用。所以函数也就是个正常的赋值。
匿名函数(Anonymous Functions)
Javascript中,可以不用写赋值的代码,如
"abcd" 1 [1,2,3]
这样都不会报错,这些数据叫做匿名的。同样的函数做为数据也可以是匿名的
function(a){return a}
匿名函数的作用有两个
1.可以把匿名函数做为一个参数传入到另一个函数中。
2.你可以理解运行这个匿名函数
下面就会详细讨论这两个功能的作用了。
回调函数(Callback Functions)
因为函数和其他数据一样可以被赋值,删除,拷贝等,所以也可以把函数作为参数传入到另一个函数中。
实例一
function invoke_and_add(a,b){
return a()+b();
}
function one(){
return 1;
}
function two(){
return 2;
}
invoke_and_add(one ,two);
结果为3;
再来看看匿名函数的用法。
实例二
invoke_and_add(function(){return 1;},function(){return 2;}),直接一个函数体传进去,没有函数名。
我们称,invoke_and_add为回调函数
我们用匿名函数替代了 one,two两个函数。
通过上面两个实例,回调函数的定义为:传递一个函数A到另一个函数B中,并且这个函数B执行函数A。我们就说函数A叫做回调函数。 说白了,就是被人直接调用的函数,在一个函数执行另一个函数!
如果没有名称,就叫做匿名回调函数
回调函数的作用
主要有三个
1.当函数作为参数传递的时候,不用为这个函数定义一个名称,这样的好处是减少了全局变量。
2.节省了大量的代码。
3.提高了程序的性能。
自调用函数(Self-invoking Functions)
自调用函数也是匿名函数的一种表现形式,这个函数在定义之后,直接调用。如下
(
function(){
alert('haha');
}
)()
看起来还挺怪异,不过确实很简单。
自调用函数很方便使用,可以不用定义更多的全局变量。还有个好处,就是这个函数不能被执行两遍。真是非常适合做初始化的工作。
许多著名的javascript库特别多的源码中使用了这个功能,例如本人喜欢的Jquery.
内部函数(Inner Functions)
把函数作为一个值来思考一下,既然一个值可以定义在函数中,把函数做为数据放在函数中也未尝不可。如下:
function a(param){
function b(theinput){
return theinput *2;
}
return 'The result is '+b(param);
}
也可以这么写
var a = function(param){
var b = function(theinput){
return theinput*2;
};
return 'The result is '+b(param);
};
b函数是在a函数之中的 ,也就是意味着,在a函数的外部是无法访问b函数的。所以也称之为私有函数(private function)
a(2);
a(8);
b(2);
发现b(2)是没有定义的。也就确定了它确实是私有函数。
内部函数的是使用还是有很多好处的。
1.可以有更少的全局变量。过多的使用全局变量就有可能由于命名冲突而产生过多的bugs
2.私有性,可以设计更好的接口函数供外部访问。
返回值为函数的函数(Functions that Return Functions)
在前几篇文章已经介绍了函数要有返回值,即使没有写return,函数也会返回一个undefine。
接下来看看返回值为函数的情况
function a(){
alert('a');
return function(){
alert('b');
};
}
在这个例子中,a函数执行了alert('a'),以及它返回了另一个函数b。关于返回b的调用我们可以这样来用。
var newFunc = a();
newFunc();
执行结果为 alert a和alert b
如果不想赋值调用这个函数也可以简写如下
a()();
函数的自身重写
因为函数可以返回一个函数,那就意味着可以用一个新的函数替代一个旧的函数,根据前一个例子来改进一下
a=a();
第一次运行函数a,就alert a,再次运行函数a,就alert b,这个功能对初始化非常有用。这个函数a重写了自己,避免在以后调用初始化的功能(上个例子为alert a)。
当然我们还有更简单的方法来重写a函数那就是在a函数的内部重写它,来看看代码
function a(){
alert("a")
a=function(){
alert("b");
}
}
只有在初次调用a函数才会执行 alert a 在以后的调用中,都会执行alert b
下面结合前几节看个综合的例子
var c = function(){
function a(){
alert('a')
}
function b(){
alert('b')
}
a();
return b;
}();//alert('a');
c();//alert('b');
这个例子有以下几点需要注意
1. a函数,b函数是内部函数。
2. return b 返回的是个函数的引用。
3. 子调用函数重写了函数c。
如果能明白这个例子,关于内部函数,子调用函数和返回值为函数的函数就可以都理解了。
闭包(Closures)闭包属于比较难的一部分,在学习闭包之前,先来学习以下Javascript的作用域(Scope)
作用域链(Scope Chain)
函数内部定义的变量,在函数外不能访问,或者变量定义在代码段中(如if,for中),在代码段之外也不可访问。
var a =1;
function f(){
var b=1;
return a;
}
f();//a=1
b;//b 没有定义
a 是全局变量,而b定义在函数f之中。所以:
在f函数中,a和b都可以访问。
在f函数之外,a可以访问,而b不可以
再次看个例子
var a = 1;
function f(){
var b = 1;
function n(){
var c =3;
}
}
如果定义一个函数n在函数f中,函数n不但可以访问自己作用域的c,而且还能访问所谓的父作用域的b和a
这就是作用域链(Scope Chain)
词法作用域(Lexical Scope)
In Javascript, there is also lexical scope (Lexical Scope). This means that the function generates its scope when it is defined, not when it is called. Just look at an example to understand.
function f1(){var a=1;f2();}
function f2(){return a;}
f1();//a is not defined
Look at function f1 first , the function f2 is called, because the function local variable a is also in f1, it may be expected that the function f2 also accesses a, but this is not the case.
Because the f2 function has been defined at this time, but no a has been found in its range. Whether it is function f1 or function f2, only its own local variables or global variables can be accessed.
Breaking the Chain with Closure
Let’s illustrate closures with an example.
Example 1
function f(){
var b="b";
return function(){
return b;
}
}
Function f contains variable b, It is not possible to access b in the global scope, and the result is undefined.
Look at the return value of this function as a function. This new function can access the variable b in the scope of f. Look at the following code
var n = f();
n();//Access b
Since the function f itself is global, and the function it returns is a new global function, and it The scope of function f can be accessed.
Example 2
The result of this example is the same as the previous example, but there is one difference. Function f does not return a function, but creates a new global variable n. The code is as follows
var n;
function f(){
var b = "b";
n=function(){
return b;
}
}
So ok Direct n() to access variable b in function f
Through the above two examples, we can say that when a function points to its parent scope and its function is to point to local variables, it can be called a closure.
Closure actually provides an excuse, a method for external access to internal variables
When we create a function f that passes parameters, this parameter becomes the function f Local variables. We can create an internal function of f to return this parameter
function f(arg){
var n =function(){
return args;
}
arg;
return n;
}
var m= f(123);
m();//124
Application of closures in loops
It is easy to cause some bugs during recycling, although the surface is normal.
Look at the following code
function f(){
var a = [];
var i;
for(i=0;i<3;i ){
a [i] = function(){
alert(i);
a[0]();//3
a[1]();//3
a[2]();//3
Create a new loop. Our purpose is A new function is created each time it loops, and the function returns the sequence value of the loop, which is i. Let’s take a look at the running results of the above code
are all 3. And the results we expect are 1, 2, 3.
Why exactly? We created three new closures, all pointing to variable i. The closure does not remember the value of variable i, it is only a reference to variable i. After the loop ends, the value of i is 3, so the results are all 3.
Let’s see the correct way to write it
function f() {
var a = [];
var i;
for(i = 0; i < 3; i ) {
return function(){
alert(x);
return x;
}
})(i);
}
return a;
}
var a = f();
a[0]();//0
a[1]();//1
a[2]();//2
We used a closure again to turn the variable i into a local variable x. The x variable has a different value every time. If you don’t fully understand the self-calling function, you can understand it by writing it as follows
function f() {
function makeClosure(x) {
return function(){
return x;
}
}
var a = [];
var i;
for(i = 0; i < 3; i ) {
a[i] = makeClosure(i); / /makeClosure, used to remember the value of i.
}
return a;
}