JavaScript中的函数由于其独特的功能和特性大大增强了JS的编程实力。在JS中函数也是对象,所以可以为它添加属性和方法,但typeof操作符对函数返回字符串’fnuction’,原生的toString()方法返回代表这个函数的源代码。
一、函数的实质
函数本身也是JS中创建的一种变量(准确分类为对象),它由一系列可执行代码+name属性+形参列表组成。函数可以通过name属性或者变量名来调用。函数具有函数作用域,相对于全局作用域对程序执行环境的变换进行控制。
二、函数的定义
函数可以有三种定义方式,最常用的是var变量声明也就是函数定义表达式和function关键字声明。这两种方式的不同之处在于:1、函数声明方式会使 得函数声明提升,即可以在函数声明之前合法使用函数,函数定义表达式方式只会使函数名提前,但可执行代码等并没有得到声明;2、每个函数都有一个name 属性,代表函数的名字,使用函数声明定义的函数name属性不为空,使用函数定义表达式定义的函数成为匿名函数,name属性是空字符串。JS中不建议使 用构造函数模式定义函数,消耗内存,效率也比较低。
三、函数的局部作用域
JavaScript中没有块级作用域,for循环结构等内部并不存在局部变量这种说法。但函数有局部作用域,函数内部的形参列表、变量、内嵌函数都是属 于该局部作用域的局部变量或函数,只能在该函数内部使用,一般情况下当函数执行结束(即遇到return语句或者执行到代码末尾时)后会被JS内存管理机 制当做“垃圾”回收掉。
四、函数的调用
1、作为对象的方法调用,此时执行上下文是该对象;
2、作为普通函数调用,无论是在全局中调用还是在某个函数内部调用,执行上下文都是全局作用域;
3、作为构造函数调用,此时执行上下文是新创建的那个对象实例;
4、作为对象冒充中的函数调用即使用call或者apply方法时,执行上下文是那个间接调用该函数的对象。
五、函数词法分析过程
在函数运行之前会有一个预编译也就是词法分析过程,这个过程如下:
1、分析形参列表,将形参变量作为属性保存在AO活动对象中,属性值为undifined;
2、分析函数内部声明的变量,如果AO对象中还没有这个属性,则保存这个属性,值为undifined,如果已有这个属性则什么也不做;
3、分析内嵌函数,如果AO中没有同名属性,则添加这个属性(作为方法保存),如果已有同名属性,则覆盖之前的属性。
函数词法分析时只是决定了该函数的AO对象中有哪些属性,是变量还是函数,变量的值都是undifined,当函数运行时才会给AO对象中的属性赋值!
六、函数最棒的特性——闭包
1、闭包的形成原理及实质
闭包是指这样一种函数,它内嵌于某个函数中,那么它的作用域链就会关联所有外层函数和全局作用域的变量对象,也就是说它可以访问所有外层函数和全局作用域 中的变量对象。全局作用域的变量对象要等到程序执行结束之后才会被回收,如前所述,外层函数的变量对象在一般情况会在函数执行结束后被回收掉。可是如果出 现这样的情况,这个或这几个闭包被外层函数作为返回值返回或者作为返回值的方法返回(外层函数返回一个对象,闭包作为这个对象的方法也被返回),这若干个 闭包共享这些被滞留下来的变量对象。总而言之就是闭包被保存下来了,那么闭包本身的变量对象不会被销毁,更重要的一点就是闭包最大的作用——它会使得它所 关联到的变量对象(即所有外层函数的变量对象)都滞留内存,不会被回收!从使用变量的角度看,那些变量对象从此只能通过这个闭包访问了。
2、闭包的作用
(1)实现缓存机制,节约计算机资源,加快执行速度提高效率;
(2)实现私有变量的封装;
(3)实现面向对象中的对象。
3、闭包带来的问题
(1)、可能会因为造成循环引用导致内存泄露;
(2)、由于闭包滞留内存,会比较消耗资源;
(3)、同一次的执行结果生成的闭包共享该次滞留下来的变量对象,可能会造成开发人员意料之外的结果,比如在循环中绑定事件处理函数。
原文地址:www.bfy0412.com