Heim  >  Artikel  >  Web-Frontend  >  javascript 嵌套的函数(作用域链)_javascript技巧

javascript 嵌套的函数(作用域链)_javascript技巧

WBOY
WBOYOriginal
2016-05-16 18:32:361050Durchsuche

嵌套的函数(作用域链)
当你进行函数的嵌套时,要注意实际上作用域链是发生变化的,这点可能看起来不太直观。你可把下面的代码置入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 {
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参数。
Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn