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 相关文章推荐
Extjs ajax同步请求时post方式参数发送方式
Aug 05 Javascript
jquery实现的一个文章自定义分段显示功能
May 23 Javascript
JQuery的Ajax中Post方法传递中文出现乱码的解决方法
Oct 21 Javascript
你不知道的高性能JAVASCRIPT
Jan 18 Javascript
前端框架Vue.js构建大型应用浅析
Sep 12 Javascript
Node.js复制文件的方法示例
Dec 29 Javascript
微信小程序图片横向左右滑动案例
May 19 Javascript
vue使用$emit时,父组件无法监听到子组件的事件实例
Feb 26 Javascript
vue-test-utils初使用详解
May 23 Javascript
JS实现的排列组合算法示例
Jul 16 Javascript
node.js事件轮询机制原理知识点
Dec 22 Javascript
JavaScript设计模式--简单工厂模式实例分析【XHR工厂案例】
May 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
将博客园(cnblogs.com)数据导入到wordpress的代码
2013/01/06 PHP
Thinkphp调用Image类生成缩略图的方法
2015/03/07 PHP
php版微信公众账号第三方管理工具开发简明教程
2016/09/23 PHP
php实现登录页面的简单实例
2019/09/29 PHP
Javascript YUI 读码日记之 YAHOO.util.Dom - Part.2 0
2008/03/22 Javascript
jquery获取ASP.NET服务器端控件dropdownlist和radiobuttonlist生成客户端HTML标签后的value和text值
2010/06/28 Javascript
基于jquery的自定义鼠标提示效果 jquery.toolTip
2010/11/14 Javascript
原来Jquery.load的方法可以一直load下去
2011/03/28 Javascript
原生javascript和jquery判断浏览器版本等信息
2013/07/04 Javascript
jquery获取当前点击对象的value方法
2014/02/28 Javascript
javascript实现的图片切割多块效果实例
2015/05/07 Javascript
DEDECMS如何为文章添加HOT NEW标志图片
2015/08/14 Javascript
js实现延时加载Flash的方法
2015/11/26 Javascript
AngularJS中指令的四种基本形式实例分析
2016/11/22 Javascript
angular中的http拦截器Interceptors的实现
2017/02/21 Javascript
vue-cli配置文件——config篇
2018/01/04 Javascript
angular实现input输入监听的示例
2018/08/31 Javascript
vue 监听某个div垂直滚动条下拉到底部的方法
2018/09/15 Javascript
基于Nodejs的Tcp封包和解包的理解
2018/09/19 NodeJs
小程序自定义日历效果
2018/12/29 Javascript
微信小程序select下拉框实现效果
2019/05/15 Javascript
JavaScript 防抖和节流遇见的奇怪问题及解决
2020/11/20 Javascript
node.js通过Sequelize 连接MySQL的方法
2020/12/28 Javascript
Python使用pylab库实现绘制直方图功能示例
2018/06/01 Python
DRF使用simple JWT身份验证的实现
2021/01/14 Python
使用CSS3实现一个3D相册效果实例
2016/12/03 HTML / CSS
不开辟用于交换数据的临时空间,如何完成字符串的逆序
2012/12/02 面试题
教师自荐书
2013/10/08 职场文书
新农村建设标语
2014/06/24 职场文书
个人租房协议书范本
2014/09/30 职场文书
英文道歉信
2015/01/20 职场文书
初中班长竞选稿
2015/11/20 职场文书
九年级数学教学反思
2016/02/17 职场文书
退休劳动合同怎么写?
2019/10/25 职场文书
学习nginx基础知识
2021/09/04 Servers
SQL SERVER中的流程控制语句
2022/05/25 SQL Server