关于 javascript 闭包及函数原型 作用链域 总结

来源:互联网 发布:ui 设计软件 编辑:程序博客网 时间:2024/05/16 17:38

js中的魔鬼三角。需要一个反复的过程,这一次似乎懂了,下一次又忘记了。再转回来复习一下。所谓:为伊消得人憔悴,衣带渐宽终不悔。

 

1.javascript 闭包的作用:

2.javascript 函数原型:

                                     原型查找机制:查找操作总是先查找对象本身,然后再检查构造函数的原型。

 

 

 

3.作用链域:

这里需要先讲一下 执行环境,javascript是以函数为中心的语言。每个函数都会有自己的“执行环境”,这些执行环境是彼此独立的,但是却存在某种神秘的联系。每发生一次调用,脚本引擎就需要预先为函数创建一个“执行环境”。脚本引擎类似于浏览器中的虚拟机,用来对javascript脚本的解析,预编译,执行。当发生调用的时候,脚本引擎就会用来维护这些“执行环境”。

fnA-->fnB--->fnC....

函数fnA调用fnB的时候,引擎就会先把fnA的执行环境保存起来,等fnB返回得到时候,再恢复fnA的执行环境,继续FnA后续的操作。一般来说 会用栈来维护执行环境的数据结构或者称为“执行环境栈”,发生调用之前,先把上一个函数的执行环境做入栈操作,被调函数返回后,做出栈操作。显然,在这个“执行环境”栈中,当前活动的“执行环境”位于栈的最顶部。

 

此步骤是 建立“执行环境栈”。

建立这个“环境”有时三个重要的步骤:1.建立活动对象2.分配“作用链域”3.绑定 this

实例:

var userName ="大漠穷秋"

function outer(outerArg0){

   var outerVar=1;

   function inner (innerArg0){

  var innerVar =1;

}

inner(1);

  alert(this.userName);

}

outer(0);

 

 在外部函数outer开始执行时,建立"执行环境"的过程开始了:

 第一步:创建“活动对象”,暂且称之为activeObj,此时outer函数的活动对象是这样的:

ActiveObj:{outerArg0:0,outerVar:undefined,inner:undefined}

其中,outerArg0是outer函数定义的参数,outerVar 是outer中局部变量,inner是其中的嵌套内部函数,这些参数都成为这个“活动对象”的属性。

第二步:分配 作用域链:js代码是解释执行的,在函数被执行前,解释器必然会预先扫描一下代码。在这个扫描的过程中,对于outer函数来数,会建立一个“函数对象”,相当于我们使用new Function来创建函数。它里面隐藏着一些内部属性,这些属性是供脚本引擎执行函数使用的,它对程序员是不可见的,在这些属性里,有一个[[scope]],那么引擎给函数分配的“作用域链”就保存在这里。

[[scope]] 可以看成是数组或者链表,在为outer函数建立对象的时候,默认的会往里面push一个“顶级作用域”的windows对象。显然当outer函数刚刚建立的时候,作用域链是这样的[window]但是,当我们开始调用outer函数的时候,有一个重要的步骤,再把第一步中创建的“活动对象”插入到这个链表的头部。所以此时,outer函数的作用域链成为[outer.ActiveObj,window]。

第三步:绑定this。函数内部的this默认都是指向全局对象window。所以如果我们没有手工为函数outer指定this,所以这里的this指向的是window.

所以这里会弹出“大漠穷秋”。

 

接着 我们来看看inner函数的执行环境的创建过程:

第一步:创建“活动对象”,,此时inner函数的活动对象是这样的:

ActiveObj:{innerArg0:0,innerVar:undefined} 与outer类比 很容易就明白。

第二步:分配 作用域链:这里出现了最大的不同点:因为inner是outer函数的内嵌函数,所以它的默认作用域链是这样的[{outer的活动对象},window]。然后再把第一步的活动对象插入到链表头部,变成[inner.ActiveObj,[{outer的活动对象},window]。

第三步:绑定this。函数内部的this默认都是指向全局对象window。所以如果我们没有手工为函数inner指定this,所以这里的this仍然指向的是window.

 

函数总在定义它的作用域运行,而不是在执行它的作用域运行。作用域在编译的时候就确定了,和以后的调用无关。

 

以上的步骤实在函数被调用的时候触发。

 

到这里就是js构建作用域链的过程。函数的执行环境和函数是一个整体,他们是不可分割的,比如说定义好了inner函数后,它的作用域链中就自动形成这样一个结构[{outer活动对象},window].这就是所谓的"词法作用域"的奥义,也是理解闭包的奥义。

 

 标志符解析:属性查找的机制,从作用域链的第一个对象依次往后查找,如果在对象上找到所需要的属性就立刻返回,否则继续,如果一直找到window还一无所获,则返回undefined。这个和prototype查找机制很相似。

当函数返回他的内嵌函数的时候,确切的说 不仅返回了内嵌函数指针,同时也返回了内嵌函数的作用域链,而这个内嵌函数的作用域链上恰恰报含了外部函数的属性记录,所以这才是闭包魔法的根源。

 

闭包的官方定义:闭包是一个表达式(通常是一个函数),可以有任意参数,连同绑定了这些参数的环境一起构成。所以闭包不是一个单纯的东西,它是函数及其作用域链的综合体。

 

修改作用链域和this:在某些情况下,操作和控制作用链域和this是非常必要的编码技巧。

js提供了原生的with来修改作用域链。使用 call和apply来动态绑定函数中的this.

 

var user={userName:"大漠穷秋",pwd:12345};

var mobileNumer="诺基亚";

function  outer(outerArg0){

  var outVar=1;

 function inner(innerArg0){

   var innerVar=2;

   alert(this.mobileNumber);

}

inner.call({mobileNumber:"联想"},3);

}

 outer(1);

 

此时函数中的this就指向了我们传递过去的{mobileNumber:"联想"}对象。

 

所以 javascript  和java c语言中的this,不太一样,它不再是一根筋,它会根据实际情况做出改变这就是所谓的"动态绑定"。

 

 

 

 

 

原创粉丝点击