javascript 嵌套的函数(作用域链) |
本文标签:javascript,嵌套 嵌套的函数(作用域链) 当你进行函数的嵌套时,要注意实际上作用域链是发生变化的,这点可能看起来不太直观 。你可把下面的代码置入firebug监视值的变化 。 复制代码 代码如下: var testvar = window属性; var o1 = {testvar:1, fun:function(){alert(o1: +this.testvar+<<);}}; var o2 = {testvar:2, fun:function(){alert(o2: +this.testvar);}}; o1.fun();1 o2.fun();2 o1.fun.call(o2);2 这是本文的首个例子 。 复制代码 代码如下: var testvar = window属性; var o3 = { testvar:3, testvar2:3**, fun:function(){ alert(o3: +this.testvar);//obj3 var inner = function(){ alert(o3-inner: +this.testvar);//window属性 alert(o3-inner: +this.testvar2);//undefined(未定义) }; inner(); } }; o3.fun(); 这里我们换了别的函数,这个函数与原先的函数几乎相似但区别是内部函数的写法 。要注意的是内部函数运行时所在的作用域,和外部函数的作用域是不一样的 。Ext可让你调用函数时指定函数的作用域,避免作用域的问题 。 变量的声明 初始化变量时一定要加上“var”关键字,没有的话这就是一个全局变量 。譬如,在下面的例子中,会有一个变量写在函数内部,然而你打算仅仅是声明局部的变量,但实际也可能出现覆盖全局变量的值的情形 。在FIREBUG "DOM"的标签页中,你可通过检测“window”看到所有的全局变量 。如果你发现有“k”或“x”变量那证明你把这个变量分配在一个不合适的作用域里面 。见下例: 复制代码 代码如下: var i = 4; var j = 5; var k = 7; var fn = function(){ var i = 6; k = 8;//注意前面没有var 所以这句话的意思的把8赋予到变量k中去! alert(i);//6 alert(j);//5 alert(k+-1);//8-1 x = 1;//这句的作用有两种情况,创建全部变量x或覆盖(overwrite)全部变量x }; fn(); alert(k+-2);//8-2 (注意不是7-2) 与前面例子变化不大,另外注意的是函数内的k前面是没有var关键字的,所以这里不是声明局部变量,而是将某个值再次分配到k这个全局变量中 。另外要注意的是,alert方法执行期间,参数i是当前能找到的局部变量,它的值是6,但参数j在当前作用域找不到,就沿着作用域链(scope chain)向上查找,一直找到全局变量的那个j为止 。 在Ext中指定作用域 前面已提及,当调用函数时Ext能灵活处理作用域的问题 。部分内容来自dj的帖子 。 调用函数时,你可以把this想象为每个函数内的一个特殊(躲起来的)参数 。无论什么时候,JavaScript都会把this放到function内部 。它是基于一种非常简单的思想:如果函数直接是某个对象的成员,那么this的值就是这个对象 。如果函数不是某个对象的成员那么this的值便设为某种全局对象(常见有,浏览器中的window对象) 。下面的内部函数可以清晰的看出这种思想 。 一个函数,若是分配到某个变量的,即不属于任何对象下的一员,那么this的参数就变为windows对象 。下面是一个例子,可直接粘贴到Firebug的console: 复制代码 代码如下: var obj = { toString:function(){ return obj的范围内(作用域内);}, //重写toString函数,方便执行console.log(this)时的输出 func: function(){ // 这里的函数直接从属与对象"object" console.log(this); var innerFunc = function(){ //n这里的函数不是特定对象的直接成员,只是另外一个函数的变量而已 console.log(this); }; innerFunc(); } }; obj.func(); // 输出 "obj的范围内(作用域内)" // 输出 "Window的一些相关内容..." 缺省下是这样调用一个参数的-但你也可以人为地改变this参数,只是语法上稍微不同 。将最后一行的"obj.func();" 改为: 复制代码 代码如下: obj.func.call(window); // 输出 "Window的一些相关内容..." // 输出 "Window的一些相关内容..." 从上面的例子中可以发现,call实际上是另外一个函数(方法) 。call 属于系统为obj.func内建的方法(根据JavaScript之特点可得知,函数是一种对象 。) 。 通过这样改变this指向的作用域,我们可以继续用一个例子来修正innerFunc中的this参数,——“不正确”的指向: 复制代码 代码如下: var obj = { toString:function(){ return obj的范围内(作用域内);}, //重写toString函数,方便执行console.log(this)时的输出 func: function(){ // 这里的函数直接从属与对象"object" console.log(this); var innerFunc = function(){ //n这里的函数不是特定对象的直接成员,只是另外一个函数的变量而已 console.log(this); }; innerFunc.call(this); } }; obj.func(); // 输出 "obj的范围内(作用域内)" // 输出 "obj的范围内(作用域内)" Ext的作用域配置 可以看到,没有分配作用域的函数,它的this"指向的是浏览器的window对象(如事件句柄event handler等等),——除非我们改变this的指针 。Ext的很多类中 scope是一个配置项(configuration)能够进行指针的绑定 。相关的例子参考Ajax.request 。 Ext的createDelegate函数 *除了内建的call/apply方法,Ext还为我们提供-- 辅助方法createDelegate 。 该函数的基本功能是绑定this指针但不立刻执行 。传入一个参数,createDelegate方法会保证函数是运行在这个参数的作用域中 。如: 复制代码 代码如下: var obj = { toString:function(){ return obj的范围内(作用域内);}, //重写toString函数,方便执行console.log(this)时的输出 func: function(){ // 这里的函数直接从属与对象"object" console.log(this); var innerFunc = function(){ //n这里的函数不是特定对象的直接成员,只是另外一个函数的变量而已 console.log(this); }; innerFunc = innerFunc.createDelegate(this); // 这里我们用委托的函数覆盖了原函数 。 innerFunc(); // 按照一般的写法调用函数 } }; obj.func(); // 输出 "obj的范围内(作用域内)" // 输出 "obj的范围内(作用域内)" 这是一个小小的例子,其原理是非常基本基础的,希望能够好好消化 。尽管如此,在现实工作中,我们还是容易感到迷惑,但基本上,如果能按照上面的理论知识去分析来龙去脉,万变还是不离其中的 。 另外还有一样东西,看看下面的例子: 复制代码 代码如下: varsDs.load({callback: function(records){ col_length = varsDs.getCount();//这里的varDs离开了作用域? //col_length = this.getCount();//这个this等于store吗? for (var x = 0; x < col_length; x++) { colarray[x] = varsDs.getAt(x).get(hex); } }});不过可以写得更清晰: var obj = { callback: function(records){ col_length = varsDs.getCount();//这里的varDs离开了作用域? //col_length = this.getCount();//这个this等于store吗? // ... } }; varsDs.load(obj);现在函数callback直接挂在obj上,因此this指针等于obj 。 但是注意: 这样做没用的 。为什么?因为你不知obj.callback最终执行时发生什么情形 。试想一下Ext.data.Store的load方法(仿造的实现): 复制代码 代码如下: ... load : function(config) { var o = {}; o.callback = config.callback; //进行加载 o.callback(); } ... 这个仿造的实现中,回调函数的作用域是私有变量“o” 。 因为通常你无法得知函数是如何被调用的,如果不声明作用域,你很可能无法在回调函数中使用this参数 。 |