JavaScript(高级篇--function)
三种定义函数的方式:
function语句形式
函数直接量形式
通过Function构造函数形式定义函数
比较三种方式定义的区别:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Untitled Document</title> <script type=text/javascript charset=utf-8> // 3种方式定义函数 // 1 function语句式 function test1(){ alert('我是test1'); } //test1(); // 2函数的直接量 ECMAScript var test2 = function(){ alert('我是test2'); } //test2(); // 3function构造函数式 var test3 = new Function("a" , "b" ,"return a+b;"); //alert(test3(10,20)); //1.效率对比 var d1 = new Date(); var t1 = d1.getTime(); for(var i =0 ; i <100000;i++){ //function test1(){;} //function语句的形式 15ms(只创建一次,后面重复的不再创建,静态特性) var test2 = new Function(); //1500ms(每次都重新创建,动态特性) } var d2 = new Date(); var t2 = d2.getTime(); alert(t2 -t1); //2. 解析顺序问题 对于function语句式的函数,javascript解析器会优先的解释 test1(); //有结果 function test1(){ alert('1111'); } //alert(test2); //表示变量声明了 但是没有被赋值 //test2(); //结果is not a function , 顺序解析 var test2 = function(){ alert('2222'); } // 4 2 3 3 5 6 关键是理解function 优先解析,其余2种是顺序解析 function f(){return 1;} // 函数1 alert(f()); //返回值为4 说明第1个函数被第4个函数覆盖 var f = new Function("return 2;"); // 函数2 alert(f()); //返回值为2 说明第4个函数被第2个函数覆盖 var f = function(){return 3;} // 函数3 alert(f()); //返回值为3 说明第2个函数被第3个函数覆盖 function f(){return 4;} // 函数4 alert(f()); //返回值为3 说明第4个函数被第3个函数覆盖 var f = new Function("return 5"); // 函数5 alert(f()); //返回值为5 说明第3个函数被第5个函数覆盖 var f = function(){return 6 ;} // 函数6 alert(f()); //返回值为6 说明第5个函数被第6个函数覆盖 //3. 函数作用域的概念 var k = 1 ; function t1(){ var k = 2 ; //局部变量 k //function test(){return k ;} //function语句 返回的是2 var test = function(){ return k}; //函数直接量 返回的是2 // var test = new Function('return k;'); // 构造函数的方式,顶级作用域,相当于在全局创建一个函数,所以返回的是1 alert(test()); } t1(); </script> </head> <body> </body> </html>
函数的参数:arguments对象
arguments是表示函数的实际参数(与形参无关)
callee函数(回调函数属性)
arguments对象的秘密属性 callee属性: 他能返回arguments对象所属的函数的引用,这相当于在自己的内部调用自己。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Untitled Document</title> <script type=text/javascript charset=utf-8> // function参数 //形参列表 //js中 函数的参数: 形参 实参 function test(a,b){ // alert(test.length); //判断形参个数,不建议这样用 2个 //函数的实际参数 内部就是用一个数组去接受函数的实际参数 // arguments 对象 可以访问函数的实际参数 // arguments 对象 只能在函数的内部访问和使用 // alert(arguments.length); // alert(arguments[0]); // alert(arguments[1]); if(arguments.callee.length == arguments.length){ return a+b; } else { return '参数不正确!'; } //arguments对象 用的最多的 还是做递归操作 //arguments.callee 指的就是函数本身,底层代码都是这种写法,不能写死 } // alert(test(10,20)); // 30 function fact(num){ if(num <=1) return 1 ; else return num*arguments.callee(num-1); } // alert(fact(5)); /* function fact(num){ if(num <=1) return 1 ; else return num*fact(num-1); } var F = fact ; //复制函数 fact = null; //在大型程序中调用方法,可能把原函数置空,这样就会报错,所以fact函数内部的调用本身不能写死 alert(F(5)); */ </script> </head> <body> </body> </html>
this对象是在运行时基于函数的执行环境绑定的。在全局函数中,this等于window,而当函数被作为某个对象的方法调用时,this等于那个对象。
也就是说this关键字总是指代调用者。

<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Untitled Document</title> <script type=text/javascript charset=utf-8> // this:this对象是指在运行时期基于执行环境所绑定的 // this总是指向调用者,也就是说 谁调用了我 我就指向谁 var k = 10 ; function test(){ k = 20 ; } test(); alert(k); //20, 函数内部对k再次赋值,覆盖了全局变量K的值,见下面的代码就明白了 /* function test(){ this.k = 20 ;// this 指window } window.test(); */ </script> </head> <body> </body> </html>
每一个函数都包含两个非继承而来的方法:call、apply。 这俩个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内this对象的值。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Untitled Document</title> <script type=text/javascript charset=utf-8> //1. call apply 简单的用法:绑定一些函数 用于传递参数 调用 function sum(x , y){ return x+y; } function call1(num1 , num2){ return sum.call(this , num1 , num2); //把sum函数绑定到当前函数,并且传递参数 } function apply1(num1 , num2){ return sum.apply(this , [num1,num2]);//作用同上,只是参数写法不同 } alert(call1(10 , 20)); alert(apply1(20,40)); //2. 扩充作用域,最大好处就是对象不需要与方法有任何耦合关系 window.color = 'red'; var obj = {color:'blue'}; var obj2 = {color:'yellow'}; function showColor(){ alert(this.color); } showColor.call(window); //red showColor.call(obj); //blue //3. call方法的简单模拟与实现 //function 方法 function test1(a , b){ return a+b; } // 自定义的对象,JS约定函数首字母大写就是自定义对象 function Obj(x, y){ this.x = x ; this.y = y ; return x*y; } var o = new Obj(10 , 20); // alert(test1.call(o,o.x ,o.y)); //对象Obj调用函数test1,可以使用外部的方法。 o.method = test1 ;//给对象定义一个临时的方法,把外部的方法赋给他,效果一样 alert(o.method(o.x , o.y)); delete o.method; </script> </head> <body> </body> </html>
执行环境(execution context)是javascript中最为重要的一个概念。执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为。每一个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中。虽然我们的代码无法访问这个对象,但是解析器在处理数据时会在后台执行它。
全局执行环境是最外围的一个执行环境。根据ECMScript实现所在的宿主环境不同,表示执行环境的对象也不一样。
每一个函数都有自己的执行环境。当执行流进一个函数时,函数的环境就会被推入一个环境栈中。而在函数执行之后,栈将其环境弹出,把控制权返还给之前的执行环境。当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain)。作用域链的用途,是保证对执行环境有权访问的所有变量和函数的有序访问。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Untitled Document</title> <script type=text/javascript charset=utf-8> //1 执行环境 window对象(最上层的执行环境) var color1 = "blue"; function changeColor(){ // 每一个函数 都有一个执行环境 (variable obj) var color2 = "red"; function swapColor(){ // 这个函数 又产生了一个执行环境 (variable obj) // c3 3级作用域 c2 2级作用域 c1 1级作用域 var color3 = color2; color2 = color1; color1 = color3; //这里可以访问:color1、2、3 } //这里可以访问color1、color2、但不能访问color3 swapColor(); } //这里只能访问color1 changeColor(); // 作用域 window 第一个作用环境 // 环境变量 可以一层一层的向上进行追溯 可以访问它的上级 环境(变量和函数) ----作用域链的概念 // 一层一层的像上追溯--大型程序很少用全局变量,一层层找效率慢 </script> </head> <body> </body> </html>
垃圾收集、块级作用域

<script type=text/javascript charset=utf-8> //垃圾收集 方法1 标记方法 2 引用计数法 /* function test(){ var a = 10 ; //被使用 var b = 20 ; //被使用 var c ; } test(); //执行完毕 之后 a、b又被标记了一次 :没有被使用 */ //块级作用域的概念 //高级程序语言 java for if 块级作用域的概念、 // js 没有块级作用域的概念, 所以i 在for循环执行完毕还没销毁,要等整个函数执行完毕才被回收 function test(){ for(var i = 1 ; i <=5; i++){ //i alert(i); } alert(i); //6 } test(); //alert(i);//这里就会报错 // js : () 表示执行 //模拟块级作用域的概念(function(){ })();, 函数执行完毕,i被回收了。 function test(){ (function(){ for(var i = 1 ; i <=5; i++){ //i alert(i); } })(); alert(i);//i is not defined } test(); (function(){ alert('我直接执行了!'); })(); </script>
闭包:一个函数 可以访问另外一个函数作用域中的变量。 需要理解作用域链的概念

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Untitled Document</title> <script type=text/javascript charset=utf-8> var name = "xiao A"; var obj = { name : "xiao B" , getName: function(){ return function(){ return this.name; } } }; //alert(obj.getName()()); var k = obj.getName(); //全局作用域 function(){return this.name;} alert(typeof k); // function类型 alert(k()); //xiao A, 因为window调用这个函数中,所以this 是window var name = "xiao A"; var obj = { name : "xiao B" , getName: function(){ // this总是指向调用者, obj var o = this; return function(){ return o.name; } } }; //alert(obj.getName()()); var k = obj.getName(); alert(k()); //xiao B // 闭包:一个函数 可以访问另外一个函数作用域中的变量 // 封闭性 : private 起到一个保护变量的作用 //temp 不会被回收,因为被下面那个函数引用了。 // 1 级作用域 function f(x){ // 2 var temp = x ; //局部变量 //temp已经没有被使用 return function(x){ // 3 (function 有了一个执行域 var obj) temp += x ; // 又被使用了 alert(temp); } } var a = f(50);//50 赋给了temp , 不会给下面的函数的参数X, 因为下面那个函数当做一个Obj返回了 alert(a);//function(x){temp += x ; alert(temp);} a(5); //55 a(10);//65 a(20); //85 </script> </head> <body> </body> </html>