一.定义函数
定义函数的两种方式:函数声明和函数表达式
1.函数声明
function functionName(arg0,arg1,…){
//函数体
}
①name属性:访问函数的名字
alert(funcitonName.name);//”functionName”
②函数声明提升
在代码执行之前会先读取函数声明(解析器会率先读取函数声明)。所以函数声明可以放到调用它的语句后边
2.函数表达式:创建函数再赋值给变量
var functionName=function(arg0,arg1,…){
//函数体
};
①函数声明要求有名字,但函数表达式不需要。没有名字的函数表达式叫匿名函数
②没有函数声明提升,所以使用前先赋值
sayName(); //出错
var sayName=function(){
alert(“liuzhongyi”);
};
③在控制语句中应该使用函数表达式,而不是函数声明
④使用匿名函数可以将函数当做值来使用,将函数作为其他函数的返回值等
二.递归
递归函数:一个函数通过名字调用自身
但使用函数名调用自身时,只要函数名变化,递归函数将出现问题
①使用arguments.callee来调用自身:是一个指针,指向正在执行的函数
function myFunction(num){ if(num<=1){ return 1; }else{ return num * arguments.callee(num-1); } }
②使用函数表达式
var myFunction=(function f(num){ if(num<1){ return 1; }else{ return num*f(num-1); } });
即使将myFunction改名,f(num)依然能在函数内使用
三.闭包
闭包:有权访问另一个函数作用域中的变量 的函数
创建闭包:在函数内部创建另一个函数
1.闭包原理
①执行环境和作用域链
作用域链本质是一个指向变量对象的指针列表,只引用但不实际包含变量对象
当函数执行完毕后,执行环境,作用域链及局部变量对象就会销毁,内存中只保存全局作用域(全局变量对象)
②在一个函数内部定义的匿名函数将外部函数的活动对象(局部变量对象)添加到它的作用域链中
这个匿名函数被返回后,它的作用域链被初始化为包含它自己的作用域,包含函数的作用域和全局作用域
函数在执行完毕后,其活动对象也不会销毁,因为匿名函数的作用域链依然在引用这个活动对象
解除匿名函数的引用,活动对象被销毁 myFunction=null;
2.闭包与变量
闭包只能取得包含函数中任何变量的最后一个值 //没看懂
3.关于this对象
this对象是与函数运行时的执行环境绑定的,全局函数中this等于window,当函数作为某个对象的方法调用时,this等于那个对象
var name="window"; var object={ name:"object", getThis:function(){ return function(){ return this.name; }; } }; alert(object.getThis()); //"window" 因为this能够访问到全局变量中的name变量
this不能指向自己吗??
var name="window"; var object={ name:"object", getThis:function(){ return this.name; } }; (object.getThis=object.getThis)(); //"window"
4.内存泄漏
闭包的作用域链中保存着一个HTML元素,那么该元素将无法被销毁
因为BOM,COM对象是以COM对象的形式实现的,采用的是引用计数的垃圾收集策略,由于闭包的存在,HTML元素的引用次数至少是1,所以不能被收回
四.模仿块级作用域
function outputNumbers(count){ for(var i=0;i<count;i++){ alert(i); } var i; alert(i); //依然计次 }
Java,C中i只会在for循环的语句块中有定义,循环一结束,变量i就会被销毁。但JS中没有块级作用域,i定义在outputNumbers()的活动对象里,而且会对后续的再次声明视而不见
1.私有作用域:用匿名函数模仿块级作用域
function关键字表示函数声明的开始,将函数声明转换成函数表达式,只要给它加上一对圆括号。函数表达式可以直接作为值来取代函数名。调用函数就是在后边加一对圆括号
(function{ //块级作用域 })();
匿名函数中定义的任何变量,都会在执行结束时被销毁
function outputNumbers(count){ (function(){ for(var i=0;i<count;i++){ alert(i); } })(); alert(i); //出错 }
好处:
①私有作用域,每个开发人员都可以定义自己的变量,而不用担心扰乱全局作用域
②可以减少闭包占用的内存,因为没有指向匿名函数的引用,函数执行完作用域链就可以销毁
2.私有变量
所有对象属性都是公有的。(可以在外部访问对象属性)
私有变量:函数的参数,局部变量,函数内部定义的其他函数。在函数外部不能访问这些变量
公有方法:可以通过闭包来实现公有方法,而通过公有方法可以访问在包含作用域中定义的变量
特权方法:有权访问私有变量的方法叫特权方法
自定义类型的特权方法
①在构造函数中定义特权方法
function Person(name){ this.getName=function(){ return this.name; }; this.setName=function(value){ this.name=value; }; } var person1=new Person("liu"); alert(person1.getName());// "liu" person1.setName("zhong"); alert(person1.getName()); //"zhong"
实现了在构造函数外部访问了私有变量name
缺点:构造函数的弊端,每个实例都会创建同一组相同的方法
②静态私有变量
(function(){ var name=""; Person=function(value){ //声明全局变量,使用函数表达式,因为函数声明只能创建局部函数 name=value; }; Person.prototype.getName=function(){ return name; }; Person.prototype.setName=function(){ name=value; }; })(); var person1=new Person("liu"); alert(person1.getName()); //"liu" person1.setName("zhong"); alert(person1.getName());
在一个函数上调用私有变量会影响所有实例,每个实例没有自己的私有变量
单例的特权方法
①模块模式
为单例(只有一个实例的对象)创建私有变量和特权方法
当必须创建一个对象并以某些数据对其初始化,同时还要公开一些能够访问这些私有数据的方法,就可以使用模块模式
var application=function(){ //私有变量和函数 var components=new Array(); //初始化 components.push("liu"); //公有方法 return{ getLength:function(){ return components.length; } }; }();
②增强的模块模式
单例必须是某种类型的实例,同时还必须添加某些属性和方法对其以增强的情况
//application对象必须是BaseComponent的实例 var application=function(){ //私有变量和函数 var components=new Array(); //初始化 components.push(new BaseComponent()); //创建application的一个局部副本 var app=new BaseComponent(); //公共接口 app.getLength=funtion(){ return components.length; }; //返回这个副本 return app; }();