关于javascript 回调函数中变量作用域的讨论

1、背景
Javascript中的回调函数,相信大家都不陌生,最明显的例子是做Ajax请求时,提供的回调函数,
实际上DOM节点的事件处理方法(onclick,ondblclick等)也是回调函数。
在使用DWR的时候,回调函数可以作为第一个或者最后一个参数出现,如:
JScript code function callBack(result){ } myDwrService.doSomething(param1,param2,callBack);//DWR的推荐方式 //或者 myDwrService.doSomething(callBack,param1,param2);

2、问题描述
最近在使用Dojo+Dwr的时候,碰到一个问题:
如果回调函数是属于某个对象(记为obj1)的方法,等到DWR执行该回调函数的时候,
上下文却不是obj1。
表现的现象就是在回调函数中访问obj1的任何属性都是undefined。
版本:Dojo1.3.1和dwr2
3、模拟问题的代码
下面的测试代码可以模拟这个问题:
JScript code


代码如下:

<html>
<head>
<script type="text/javascript"><!--
    var context="全局";
    var testObj={
        context:"初始",
        callback:function (str){
            //回调函数
            alert("callback:我所处的上下文中,context="+this.context+",我被回调的方式:"+str);
        }
    };
    //创建一个对象,作为测试回调函数的上下文
    testObj.context="已设置";
    function testCall(){
        callMethod(testObj.callback);
        callObjMethod(testObj,testObj.callback);
    }
    function callMethod(method){
        method("通过默认上下文回调");
    }
    function callObjMethod(obj,method){
        method.call(obj,"指定显式对象上下文回调");
    }
// --></script>
</head>
<body> <a href="javascript:void(0)" onclick="testCall()">调用测试</a> </body>
</html>

在callObjMethod方法中,我用了两种方式回调“method"方法:
第一种方式:method("通过默认上下文回调");
没有指定上下文,我们发现回调函数内部访问context的值是全局变量的值,
这说明,执行该方法的默认上下文是全局上下文。
第二种方式:method.call(obj,"指定显式对象上下文回调");
指定obj为method执行的上下文,就能够访问到对象内部的context。

4、研究DWR
因为06年使用DOJO+DWR(1.0)的时候,已经遇到过这个问题,当时没做太多功课,直接改了dwr的源代码。
现在用dwr2,于是想先看看DWR是不是对这个问题有新的处理方式,
将dwr.jar中的engine.js拿出来,查看了有关回调的相关代码(_remoteHandleCallback和_execute),
发现对回调的处理方式似乎比1.0更简单,没办法将对象和方法一起传过去。
5、做进一步的研究
因为这次DWR在项目中的使用太广泛,而且我相信这样的需求应该是可以满足的,于是没有立刻修改源码,
首先,在Google上搜Dojo+dwr,没有查到什么结论,可能Dojo的用户不是太多。
于是又搜”javascript callback object context“,得到一篇文章专门介绍java回调函数的问题:
http://bitstructures.com/2007/11/javascript-method-callbacks
最重要的一句话:
When a function is called as a method on an object (obj.alertVal()),
"this" is bound to the object that it is called on (obj).
And when a function is called without an object (func()),
"this" is bound to the JavaScript global object (window in web browsers.)
这篇文章也提供了解决方案,就是使用Closure和匿名方法,
在javascript中,在function内部创建一个function的时候,会自动创建一个closure,
而这个closure就能记住对应的function创建时的上下文。
所以,如果这样:
JScript code var closureFunc=function(){ testObj.callback(); }
那么无论在什么地方,直接调用closureFunc()和调用testObj.callback()是等价的。
详情参见上面提到的文章:http://bitstructures.com/2007/11/javascript-method-callbacks。
6、改进模拟代码
模拟代码只,我们再增加一种回调方式:
JScript code


代码如下:

<html>
<head>
<script type="text/javascript"><!--
    var context="全局";
    var testObj={
    context:"初始",
    callback:function (str){
        //回调函数
        alert("callback:我所处的上下文中,context="+this.context+",我被回调的方式:"+str);
        }
    };
    //创建一个对象,作为测试回调函数的上下文
    function testCall(){
        callMethod(testObj.callback);
        callWithClosure(function(param){testObj.callback(param);});
        testObj.context="已设置";
        callObjMethod(testObj,testObj.callback);
    }
    function callMethod(method){ method("通过默认上下文回调"); }
    function callWithClosure(method){ method("通过Closure保持上下文回调"); }
    function callObjMethod(obj,method){ method.call(obj,"指定显式对象上下文回调"); }
// --></script>
</head>
<body> <a href="javascript:void(0)" onclick="testCall()">调用测试</a> </body>
</html>

测试以上代码,我们可以发现,通过Closure和通过显示指定对象得到的效果一致。
7、模拟更加真实的调用情景
但是以上代码还有一个问题,通常在真实环境中,如果回调函数是对象中方法,那么发起请求的方法也处在同一个对象,
在javascript中,this也可以代表当前对象,但不能直接用在匿名function中用,比如:
JScript code var testObj={ context:"初始", callback:function (str){//回调函数 alert("callback:我所处的上下文中,context="+this.context+",我被回调的方式:"+str); }, caller:function(){ callWithClosure(function(param){this.callback(param);}); } };//创建一个对象,作为测试回调函数的上下文
以上代码中的this指的不是testObj,而是全局上下文,
需要在closure外写一个临时变量来代表this,完整的代码如下:
JScript code


代码如下:

<html>
<head>
<script type="text/javascript"><!--
var context="全局";
var testObj={
    context:"初始",
    callback:function (str){
        //回调函数
        alert("callback:我所处的上下文中,context="+this.context+",我被回调的方式:"+str);
    },
    caller:function(){
        callWithClosure(function(param){this.callback(param);});
        var temp=this;
        callWithClosure(function(param){temp.callback(param);});
    }
};
//创建一个对象,作为测试回调函数的上下文
testObj.context="已设置";
function testCall(){
    //callMethod(testObj.callback);
    testObj.caller();
    //callWithClosure(function(param){testObj.callback(param);});
    //callObjMethod(testObj,testObj.callback);
}

function callObjMethod(obj,method){method.call(obj,"指定显式对象上下文回调"); }
function callMethod(method){ method("通过默认上下文回调"); }
function callWithClosure(method){ method("通过Closure保持上下文回调"); }
function callback(str){ alert("callback:我是定义在外部的全局函数。"); }
// --></script>
</head>
<body>
<a href="javascript:void(0)" onclick="testCall()">调用测试</a>
</body>
</html>

8、什么是Closure
Two one sentence summaries:
a closure is the local variables for a function - kept alive after the function has returned,
or
a closure is a stack-frame which is not deallocated when the function returns. (as if a 'stack-frame' were malloc'ed instead of being on the stack!)

(0)

相关推荐

  • VBScript中变量作用域

    下午在写程序的时候,碰到个变量重定义的问题,具体是在一个函数中的两个地方定义了相同的变量,两个变量分别放在IF语句的两部分中,本来以为这两次定义在语句中不同的块,应该没有影响,然而在运行中IIS却提示变量重定义,把重复定义去掉即正确. 解决了问题之后突然想到在Fdream的blog看过一篇文章<JavaScript变量无块级作用域>,似乎这两个是差不多的,在VBScript中变量同样没有块级作用域. 再次去看了那篇文章之后我也做了下试验,得到结果:在VBScript中,函数中定义的变量的作用域

  • 理解JavaScript变量作用域更轻松

    JavaScript本身作为一门简单的语言,就其变量作用域问题一样令不少人头晕,这主要是因为JavaScript闭包的存在.本文不打算深入讲解JavaScript变量作用域问题(其实本人也没有能力能把这一话题讲的深入些),也不讲"闭包"话题,本文只讨论最实用的JavaScript作用域知识点. 一.JavaScript作用域分类 JavaScript就两种作用域:全局(window).函数级(function).函数级(function)不要理解为"块级(大括号{}级)&qu

  • Javascript变量作用域详解

    变量的作用域指的是变量的可见性,而生命周期则(存活期)则是从另一个角度考察变量. JS中变量的作用域分为全局变量和局部变量,函数内定义的称为局部变量,函数外的称为全局变量.("函数外的称为全局变量"是相对的,另此处讨论的前提是用var显式声明的变量,函数内不用var定义的变量默认是全局变量,当然忽略var声明变量是不赞成的). 复制代码 代码如下: var glob = 4;//函数外声明全局变量 function fun() {     var height = 20; //函数内用

  • js变量作用域及可访问性的探讨

    每一种语言都有变量的概念,变量是用来存储信息的一个元素.比如下面这个函数: 复制代码 代码如下: function Student(name,age,from)  {   this.name = name;    this.age = age;   this.from = from;    this.ToString = function()   {    return "my information is name: "+this.name+",age : "+t

  • javascript中的变量作用域以及变量提升详细介绍

    变量作用域"一个变量的作用域表示这个变量存在的上下文.它指定了你可以访问哪些变量以及你是否有权限访问某个变量." 变量作用域分为局部作用域和全局作用域. 局部变量(处于函数级别的作用域)不像其他对面对象的编程语言(比方说C++,Java等等),javascript没有块级作用域(被花括号包围的):当是,javascript有拥有函数级别的作用域,也就是说,在一个函数内定义的变量只能在函数内部访问或者这个函数内部的函数访问(闭包除外,这个我们过几天再写个专题). 函数级别作用域的一个例子

  • 有关js的变量作用域和this指针的讨论

    一.变量作用域:[P71] 这一句话说的非常精辟:"在ECMAScript中,只有两种执行环境,全局环境和函数环境,每个函数都是一个执行环境,包括嵌套函数.换句话说,其他情况下即使变量声明在一对大括号中,在括号外部仍然可以访问这些变量".以下给出例子: 复制代码 代码如下: for(var i=0; i<5; i++) { var num = 20; // 在for语句中声明的变量 } alert(num); // 在for语句外部调用变量,仍然可以得到num的值 对异常语句也同

  • 关于JavaScript中var声明变量作用域的推断

    一.迷思!由一段代码引发的疑惑 请看如下代码: 复制代码 代码如下: for(var i=0;i<3;i++) { console.log(j+","+k); for(var j=0;j<3;j++) { var k = j+1; } } console.log(i); 输出结果: undefined,undefined 3,3 3,3 3 如果你是搞c.java等语言的,可能你会不解,为何j.k这种局部变量可以被作用域外的代码访问呢? 如果JavaScript中用var声

  • JavaScript的变量作用域深入理解

    在学习JavaScript的变量作用域之前,我们应当明确几点: a.JavaScript的变量作用域是基于其特有的作用域链的. b.JavaScript没有块级作用域. c.函数中声明的变量在整个函数中都有定义. 1.JavaScript的作用域链 首先看下下面这段代码: 复制代码 代码如下: <script type="text/javascript" language="javascript"> var rain = 1; function rain

  • ECMAscript 变量作用域总结概括

    使用var操作符声明的变量与未使用var操作符声明的变量区别 Javascript是遵循ECMAScript标准下的一个产物,自然ECMAScript的标准其要遵循 ECMAScript的变量是松散类型的,即可以用来保存任何类型的数据(未经初始化的变量,会保存一个特殊值undefined). 未使用var操作符声明 function test() { message='hi'; console.log(message); } console.log(message); 未使用var操作符声明的变

  • 关于javascript 回调函数中变量作用域的讨论

    1.背景 Javascript中的回调函数,相信大家都不陌生,最明显的例子是做Ajax请求时,提供的回调函数, 实际上DOM节点的事件处理方法(onclick,ondblclick等)也是回调函数. 在使用DWR的时候,回调函数可以作为第一个或者最后一个参数出现,如: JScript code function callBack(result){ } myDwrService.doSomething(param1,param2,callBack);//DWR的推荐方式 //或者 myDwrSer

  • 微信小程序中使用javascript 回调函数

     微信小程序中使用javascript 回调函数 回调函数原理: 我现在出发,到了通知你" 这是一个异步的流程,"我出发"这个过程中(函数执行),"你"可以去做任何事,"到了"(函数执行完毕)"通知你"(回调)进行之后的流程 点击此处转载参考文献 感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

  • ajax异步回调函数中给外部变量赋值的问题探讨

    复制代码 代码如下: <!doctype html> <head> <meta charset="utf-8"> </head> <body> <script src="http://localhost/UIBMSPHPProj/public/js/jquery-1.8.2.min.js"></script> <script> $(function(){ var str

  • 关于JavaScript回调函数的深入理解

    前言 JavaScript回调函数是成为一名成功的 JavaScript 开发人员必须要了解的一个重要概念.但是我相信,在阅读本文之后,你将能够克服以前使用回调方法遇到的所有障碍. 在开始之前,首先要确保我们对函数的理解是扎实的. 快速回顾:JavaScript 函数 什么是函数? 函数是在其中有一组代码的逻辑构件,用来执行特定任务.实际上为了易于调试和维护,函数允许以更有组织的方式去编写代码.函数还允许代码重用. 你只需定义一次函数,然后在需要时去调用它,而不必一次又一次地编写相同的代码. 声

  • javascript回调函数的概念理解与用法分析

    本文实例讲述了javascript回调函数的概念理解与用法.分享给大家供大家参考,具体如下: 一. 回调函数的作用 js代码会至上而下一条线执行下去,但是有时候我们需要等到一个操作结束之后再进行下一个操作,这时候就需要用到回调函数. 二. 回调函数的解释 因为函数实际上是一种对象,它可以存储在变量中,通过参数传递给另一个函数,在函数内部创建,从函数中返回结果值",因为函数是内置对象,我们可以将它作为参数传递给另一个函数,到函数中执行,甚至执行后将它返回,它一直被"专业的程序员"

  • javascript回调函数详解

    在高级语言层出不穷的年代, 各个语言都号称有着一切皆为对象的自豪说法, 而 js 作为一门脚本语言却相对于java等传统面向对象语言有很大的不同之处, 除了 js 诡异的继承体系之外, 最令人着迷的一个特性就是回调函数, 当然也有很多人对他诟病, 笔者认为 回调函数 和 异步 是js语言特性的两大最为突出的店, 当然正如所有优点需要满足自我的需求, 这个世界没有银弹, 比如大量的使用回调函数将会使你的代码冗余, 错乱影响代码人的视觉与思维体验. 本文是自己对学习回调函数的的体会, 难免不完善甚至

  • es6函数中的作用域实例分析

    本文实例讲述了es6函数中的作用域.分享给大家供大家参考,具体如下: 一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域(context).等到初始化结束,这个作用域就会消失.这种语法行为,在不设置参数默认值,是不会出现的. var x = 1; function f(x, y = x) { console.log(y); } f(2) // 2 上面的代码中,参数y的默认值等于变量x.调用函数f时,参数形成一个单独的作用域.在这个作用域里面,默认值变量x指向第一个参数x,

  • 浅谈vue使用axios的回调函数中this不指向vue实例,为undefined

    今天在vue-cli脚手架搭建的项目中使用axios时,遇到无法解析this.$route的报错信息,最后发现是作用域的问题. 1.解决方法:使用 => 原代码: axios.get('/user', { params: { ID: 12345 } }) .then(function (response) { console.log(response); }) .catch(function (error) { console.log(error); }); 修改为: axios.get('/u

  • C++11 lambda表达式在回调函数中的使用方式

    目录 一.lambda表达式在C++异步框架中的应用 二.如何在C-style注册回调函数中使用lambda表达式? 在回调函数中使用lambda表达式的好处,在于可以利用C++的RAII机制来做资源的自动申请释放,避免手动管理出错. 一.lambda表达式在C++异步框架中的应用 1. 一个boost asio的例子 // // async_tcp_echo_server.cpp // ~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-202

  • 关于Javascript回调函数的一个妙用

    前言 其实回调函数简单通俗点就是当有a和b两个函数,当a作为参数传给b,并在b中执行,这时a就是一个回调(callback)函数,如果a是一个匿名函数,则为匿名回调函数那下面们来通过一个实例来具体解释下Javascript回调函数怎么使用. 实例 在很久很久以前,有一个人. var person; 他是个人,也就是一个对象. person= {}; // 在JavaScript中,花括号就代表是一个对象 他有个名字叫小明. person.name = '小明'; 看一下是不是真的叫小明. ale

随机推荐