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


Posted in Javascript onMarch 15, 2010

嵌套的函数(作用域链)
当你进行函数的嵌套时,要注意实际上作用域链是发生变化的,这点可能看起来不太直观。你可把下面的代码置入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参数。
Javascript 相关文章推荐
jQuery 入门级学习笔记及源码
Jan 22 Javascript
跟着JQuery API学Jquery 之三 筛选
Apr 09 Javascript
firefox事件处理之自动查找event的函数(用于onclick=foo())
Aug 05 Javascript
20个非常棒的 jQuery 幻灯片插件和教程分享
Aug 23 Javascript
热点新闻滚动特效的js代码
Aug 17 Javascript
javascript 循环调用示例介绍
Nov 20 Javascript
toggle()隐藏问题的解决方法
Feb 17 Javascript
jquery结合CSS使用validate实现漂亮的验证
Jan 29 Javascript
JS实现的添加弹出层并完成锁屏操作示例
Apr 07 Javascript
浅谈Fetch 数据交互方式
Dec 20 Javascript
Vue开发之watch监听数组、对象、变量操作分析
Apr 25 Javascript
小程序rich-text组件如何改变内部img图片样式的方法
May 22 Javascript
JavaScript 嵌套函数指向this对象错误的解决方法
Mar 15 #Javascript
JQuery 常用操作代码
Mar 14 #Javascript
Jquery Ajax学习实例 向页面发出请求,返回XML格式数据
Mar 14 #Javascript
Jquery cookie操作代码
Mar 14 #Javascript
javascript 解决表单仍然提交即使监听处理函数返回false
Mar 14 #Javascript
jQuery第三课 修改元素属性及内容的代码
Mar 14 #Javascript
jQuery 第二课 操作包装集元素代码
Mar 14 #Javascript
You might like
PHP 最大运行时间 max_execution_time修改方法
2010/03/08 PHP
PHP中如何判断AJAX提交的数据
2012/02/05 PHP
Yii2使用swiftmailer发送邮件的方法
2016/05/03 PHP
详细解读php的命名空间(二)
2018/02/21 PHP
同一个表单 根据要求递交到不同页面的实现方法小结
2009/08/05 Javascript
转换json格式的日期为Javascript对象的函数
2010/07/13 Javascript
关于Ext中form移除textfield方法:hide(),setVisible(false),remove()
2010/12/02 Javascript
jquery ajax同步异步的执行最终解决方案
2013/04/26 Javascript
jQuery遍历之next()、nextAll()方法使用实例
2014/11/08 Javascript
javascript通过获取html标签属性class实现多选项卡的方法
2015/07/27 Javascript
jQuery实现标题有打字效果的焦点图代码
2015/11/16 Javascript
javascript+css3 实现动态按钮菜单特效
2016/02/06 Javascript
基于Node的React图片上传组件实现实例代码
2017/05/10 Javascript
想用好React的你必须要知道的一些事情
2017/07/24 Javascript
Vue写一个简单的倒计时按钮功能
2018/04/20 Javascript
微信小程序学习笔记之目录结构、基本配置图文详解
2019/03/28 Javascript
Nuxt的动态路由和参数校验操作
2020/11/09 Javascript
[03:08]迎霜节狂欢!2018年迎霜节珍藏Ⅰ一览
2018/12/25 DOTA
Python ljust rjust center输出
2008/09/06 Python
python开发之IDEL(Python GUI)的使用方法图文详解
2015/11/12 Python
Python系统监控模块psutil功能与经典用法分析
2018/05/24 Python
使用EduBlock轻松学习Python编程
2018/10/08 Python
python批量赋值操作实例
2018/10/22 Python
Django中自定义admin Xadmin的实现代码
2019/08/09 Python
python使用itchat模块给心爱的人每天发天气预报
2019/11/25 Python
Tensorflow 模型转换 .pb convert to .lite实例
2020/02/12 Python
jupyter 使用Pillow包显示图像时inline显示方式
2020/04/24 Python
css实例教程 一款纯css3实现的超炫动画背画特效
2014/11/05 HTML / CSS
网站域名和主机:Domain.com
2019/04/01 全球购物
教师绩效考核方案
2014/01/21 职场文书
委托书的写法
2014/09/16 职场文书
美丽人生观后感
2015/06/03 职场文书
法定授权委托证明书
2015/06/18 职场文书
读《儒林外史》有感:少一些功利,多一些真诚
2020/01/19 职场文书
Nginx工作原理和优化总结。
2021/04/02 Servers
为什么MySQL 删除表数据 磁盘空间还一直被占用
2021/10/16 MySQL