`

Registering JavaScript object methods as callbacks(注册一个对像方法为回调函数[解决回调函数的上下文问题])

 
阅读更多
Bitstructures

Registering JavaScript object methods as callbacks

The registration of callback functions is very common in JavaScript web programming, for example to attach user interface event handlers (such as onclick), or to provide a function to handle an XHR response. Registering an object method as a callback function is not entirely straightforward, but there are a number of approaches that we can use.

Let's say we've got a constructor, an object, and a function that registers a callback:

function MyObject(val){
    this.val = val;
}
MyObject.prototype.alertVal = function(){
    alert(this.val);
}

var obj = new MyObject(8);

function register(callback){
    // some time later...
    callback();
}

The constructor stores its single argument, and the alertVal() method alerts it. The simple register() function takes a function and calls it. In a real situation the behaviour here would be much more interesting but this will do for illustration.

Why we don't want to just pass obj.alertVal to register()

Object methods are first-class functions in JavaScript and we could pass obj.alertVal to register() -- but it isn't quite what we want. Let's see what happens:

register(obj.alertVal);
// inside register(), callback === obj.alertVal
callback()
// inside MyObject.prototype.alertVal
alert(this.val);
// because callback() was not called on an object,
// this === the JavaScript global object and not obj;
// this.val is the global variable val, not obj.val

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.) When we passed obj.alertVal to register() we were passing a reference to the function bound to obj.alertVal, but no reference to the object obj.

So, we need to bind our method to the object.

Closure with an anonymous function

In JavaScript, whenever a function is defined within another one a closure is created [JavaScript Closures for Dummies ] [JavaScript Closures ]. A closure remembers the variable bindings that were in scope when the function was created. These bindings are then available whenever the function is called. We can bind our method to our object instance with the following:

register(function(){obj.alertVal()});

Whenever the anonymous function is called, "obj" will be bound to the value that it had when the function was created. Which is exactly what we want.

(If we execute the above code outside a function it will behave differently. No closure will be created, instead, the current value of the global variable "obj" will be used whenever the anonymous function is called, not the value at function definition.)

If we want to register a method on the object currently bound to "this", we need to take an extra step:

var obj = this;
register(function(){obj.alertVal()});

If we don't explicitly bind "this" to a named variable (obj) and instead use register(function(){this.alertVal()}) we will lose our object reference. "this" will be bound to the JavaScript global object whenever the anonymous function is called.

Build a generic closure maker

Instead of building a closure each time we want to register a method as a callback, we could write a utility function to do it for us. For example:

function bind(toObject, methodName){
    return function(){toObject[methodName]()}
}

With such a function we can then register our method with:

register(bind(obj, "alertVal"));

Dojo (dojo.hitch()) and Prototype (bind() ) both have such utility functions (that also allow you to provide arguments to pass to the method when called, something that our "bind" doesn't do.)

If we want to register a method of "this", we don't need to explicitly bind it (as we did above) before calling "bind" -- the function call does the binding for us. register(bind(this, "alertVal")) works as expected.

Alter the register function to take the object too

If we changed our register function to:

function register(anObject, methodName){
    // some time later...
    anObject[methodName]();
}

We could register our call with:

register(obj, "alertVal");

dojo.connect and YUI's YAHOO.util.Event.addListener() [YUI Event Utility ] [YAHOO.util.Event API docs ] both include this binding style in their API.

Bind the method to the object at construction

We could bind our method to the object (or instance variables as shown here) in the constructor function:

function MyObject(val){
    this.alertVal = function(){
        alert(val);
    }
}

We could then register obj.alertVal directly as "val" is already bound:

obj = new MyObject(8);
register(obj.alertVal);

Douglas Crockford writes about this programming style in Private Members in JavaScript.

Circular references and memory leaks

Whichever method you use you need to be careful about avoiding circular references when registering event handlers (such as onclick) on document objects. For example, if we register an object method as an event handler on an element, such that the method is bound to the object, and the object has a reference back to the element, then we have a circular reference, and a potential leak (in this case a solution would be to have the object store the element's id rather than store a reference to the element object itself.) Here are some articles that discuss ways to cause memory leaks and ways to avoid them:

About me

Simon Bates is a software developer living and working in Toronto.

Starting points

Colophon

This blog is running on a Python application that I built with Django . The source code is available under the MIT license.

分享到:
评论

相关推荐

    python中实现延时回调普通函数示例代码

    回调函数是我们在python编程中经常会遇到的一个问题,而想在将来某一时刻进行函数回调,可以使用call_later()函数来实现,第一个参数是回调用延时,第二个是回调的函数名称 例子如下: import asyncio def ...

    Remote COM registering远处COM注册

    Remote COM registering远处COM注册

    javascript权威指南(第六版)

    包含英文和中文两个版本附有本书源码. Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xiii 1. Introduction...

    python中利用Future对象回调别的函数示例代码

    Future对象也可以像协程一样,当它设置完成结果时,就可以立即进行回调别的函数 例子如下: import asyncio import functools def callback(future, n): print('{}: future done: {}'.format(n, future.result())...

    Registering_3DSC_ICP.rar

    1、VS2015与PCL.1.9.1配置; 2、项目中包括代码及pcd点云数据; 3、对点云进行粗精配准,并且计算分析误差;

    JEDEC JESD82-31A:2019 DDR4 Registering Clock Driver Definition (

    JEDEC JESD82-31A:2019 DDR4 Registering Clock Driver Definition (DDR4RCD02) - 完整英文电子版(194页).zip

    JESD--DDR4 Registering Clock Driver Spec PDF

    JESD--DDR4 Registering Clock Driver Spec PDF

    Simple Book Registering System in Python Free Source Code.zip

    Simple Book Registering System in Python Free Source Code.zip

    eureka-server Springcloud 服务注册

    服务注册 Springcloud eureka-server 服务注册 Springcloud eureka-server 服务注册 Springcloud eureka-server

    Spring中文帮助文档

    2.6.4. 将Spring 应用程序上下文部署为JCA adapter 2.6.5. 计划任务 2.6.6. 对Java 5 (Tiger) 支持 2.7. 移植到Spring 2.5 2.7.1. 改变 2.8. 更新的样例应用 2.9. 改进的文档 I. 核心技术 3. IoC(控制反转)...

    regsvr32.exe问题解决方案

    今天电脑打不开所有的邮箱和QQ空间,我都被...和什么流氓软件之类的东东造成的,估计是浏览器的问题,后来了解到jscript.dll 没被正确注册可能会导致这种现象的出现,结果用regsvr32 jscript.dll命令 后一切恢复正常。

    Thank you for registering Visual Studio 2010 Express for Windows Phone.

    Thank you for registering Visual Studio 2010 Express for Windows Phone.

    Kindle如何注册

    介绍如何注册介绍如何注册介绍如何注册介绍如何注册介绍如何注册

    Spring API

    2.6.4. 将Spring 应用程序上下文部署为JCA adapter 2.6.5. 计划任务 2.6.6. 对Java 5 (Tiger) 支持 2.7. 移植到Spring 2.5 2.7.1. 改变 2.8. 更新的样例应用 2.9. 改进的文档 I. 核心技术 3. IoC(控制反转)...

    ZendFramework中文文档

    为每个动作设置上下文 7.8.4.3.4. 初始化上下文开关 7.8.4.3.5. 另外的功能 7.8.4.3.6. AjaxContext 函数 7.8.4.4. FlashMessenger 7.8.4.4.1. 简介 7.8.4.4.2. Basic Usage Example 7.8.4.5. JSON 7.8.4.6...

    rxdb-utils:RxDB缺少的部分

    您可以一个一个地注册它们-只选择您想要添加的那些,或全部使用register()进行register() ; 注册所有插件 import * as RxDB from 'rxdb' ; import memory from 'pouchdb-adapter-memory' ; import register from '...

    oracle jdbc补丁

    解决“Error while registering Oracle JDBC Diagnosability MBean.”把文件解压覆盖到目录$Oracle_Home\product\11.1.0\db_1\jdbc\lib 下,重新开启oracle sql developer就解决了。

    Registering_FPFH_ICP.rar

    1、VS2015与PCL.1.9.1配置; 2、项目中包括代码及pcd点云数据; 3、对点云进行粗精配准,并且计算分析误差;

    Registering_PFH_ICP.rar

    1、VS2015与PCL.1.9.1配置; 2、项目中包括代码及pcd点云数据; 3、对点云进行粗精配准,并且计算分析误差;

    Registering_NDT_ICP.rar

    1、VS2015与PCL.1.9.1配置; 2、项目中包括代码及pcd点云数据; 3、对点云进行粗精配准,并且计算分析误差;

Global site tag (gtag.js) - Google Analytics