一、函数的定义
window.onload=function(){ //函数的调用 fcName(); fc1(); var num=fc2(10,20); fc3(); fc4(); } //函数的参数列表 相当于函数入口;return 相当于函数出口 /*1.function 语句式 特点:a.创建一次存储在内存中,每次使用从内存中取,效率高 b.js 解析器会优先解析然后存储在内存中,使用时直接取内存中的(优先解析) c.具有函数作用域 */ function fcName(){ alert("function 语句式"); } /*2、函数的直接量式(ECMAScript推荐) 特点:a.创建一次存储在内存中,每次使用从内存中取,效率高 b.js 解析器 只是声明了变量fc1 并没有给其赋值 ,只用当程序运行到这里时才会赋值(顺序解析) c.具有函数作用域 */ var fc1=function(){ alert("函数的直接量式"); } /* 3.Function 构造函数式 特点:a.每次创建使用后就会销毁 不占用内存但每次使用都要创建 所以效率低 b.js 解析器 只是声明了变量fc2 并没有给其赋值 ,只用当程序运行到这里时才会赋值(顺序解析) c.顶级作用域 */ var fc2=new Function("a","b","return a+b;"); var fc3=new Function("alert('构造函数式')"); //三种函数的作用域 var m=0; function fc4(){ var m=1; function fc5(){ alert(m);// 1 }; var fc6=function(){ alert(m);// 1 } var fc3=new Function("return m;"); fc5(); fc6(); alert(fc3());// 0 }
二、函数的参数
<script type="text/javascript"> window.onload=function(){ fc1(10,20); } /* 在js中形参个数可以与实参个数不同 */ function fc1(a,b,c){ var length=fc1.length;//获得形参个数 (不建议使用) /* arguments 的对象可以访问函数的实际参数(只能在函数内部使用) */ var length2=arguments.callee.length;//获得形参个数(arguments.callee 指向函数本身) var args=arguments.length;//实际参数的个数 var arg1=arguments[0];//获得第一个实际参数 //在实际开发中我们会先判断参数个数是否相同再执行代码 if(arguments.callee.length===arguments.length){ return a+b+c; }else{ return '参数不正确'; } } </script>
三、this 关键字
window.onload=function(){ //this 对象是指在运行时基于执行环境所绑定的;this总是指向调用者 } var k=10; function test(){ this.k=20; } alert(k);// 结果为10; test();// 相当于window.test(); alert(k); // 结果为 20 因为 调用 test()函数的是window对象 所以this指向window对象 this.k 覆盖了 var k;
四、call、apply关键字
window.onload=function(){ // call 、apply 作用一:用于绑定一些函数 用于传递参数、调用 alert(call1(10,20));//30 alert(apply1(20,30));// 50 } function sum(x,y){ return x+y; } /* 把 sum函数绑定到this(call1()函数)上 */ function call1(x1,y1){ return sum.call(this,x1,y1); } function apply1(x2,y2){ return sum.apply(this,[x2,y2]); }
window.onload=function(){ // call 、apply 作用二:用于扩充作用域 window.color="red"; var obj={color:"blue"}; alert(showColor.call(this));//red 因为this指代调用者window 所以showColor 被绑定在window上 alert(showColor.call(obj));// blue showColor 被绑定在obj上 } function showColor(){ return this.color; }
window.onload=function(){ // 模拟 call 、apply 方法的实现方式 var obj=new Obj(10,30); obj.method=test; alert(obj.method(obj.x,obj.y)); delete obj.method;//使用后及时删除 临时调用 } function test(a,b){ return a*b; } //自定义的对象 function Obj(x,y){ this.x=x; this.y=y; return x*y; }
五、执行环境、作用域链
执行环境: 在javascript里面执行环境分为两类,一类是全局环境,一类是局部环境,整个页面里被共享的方法和属性就是在全局环境,
相对于全局环境,函数{}号里的执行环境就是局部环境,执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为,
每个执行环境都定义了一个与之相关的变量对象,环境中定义的所有变量和函数都保存在这个对象里,虽然我们自己编写的代码无法访问这个对象,
但解析器在处理数据时候后台会使用到它。
全局执行环境另一种说法是最外围的一个执行环境,在web浏览器的范围中(actionscript也是施行了ECMAScript标准,
它的全局范围就和javascript的全局范围不同),全局执行环境被认为是window对象,因此全局变量和函数都是作为window对象的方法和属性来创建的,
全局执行环境知道应用程序退出比如关闭网页或浏览器才会被销毁。而局部环境则是以函数对象作为关联对象。
作用域链: Javascript
高级程序设计(第三版)(P73
):当代码在一个环境中执行时,会创建变量对象的的一个作用域链(scope chain)。
作用域链的用途,是保证对执行环境有权访问的所有变量和函数的有序访问。作用域链的前端,始终都是当前执行的代码所在环境的变量对象
。如果这个环境是一个函数,则将其活动对象作为变量对象。
每一个函数都有自己的执行环境,当执行流进一个函数时,函数环境就会被推入一个环境栈中,而在函数执行之后,栈将其环境弹出,
把控制权返回给之前的执行环境,这个栈也就是作用域链。
自我理解:作用域链是由于js的变量都是对象的属性,而该对象可能又是其它对象的属性,而所有的对象都是window对象的属性,
所以这些对象的关系可以看作是一条链
//1、最外层执行环境 window对象 var color1="blue"; function changeColor(){//每个函数 都是一个执行环境 var color2="red"; function swapColor(){//该函数 也会产生一个执行环境(variable obj) //该函数内部可以访问 color1 ,color2 var color3=color2; color2=color1; color1=color3; } //这里可以访问color1、color2 但无法访问 color3 swapColor(); } //这里只能访问 color1 changeColor(); //环境变量 可以一层一层的向上进行追溯 可以访问它的上级环境变量和函数
六、垃圾收集、块级作用域
//垃圾回收机制 一.标记法 function test1(){ var a=10; var b=20; } /* 当执行test()函数时变量a、b 会被标记为被使用,一段时间间隔后会标记为未使用 这是js的垃圾回收机制会回收a、b */ test(); //垃圾回收机制二:计数法 function test2(){ var a=10;// 被计数 count++ count=1; var c; c=a; //count++ count=2; a=50;// count-- count=1; } //当count=0 时js的垃圾回收机制就会回收 该变量
块级作用域
/* 在高级程序预言中 for if 语句具备块级作用域的概念 在js中没有块级作用域的概念 所以会打印 6 */ function test1(){ for(var i=0;i<5;i++){ alert(i); } alert(i);// 6 } // 模拟js的块级作用域 使用() function test2(){ (function (){ for(var i=0;i<5;i++){ alert(i); } })(); alert(i);// 这样 i在 for循环结束后 就会被 回收 所以程序出错 }
七、closure 闭包
<script type="text/javascript"> var name="AA"; var obj={ name:"BB", getName:function(){ return function(){ return this.name; } } }; var fg=obj.getName()();//AA //拆开写 var m=obj.getName();//getName()函数 返回的是一个匿名函数 我们定义我为函数m() /* 很显然m()属于window对象 根据作用域链的特点变量只能向上层层追溯 所以 this.name指向的是window.name, 因此最终打印AA */ var ff=m();//AA //如果我希望最终的结果是 BB 该如何改造代码 var obj1={ name:"BB", getName:function(){ var o=this;//this 指向obj1 return function(){ return o.name; } } }; var m1=obj1.getName(); var dd=m1();//虽然m1()函数属于window对象但 它内部的name属性属于obj1对象 所以结果是BB /* 闭包:一个函数可以访问另外一个函数作用域中的变量 封闭性:起到保护变量的作用 */ function f(x){ var temp=x; return function(x){ temp+=x;//temp又被使用 所以在2级作用域后没有被回收 return temp; } } var te1=f(20); var te=te1(2);//22 te=te1(3);//25 te=te1(5);//30 </script>