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 相关文章推荐
用Javascript读取中文COOKIE的解决办法
Feb 15 Javascript
js与运算符和或运算符的妙用
Feb 14 Javascript
分享2个jQuery插件--jquery.fileupload与artdialog
Dec 26 Javascript
jquery获取table指定行和列的数据方法(当前选中行、列)
Nov 07 Javascript
JS运动特效之链式运动分析
Jan 24 Javascript
vue2.0之多页面的开发的示例
Jan 30 Javascript
js操作二进制数据方法
Mar 03 Javascript
详解Vue-cli中的静态资源管理(src/assets和static/的区别)
Jun 19 Javascript
vue.js内置组件之keep-alive组件使用
Jul 10 Javascript
JS/HTML5游戏常用算法之碰撞检测 地图格子算法实例详解
Dec 12 Javascript
Javascript Web Worker使用过程解析
Mar 16 Javascript
vue 如何从单页应用改造成多页应用
Oct 23 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中计算程序运行时间的类代码
2012/11/03 PHP
关于Iframe如何跨域访问Cookie和Session的解决方法
2013/04/15 PHP
php中FTP函数ftp_connect、ftp_login与ftp_chmod用法
2014/11/18 PHP
使用新浪微博API的OAuth认证发布微博实例
2015/03/27 PHP
php实现的错误处理封装类实例
2017/06/20 PHP
PHP实现转盘抽奖算法分享
2020/04/15 PHP
JS中toFixed()方法引起的问题如何解决
2012/11/20 Javascript
鼠标选择动态改变网页背景颜色的JS代码
2013/12/10 Javascript
JQuery设置获取下拉菜单某个选项的值(比较全)
2014/08/05 Javascript
JavaScript实现查找字符串中第一个不重复的字符
2014/12/29 Javascript
jQuery中:focus选择器用法实例
2014/12/30 Javascript
Jquery表单验证失败后不提交的解决方法
2016/10/18 Javascript
微信小程序 保留小数(toFixed)详细介绍
2016/11/16 Javascript
Vue表单验证插件Vue Validator使用方法详解
2017/04/07 Javascript
get  post jsonp三种数据交互形式实例详解
2017/08/25 Javascript
详解http访问解析流程原理
2017/10/18 Javascript
详解Vue中watch的高级用法
2018/05/02 Javascript
基于vue的验证码组件的示例代码
2019/01/22 Javascript
使用原生JS实现滚轮翻页效果的示例代码
2020/05/31 Javascript
python中sys.argv参数用法实例分析
2015/05/20 Python
理解Python垃圾回收机制
2016/02/12 Python
python 打印直角三角形,等边三角形,菱形,正方形的代码
2017/11/21 Python
利用python将xml文件解析成html文件的实现方法
2017/12/22 Python
Python并行分布式框架Celery详解
2018/10/15 Python
python打包生成的exe文件运行时提示缺少模块的解决方法
2018/10/31 Python
利用pandas向一个csv文件追加写入数据的实现示例
2020/04/23 Python
如何利用python发送邮件
2020/09/26 Python
美国购买和销售礼品卡平台:Raise
2017/01/13 全球购物
亚马逊墨西哥站:Amazon.com.mx
2018/08/26 全球购物
Internet体系结构
2014/12/21 面试题
单位未婚证明范本
2014/01/18 职场文书
创业计划书的主要内容有哪些
2014/01/29 职场文书
诉讼授权委托书范本
2014/10/05 职场文书
《一面五星红旗》教学反思
2016/02/23 职场文书
关于Vue Router的10条高级技巧总结
2021/05/06 Vue.js
详解Spring Security中的HttpBasic登录验证模式
2022/03/17 Java/Android