在前一章中讲了JavaScript的入门语法,在这一章要看看函数的使用。
JavaScript中的函数和Python中的非常类似,只不过定义的方式有些不同,下面看看是怎么定义的
//定义普通函数 function f1(){ console.log('hello world!') } //定义带参数的函数 function f2(a,b){ console.log(a) console.log(b) } //定义带返回值的函数 function f3(a,b){ return a+b } //定义匿名函数 var sum = function(a,b){ return a+b } //定义立即执行函数 (function(a){ console.log('立即执行函数') })(a)
立即执行函数其实有些像python里的lambda方法,的用法应该注意一下:因为在我们在全局内定义了一个变量,在定义函数的过程是无法调用这个变量的。
(function(a,b){ console.log(a+b); console.log('函数中') })(123,456)
这个方法可以让外部访问不到函数中的变量,防止函数污染全局变量。
这里要将一个JavaScript里比较随意的一点(也可以说是一个坑),我们看一看下面的函数
function fun(a,b){ console.log(a+b); } fun(12,34)
这样没啥问题,因为形参和函数调用的时候的实参数量是一致的。那么我们改一下实参的状态,会怎么样?
fun(12,34,56) //打印值为46 fun(12) //打印值为NaN
arguments的使用
这里就要讲到arguments的使用了,类似Python中的*args,arguments表达了函数的形参(但是是可以不用在声明函数中明确的写出来的)
所以在JS的函数里不管是多传参数或少传参数都是不会报错的,我们可以这样看看
function fun2(a,b){ console.log(arguments) for (i=0;i<arguments.length;i++){ console.log(arguments[i]) } } fun2(11,22,33,44)
而我们想要算一下传入参数的累加值,就要按下面的方法了
function fun_sum(a,b){ var totle=0; for (i=0;i<arguments.length;i++){ totle += arguments[i] } return totle } console.log(fun_sum(1,2,3,4,5))
局部变量
在JavaScript内部声明的变量(var)是局部变量,所以只能在函数内部访问,就是说这个变量的作用域只有函数内部。只要函数运行完毕,改变量就会被删除。
全局变量
在函数外部声明的变量是全局变量,网页上所有的脚本和函数都能访问。
var test = 'test' console.log('声明函数前',test) function fun(){ console.log('开始声明函数',test); var test = 'in func'; console.log('函数声明完成',test); } console.log('调用函数前',test); fun(); console.log('调用函数后',test); //////////输出////////// 声明函数前 test 调用函数前 test 开始声明函数 undefined 函数声明完成 in func 调用函数后 test
注意这里还有个非常坑的点:
我们在定义函数前有个变量test,如果在函数内部没有对这个test进行赋值,那么这个变量是正常的,但是一旦有了赋值操作,在操作前就成了underfined了(上面的例子就是有了赋值操作),在看看下面的
var test2 = 123; function fun2(){ console.log(test2) } fun2() //////////输出////////// 123
变量生存周期
JavaScript的变量生命期从他们被声明的时候开始,
局部变量是在函数运行结束的时候被删除,而全局变量是在页面被关闭的时候删除。
还是要看看这几个例子:
例子1
var city = 'beijing'; function fun(){ var city = 'shanghai'; function inner(){ var city = 'shenzhen'; console.log(city); } inner(); } fun() ////////输出//////// shenzhen
例子2
var city = 'beijing'; function fun(){ console.log(city); } function fun2(){ var city = 'shanghai'; return fun; } var ret = fun2(); ret(); //////////输出////////// beijing
案例3.闭包
var city = 'beijing'; function f(){ var city = 'shanghai'; function inner(){ console.log(city); } return inner; } var ret = f(); ret() //////////输出////////// shanghai
在上面的例子中,ret对应的函数就是inner(),也就是闭包的用法。
所以,函数的调用一定要往回找函数的定义过程。
首先,在函数内部找->再往外部找,最后找全局作用域。
JavaScript在调用函数的一瞬间,会先进行词法分析,大致过程是这样的:
函数调用的前一个瞬间,回显形成一个激活对象:Active Object(AO),并会进行下面3个方面的分析
函数参数——如果有参数,则将此参数赋值给AO,且值为underfined;如果没有,则不做任何操作。
函数局部变量——如果AO上右同名的值,则不做任何操作,如果没有,则将此变量赋值给AO,并且值为underfined。
函数声明——如果AO上有,则会将AO上的对象覆盖,如果没有则不做任何操作。
综上所述,函数内部无论是使用参数还是使用局部变量都会到AO上找。
再看看下面的例子:
var name = 'xiaoming'; var age =12; function fun(){ console.log(age); var age = 30; console.log(age) } fun() //////////输出////////// underfined 30
这也就是讲了上面的那个坑是怎么导致的。
1 var age =12; 2 function fun(){ 3 console.log(age); 4 var age = 22 5 console.log(age) 6 function age(){ 7 console.log('inner func'); 8 } 9 console.log(age) 10 } 11 fun() 12 //////////输出////////// 13 ƒ age(){ 14 console.log('inner func'); 15 } 16 22 17 22
结合前面的规则看看是怎么执行的。
首先是分析过程:
1.检查函数内是否有参数——没有
2.检查函数内是否有局部变量——第4行的代码,定义了age局部变量,然后使AO.age=underfined
3.检查函数内是否有内置函数——第6行的代码定义了局部函数age,然后覆盖原AO.age=fun()
然后是执行阶段
1.要记得,所有的值都是从AO里找的,第3行的打印就是打印的AO.age,实际值应为fun().
2.第4行的代码是AO.age值赋值为22,后面再打印就是22了。