javascript的函数,作用域,闭包

函数具有两个功能,一个是声明创建功能,在声明时就创建了一个函数对象,另一个是运行功能,在函数体内进行逻辑操作.

首先看下面的例子:
function Fn(){console.log("Fn")};
Fn();
第一句是函数的声明时,第二句是函数运行时.function类型的对象比object类型的对象多了个运行时.那么我们来了解一下这两个时间里,函数做了什么?

函数在声明时: 创建了一个函数对象(作为当前所在的函数对象的OA对象的属性,这句的理解要看完运行时),并且为这个函数对象添加了一些隐含的属性对象,
1,__proto__,指向它的原型,默认指向Function.prototype,而Function.prototype.__proto__ 指向 Object.prototype;
2.prototype.这是作为它创建出来的实例对象的原型来存在的,其中也默认protoype.constructor == 函数名 ,
3.length:形参的个数,
4.scope对象(无法访问),可以认为是栈结构(后进先出),把函数声明时所在函数对象的VO对象引用入栈顶
还有其它的一些隐含对象,我没有研究.

函数在运行时:

1,动态指定this,可以通过a.fb(),call,apply,bind等方式指定,如果是new的话,则this指向{},但如果没有指定this,则this.的指向global对象(注意this不能通过this = a这样的方式赋值)
2.创建实参对象arguments,此对象类似数组,存储实参值.
3,创建AO对象(活动对象),且其所以的函数对象的scope指这OA对象的引用放入栈顶,如果有实参,按内部变量处理,并且引用arguments,按顺序赋值.函数内var声明的对象(初始值为undefined)或者function 声明的对象(此时担声明的函数对象也创建出来,其标识符指向报创建出来的函数对象);所以var a = function(){};和 function a(){}在这时的处理是不同的,var方式 a 的初始值为undefined,等运行到这句时才创建函数对象,并且a指向刚创建的函数对象,而function a(){}方式是还没有运行到这句,已经创建函数对象,并且 a 指向这函数对象.
4.开始按顺序执行,若有取值,则在当前的函数的scope中查找(作用域查找机制,对象的属性值按原型链查找),直找到global对象(scope最底的OA对象),再查找到globa的原型Object.prototype如果没有找到则取值为undefined. 如果执行到return ,若不指明return 值,则返回this指向的对象.所以new操作就是刚开始把this指向{},默认返回this指向的对象,有return 对象,则返回指定对象.得到的实例对像的__proto__值指向函数对象的prototype对象,用函数对象的prototype的constructor来指实例对象的实例类型.函数返回时:清理战场工作,如果返回对象中没有引用到的对象,则交由GC处理,如果返回对象有对此对象的引用,这样的对象则不能被清理,

看懂下面的例子,就知道什么是作用域链和闭包了:

function Fn (){ //!.
        //3
var b =4;     //4
function Fb(){
          //9
b = b+6; //10
console.log(b);     //11
}
var Fc = function (){//5
            //13
b = b-2;          //14
console.log(b);       //15
}
return {fb: Fb,fc: Fc}//.6

}

var fn = Fn();     //2    //7

fn.fb();           //8
fn.fc();            //12

看到所标的号,表示执行顺序.
1.Fn函数声明时,此时Fn的scope全局OA对象,并且Fn作为此全局OA对象的属性,Fn.__proto__==Function.prototype
2.Fn函数运行时,进入函数体,创建Fn的OA对象,指定this为global,并且为Fn的scope添加Fn的OA对象,此时Fn的scope有全局OA和Fn的OA
3.创建函数内var和function声明的对象,var声明的值为undefined,funtion声明的则创建函数对象,并且为所创建的函数对象创建它的OA对象.在这一步中,就可以得到 b = undefined;Fb=[Function]; Fc = undefined;注意Fb和Fc的差别.此时Fb已经是声明创建时了,那么会为Fb对象创建Fb的scope对象,并且把Fn的scope对象传给Fb的scope对象,那么这时的Fb的scope里也有全局OA和Fn的OA
4.b = 4;
5.此时才进入Fc的声明创建时,其创建情况参照Fb的创建,得到Fc=[Function]
6.创建{fb: Fb,fc: Fc}对象,并且返回此对象
7.将返回的{fb: Fb,fc: Fc}赋值给fn
8.进入Fb函数运行时,创建Fb的OA对象,并把Fb的OA对象放入到Fb的spoce中,那么现在Fb的scope中有全局OA,Fn的OA和Fb的OA.this指定为fn,
9.处理var和function声明,但Fb函数内没有,所以这里没有作什么处理.
10.在作用域链中找b,首先在Fb的OA上找,没有找到b,那住上找,在Fn的OA中找到b,此时的b值 为4,所以进行运算,得到b=10,
11.输出10
12.类似第8步
13.类似第9步
14.类似第10步,首先在Fc的OA上找,没有找到b,那住上找,在Fn的OA中找到b,此时的b值10,所以进行运算,得到b=8,
15.输出8
理解好上面的过程,就很容易理解作用域链和闭包了.所谓闭包就是返回结果里含有函数OA的引用,造成函数OA不能被清理,所以OA里声明的变量也不能被清理,并且保存了状态.

时间: 2024-11-09 01:02:04

javascript的函数,作用域,闭包的相关文章

javascript中函数作用域之”提升“

javascript中函数作用域之变量提升 当我们在函数内部用关键字var声明一个变量的时候,此变量的作用域限制在当前函数. 提升:在一个作用域内部,不管一个变量用var声明的位置在哪里,这个变量属于当前整个作用域,并且在当前作用域的任何位置都可以访问它.在javascript中,这种行为/现象称之为"提升",即一个变量在一个作用域的任何位置用var声明,javascript引擎都会把这些用var声明的变量"移动"到当前作用域的开始处. 谈到javascript这种

javascript篇-----函数作用域,函数作用域链和声明提前

在一些类似C语言的编程语言中,花括号内的每一段代码都具有各自的作用域,而且变量在声明它们的代码段之外是不可见的(也就是我们不能在代码段外直接访问代码段内声明的变量),我们称之为块级作用域,然而,不同于这类型的编程语言,javascript是没有块级作用域.取而代之的,javascript使用的是块级作用域:变量在声明它们的函数体以及这个函数体嵌套的任意函数体内都是有定义的. 在如下的所示的代码中,在不同位置定义了变量 i . j 和 k ,它们都在同一个作用域内——这三个变量在函数体内均是有定义

JavaScript之函数作用域

有过类似C语言编程经验的同学应该都知道“块级作用域(block scope)”:花括号内的每一段代码都具有各自的作用域,而且在声明它们的代码段之外是不可见的.而在JavaScript中是没有块级作用域的,JavaScript取而代之地使用了“函数作用域(function scope)”:变量在声明它们的函数体以及这个函数体嵌套的任意函数体内都是有定义的. 光用文字解释平白无味,先来段让你为之一振的代码: function myTest(num){ var i = 0; if(num == 222

javascript回调函数,闭包作用域,call,apply函数解决this的作用域问题

在JavaScrip中,function是内置的类对象,也就是说它是一种类型的对象,可以和其它String.Array.Number.Object类的对象一样用于内置对象的管理.因为function实际上是一种对象,它可以“存储在变量中,通过参数传递给(别一个)函数(function),在函数内部创建,从函数中返回结果值”. 因为function是内置对象,我们可以将它作为参数传递给另一个函数,延迟到函数中执行,甚至执行后将它返回.这是在JavaScript中使用回调函数的精髓.本篇文章的剩余部

浅谈JavaScript匿名函数与闭包

一. 匿名函数 //普通函数定义: //单独的匿名函数是无法运行的.就算运行了,也无法调用,因为没有名称. 如: function(){ alert('123'); }                        //语法错误,无法执行 1.简单的使用: var box =  function(){ return 'Lee'; } alert (box());        //需要调用box()方法. 2.通过自我执行来执行匿名函数 (function(){                /

JavaScript匿名函数和闭包

概述 在JavaScript前端开发中,函数与对其状态即词法环境(lexical environment)的引用共同构成闭包(closure).也就是说,闭包可以让你从内部函数访问外部函数作用域.在JavaScript,函数在每次创建时生成闭包.匿名函数和闭包可以放在一起学习,可以加深理解.本文主要通过一些简单的小例子,简述匿名函数和闭包的常见用法,仅供学习分享使用,如有不足之处,还请指正. 普通函数 普通函数由fucntion关键字,函数名,() 和一对{} 组成,如下所示: 1 functi

javascript匿名函数与闭包

匿名函数是相对于有名字的函数 如function setAge(){}函数名为setAge 而类似于function(){}则就是一个匿名函数 有名字的函数执行时可以写成setAge(),而没有名字的函数则执行要写成(function())(); 另一种方式是把匿名函数赋值给一个变量来使用 var a=function(){ alert("hello,world"); } a(); 匿名函数传参 (function(age){ return age; })(100); 匿名函数产生闭包

javascript中函数作用域和声明提前

javascript不像java等其他强类型语句,没有块级作用域(括号内的代码都有自己的作用域,变量在声明它们的代码段之外不可见)一说,但有自己的独特地方,即函数作用域. 函数作用域:变量在声明它们的函数体内以及这个函数体的任意内部函数体内是有定义的. 如下所示代码,在不同位置定义了变量i.j和k,它们都在同一个作用域内,即在函数体内均是有定义的. function test(){ var i=0; //i在整个函数体内均有定义 if(true){ var j=0; //j在整个函数体内均有定义

《你不知道的JavaScript》 函数作用域和块级作用域

一.函数作用域 可用在代码外添加包装函数,将内部的变量和函数定义隐藏. var a = 2; function foo() { // <- - 添加这一行 var a = 3; console.log( a ); //3 } // <- - 以及这一行 foo(); // <- - 以及这一行 console.log( a ); //2 这种技术必须声明一个具名函数foo(),foo本身“污染”了所在作用域.其次,必须显式地通过函数名( foo( ) )调用这个函数才能运行其中的代码.