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 相关文章推荐
js 鼠标点击事件及其它捕获
Jun 04 Javascript
javascript 动态加载 css 方法总结
Jul 11 Javascript
ExtJs Excel导出并下载IIS服务器端遇到的问题
Sep 16 Javascript
IE8提示Invalid procedure call or argument 异常的解决方法
Sep 30 Javascript
jquery实现的一个简单进度条效果实例
May 12 Javascript
网页右下角弹出窗体实现代码
Jun 05 Javascript
JavaScript严格模式禁用With语句的原因
Oct 20 Javascript
AngularJs 60分钟入门基础教程
Apr 03 Javascript
vue cli webpack中使用sass的方法
Feb 24 Javascript
使用taro开发微信小程序遇到的坑总结
Apr 08 Javascript
jquery实现简单拖拽效果
Jul 20 jQuery
Selenium执行JavaScript脚本的方法示例
Dec 31 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
一个ubbcode的函数,速度很快.
2006/10/09 PHP
PHP如何抛出异常处理错误
2011/03/02 PHP
解决File size limit exceeded 错误的方法
2013/06/14 PHP
利用PHP获取访客IP、地区位置、浏览器及来源页面等信息
2017/06/27 PHP
Laravel访问出错提示:`Warning: require(/vendor/autoload.php): failed to open stream: No such file or di解决方法
2019/04/02 PHP
使用UglifyJS合并/压缩JavaScript的方法
2012/03/07 Javascript
js实现幻灯片播放图片示例代码
2013/11/07 Javascript
jQuery实现可收缩展开的级联菜单实例代码
2013/11/27 Javascript
子页向父页传值示例
2013/11/27 Javascript
instanceof和typeof运算符的区别详解
2014/01/06 Javascript
全屏js头像上传插件源码高清版
2016/03/29 Javascript
React Native 自定义下拉刷新上拉加载的列表的示例
2018/03/01 Javascript
微信小程序云开发之模拟后台增删改查
2019/05/16 Javascript
vue实现固定位置显示功能
2019/05/30 Javascript
使用JavaScript和MQTT开发物联网应用示例解析
2020/08/07 Javascript
jquery简易手风琴插件的封装
2020/10/13 jQuery
[03:21]辉夜杯主赛事 12月25日TOP5
2015/12/26 DOTA
Python3 正在毁灭 Python的原因分析
2014/11/28 Python
python使用socket连接远程服务器的方法
2015/04/29 Python
深度定制Python的Flask框架开发环境的一些技巧总结
2016/07/12 Python
Python 由字符串函数名得到对应的函数(实例讲解)
2017/08/10 Python
Python实现JSON反序列化类对象的示例
2018/01/31 Python
对python使用telnet实现弱密码登录的方法详解
2019/01/26 Python
Python爬虫动态ip代理防止被封的方法
2019/07/07 Python
python创建n行m列数组示例
2019/12/02 Python
Python参数传递及收集机制原理解析
2020/06/05 Python
详解python UDP 编程
2020/08/24 Python
canvas学习总结三之绘制路径-线段
2019/01/31 HTML / CSS
世界领先的26岁以下学生和青少年旅行预订网站:StudentUniverse
2018/07/01 全球购物
数控专业推荐信范文
2013/12/02 职场文书
优秀共青团员事迹材料
2014/12/25 职场文书
商务考察邀请函模板
2015/02/02 职场文书
幼儿园庆六一主持词
2015/06/30 职场文书
互联网创业商业模式以及赚钱法则有哪些?
2019/10/12 职场文书
让JavaScript代码更加精简的方法技巧
2022/06/01 Javascript
MySQL串行化隔离级别(间隙锁实现)
2022/06/16 MySQL