函数表达式

函数表达式

定义函数的方式

使用函数声明

1
2
3
4
5
function 函数名(参数){
//函数体
}
可通过name属性访问函数名(即标识符)(有何意义?)
alert(函数名.name);//函数名

使用函数表达式

这种情况下创建的函数叫匿名函数
顾名思义,因为 function关键字后面没有函数名(即标识符)

1
2
3
var 变量=function(){
//函数体
};

两者的主要区别

(1) 函数声明无论置于调用语句前后,解析器都会先读取函数声明

后面不能跟圆括号

由于函数声明的一个重要特征—函数声明提升(在执行代码之前会先读取函数声明)这意味着可以把函数声明放在调用它的语句后面

(2)函数表达式则要等到解析器执行到它所在代码行才被真正执行

后面可以跟圆括号,表示立即自我执行

则函数表达式需要先把函数赋值给变量,再调用函数

递归

递归函数

是在一个函数通过名字调用自身的情况下构成的

1
2
3
4
5
6
7
function factorial(num){
if(num<=1){
return 1;
}else{
return num*factorial(num-1);
}
}

缺点:把该函数保存在另一个变量中,且将factorial变量设置为null时(即当函数名改变了时),导致错误

改进

arguments.callee是一个指向正在执行的函数的指针,通俗一点讲就是指向拥有arguments对象的函数

1
2
3
4
5
6
7
function factorial(num){
if(num<=1){
return 1;
}else{ //arguments.callee代替函数名
return num*arguments.callee(num-1);
}
}

闭包

概念

在讲述闭包之前必须先引入几个概念:
函数的执行环境(excution context)、活动对象(call object)、
作用域(scope)、作用域链(scope chain)
以函数a从定义到执行的过程为例阐述:

① 当定义函数a的时候,js解释器会将函数a的作用域链设置为定义a时a所在的“环境”,如果a是一个全局函数,则作用域链中只有全局变量对象。

② 当执行函数a的时候,a会进入相应的执行环境

③ 在创建执行环境的过程中,首先会为a添加一个scope属性,即a的作用域,其值就为第1步中作用域链。

④ 然后执行环境会创建一个活动对象(活动对象也是一个拥有属性的对象,但它不具有原型而且不能通过JavaScript代码直接访问)。创建完活动对象后,把活动对象添加到a的作用域链的最顶端

⑤下一步是在活动对象上添加一个arguments属性,它保存着调用函数a时所传递的参数。

⑥ 最后把所有函数a的形参和内部的函数b的引用也添加到a的活动对象上。在这一步中,完成了函数b的的定义,因此如同第3步,函数b的作用域链被设置为b所被定义的环境,即a的作用域。

a的作用域链(本质上是一个指向变量对象的指针列表)包含

  • 本地活动对象:arguments属性,a的变量,函数
  • 全局变量对象

小结

  • 闭包是指有权访问另一个函数作用域中的变量的函数,通过模拟全局作用域,使局部变量变为全局变量
    闭包就是将函数内部和函数外部连接起来的一座桥梁

创建闭包的常用方式:在一个函数内部创建另一个函数

  • JavaScript中所有的function都是一个闭包。

不过一般来说,嵌套的function所产生的闭包更为强大,也是大部分时候我们所谓的“闭包”。

  • 闭包的用途

1、使其可以访问其外部函数作用域中的变量
2、可以实现局部变量驻留在内存中从而累加
通常函数的作用域及其所有变量会在函数执行结束后被销毁。

但由于闭包(内部函数)的作用域链包含外部函数(非全局)的活动对象。当函数返回一个闭包时,函数的作用域将会一直在内存中保存到闭包不存在为止

  • 关于this对象

闭包运行时this指向window

1
2
3
4
5
6
7
8
9
10
var name="The window";
var object={
name:"The Object",
getNameFunc:function(){
return function(){
return this.name; //这里的作用域,this指向window
};
}
};
alert(object.getNameFunc()()); //"The window"
  1. 改进1

可适用对象冒充的方式使this指向object

1
alert(object.getNameFunc().call(object)); //对象冒充
  1. 改进2

访问作用域中的this/arguments对象,必须将对该对象的引用(this)保存到另一个闭包能够访问的变量中

1
2
3
4
5
6
7
8
9
10
11
12
13
var name="The window";
var object={
name:"The Object",
getNameFunc:function(){
var that=this; //这里的作用域,this指向object
return function(){
return that.name;
};
}
};
alert(object.getNameFunc()()); //"The Object"
不过这个不会常用,太绕了!!!
理解思路即可
  1. 改进3

这不是闭包,也不算改进,只是通常是用这种来表示

1
2
3
4
5
6
7
8
var name="The window";
var object={
name:"The Object",
getNameFunc:function(){
return this.name;
}
};
alert(object.getNameFunc()); //"The Object"

模仿块级作用域

在JavaScript中,从变量有定义开始,就可以在函数内部随处访问它,即使错误地重新声明同一个变量,也不会改变它的值,当然,重新赋值是会改变的

1
2
3
4
5
6
7
(function(){
//这里是块级作用域
})();

JavaScript将function关键字当作一个函数声明的开始,
而函数声明后面不能跟圆括号,给函数声明加上一对圆括
号即转换成函数表达式,后面就可以跟圆括号了

使用块级作用域,匿名函数中定义的任何变量,都会在执行结束时被销毁
这种技术经常在全局作用域中被用在函数外部,从而限制向全局作用域中添加过多的变量和函数,能有效防止命名冲突

私有变量

严格来讲,JavaScript中没有私有成员的概念,所有对象属性都是公有的,不过有一个私有变量的概念
私有变量包括函数的参数局部变量在函数内部定义的其他函数

  • 特权方法

    有权访问私有变量的公有方法,在公共域可见,不可被修改,只能替换/删除

在这里提出一个问题:在函数外部私有变量不可访问,又为什么用特权方法使之可以访问呢?为什么不直接舍弃而用公有方法呢?
(个人理解)私有变量除了不可访问还不可修改,可隐藏那些不该被直接修改的数据,但私有变量在函数外部不可见,因此通过特权方法访问它们但也仅仅是访问而已

  • 创建特权方法的方式

    方法1

    在构造函数中定义特权方法
1
2
3
4
5
6
7
8
9
10
11
12
function MyObject(){
//私有变量和私有函数
var privateVariable=10;
function privateFunction(){
return true;
}
//特权方法
this.publicMethod=function(){
privateVariable++;
return privateFunction();
};
}

方法2

在私有作用域中定义私有变量和函数,通过构造函数的原型定义特权方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
(function(){
//私有变量和私有函数
var privateVariable=10;
function privateFunction(){
return true;
}
//构造函数,在函数中不带var的变量为全局变量
MyObject=function(){ }; //有点像原型模式创建对象
//公有/特权方法
MyObject.prototype.publicMethod=function(){
privateVariable++;
return privateFunction();
};
})(); //匿名函数的自我执行

方法3

模块模式
前两种方法是用于以构造函数的方式创建私有变量和特权方法的
这种是用于以对象字面量的方式单例创建私有变量和特权方法的

  • 单例:只有一个实例的对象,其实为对象字面量声明方式,永远实例化一次
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//以对象字面量的方式创建单例对象
var singleton={
name:value,
method:function(){
//这里是方法的代码
}
}
//为单例对象添加私有变量和特权方法使其增强
var singleton=function(){
//私有变量和私有函数
var privateVariable=10;
function privateFunction(){
return true;
}
//公有/特权方法和属性
return{
publicProperty:true,
publicMethod:function(){
privateVariable++;
return privateFunction();
}
};
}(); //匿名函数的自我执行

方法4

增强的模块模式
在返回对象之前加入对其增强的代码
适合那些单例必须是某种类型的实例,同时还必须添加某些属性或方法对其加以增强的情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var singleton=function(){
//私有变量和私有函数
var privateVariable=10;
function privateFunction(){
return true;
}
//创建对象
var object=new CustomType();
//公有/特权方法和属
object.publicProperty:true,
object.publicMethod=function(){
privateVariable++;
return privateFunction();
};
//返回这个对象
return object;
}(); //匿名函数的自我执行
-------------本文结束 感谢您的阅读-------------