函数表达式
定义函数的方式
使用函数声明
1 | function 函数名(参数){ |
使用函数表达式
这种情况下创建的函数叫匿名函数
顾名思义,因为 function关键字后面没有函数名(即标识符)1
2
3var 变量=function(){
//函数体
};
两者的主要区别
(1) 函数声明无论置于调用语句前后,解析器都会先读取函数声明
后面不能跟圆括号
由于函数声明的一个重要特征—函数声明提升(在执行代码之前会先读取函数声明)这意味着可以把函数声明放在调用它的语句后面
(2)函数表达式则要等到解析器执行到它所在代码行才被真正执行
后面可以跟圆括号,表示立即自我执行
则函数表达式需要先把函数赋值给变量,再调用函数
递归
递归函数
是在一个函数通过名字调用自身的情况下构成的
1 | function factorial(num){ |
缺点:把该函数保存在另一个变量中,且将factorial变量设置为null时(即当函数名改变了时),导致错误
改进
arguments.callee是一个指向正在执行的函数的指针,通俗一点讲就是指向拥有arguments对象的函数
1 | function factorial(num){ |
闭包
概念
在讲述闭包之前必须先引入几个概念:
函数的执行环境(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 | var name="The window"; |
- 改进1
可适用对象冒充的方式使this指向object
1 | alert(object.getNameFunc().call(object)); //对象冒充 |
- 改进2
访问作用域中的this/arguments对象,必须将对该对象的引用(this)保存到另一个闭包能够访问的变量中
1 | var name="The window"; |
- 改进3
这不是闭包,也不算改进,只是通常是用这种来表示
1 | var name="The window"; |
模仿块级作用域
在JavaScript中,从变量有定义开始,就可以在函数内部随处访问它,即使错误地重新声明同一个变量,也不会改变它的值,当然,重新赋值是会改变的
1 | (function(){ |
使用块级作用域,匿名函数中定义的任何变量,都会在执行结束时被销毁
这种技术经常在全局作用域中被用在函数外部,从而限制向全局作用域中添加过多的变量和函数,能有效防止命名冲突
私有变量
严格来讲,JavaScript中没有私有成员的概念,所有对象属性都是公有的,不过有一个私有变量的概念
私有变量包括函数的参数、局部变量和在函数内部定义的其他函数
在这里提出一个问题:在函数外部私有变量不可访问,又为什么用特权方法使之可以访问呢?为什么不直接舍弃而用公有方法呢?
(个人理解)私有变量除了不可访问还不可修改,可隐藏那些不该被直接修改的数据,但私有变量在函数外部不可见,因此通过特权方法访问它们但也仅仅是访问而已
1 | function MyObject(){ |
方法2
在私有作用域中定义私有变量和函数,通过构造函数的原型定义特权方法
1 | (function(){ |
方法3
模块模式
前两种方法是用于以构造函数的方式创建私有变量和特权方法的
这种是用于以对象字面量的方式为单例创建私有变量和特权方法的
- 单例:只有一个实例的对象,其实为对象字面量声明方式,永远实例化一次
1 | //以对象字面量的方式创建单例对象 |
方法4
增强的模块模式
在返回对象之前加入对其增强的代码
适合那些单例必须是某种类型的实例,同时还必须添加某些属性或方法对其加以增强的情况
1 | var singleton=function(){ |