【JavaScript】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:
- Memory leak patterns in JavaScript by Abhijeet Bhattacharya and Kiran Shivarama Sundar (IBM developerWorks)
- Understanding and Solving Internet Explorer Leak Patterns by Justin Rogers (Microsoft)
- JavaScript Closures by Richard Cornford
http://www.bitstructures.com/2007/11/javascript-method-callbacks.html
【JavaScript】Registering JavaScript object methods as callbacks的更多相关文章
- 【译】延迟加载JavaScript
[译]延迟加载JavaScript 看到一个微信面试题引发的血案 --[译] 什么阻塞了 DOM?中提到的一篇文章,于是决定看下其博客内容,同时翻译下来留作笔记,因英文有限,如有不足之处,欢迎指出.同 ...
- 【概率论】1-3:组合(Combinatorial Methods)
title: [概率论]1-3:组合(Combinatorial Methods) categories: Mathematic Probability keywords: Combination 组 ...
- 【WIP】客户端JavaScript Web Object
创建: 2017/10/11 更新: 2017/10/14 标题加上[WIP],增加[TODO] 更新: 2018/01/22 更改标题 [客户端JavaScript Web Object, UR ...
- Python开发【前端】:JavaScript
JavaScript入门 JavaScript一种直译式脚本语言,是一种动态类型.弱类型.基于原型的语言,内置支持类型.它的解释器被称为JavaScript引擎,为浏览器的一部分,广泛用于客户端的脚本 ...
- 【拾遗】理解Javascript中的Arguments
前言 最近在看JavaScript相关的知识点,看到了老外的一本Javascript For Web Developers,遇到了一个知识盲点,觉得老外写的很明白很透彻,记录下来加深印象,下面是我摘出 ...
- 【WIP】客户端JavaScript 事件处理
创建: 2017/10/15 完成: 2017/10/15 更新: 2017/11/04 加粗事件的参数 更新: 2017/12/12 增加事件处理时获取事件对象的方法 更新: 2019/05/2 ...
- 【JS】312- 复习 JavaScript 严格模式(Strict Mode)
点击上方"前端自习课"关注,学习起来~ 注:本文为 < JavaScript 完全手册(2018版) >第30节,你可以查看该手册的完整目录. 严格模式是一项 ES5 ...
- 【repost】图解Javascript上下文与作用域
本文尝试阐述Javascript中的上下文与作用域背后的机制,主要涉及到执行上下文(execution context).作用域链(scope chain).闭包(closure).this等概念. ...
- 【WIP】客户端JavaScript DOM
创建: 2017/10/12 初步完成: 2017/10/15 更新: 2017/10/14 标题加上[WIP],继续完成 [TODO] 补充暂略的, 搜[略] DOM树 概要 基本 ...
随机推荐
- MVC的路由
MVC的路由包括以下几部分 路由名称,路由URL,路由的初始值,路由的约束,路由的命名空间 routes.MapRoute( name: "Default", url: " ...
- DOM笔记(七):开发JQuery插件
在上一篇笔记本中,讲解了如何利用jQuery扩展全局函数和对象:DOM笔记(六):怎么进行JQuery扩展? 在这篇笔记本中,将开发一个简单的动画插件,名称是example-plugin,用其实现一个 ...
- Http状态码的种类及含义
1xx 临时响应:2xx 成功:3xx 重定向: 4xx 请求错误: 5xx 服务器错误: http://www.hostspaces.net/wenzhang-detail.php?id=198 常 ...
- 转-问自己:UI设计注意的十个问题
UI 设计需要自问的 10个问题 UI 设计的魅力在于,你不仅需要适当的技巧,更要理解用户与程序的关系.一个有效的用户界面关注的是用户目标的实现,包括视觉元素与功能操作在内的所有东西都需要完整一致 ...
- Maven异常: No compiler is provided in this environment. Perhaps you are running on a JRE rather than a JDK解决(能力工场小马哥)
问题描述: No compiler is provided in this environment. Perhaps you are running on a JRE rather than a JD ...
- vim之grep
[vim之grep] :vimgrep 用于多文件搜索,如 1):vim[grep] start_stage * 在当前目录下(不包括子目录)搜索 2) :vim[grep] start_sta ...
- [原创]Devexpress XtraReports 系列 5 创建交叉报表
昨天我们已经介绍了如何创建多栏报表,详见:[原创]Devexpress XtraReports 系列 4 创建多栏报表 今天我们继续我们的XtraReports系列.Demo和数据库文件最后会附上. ...
- 进入GRUB改root用户密码
开机读取倒计时时按任意键----e---->选择第二行 kernel ---->按e, 再按空格 >输入1----回车--->选择kernel输入b----> passw ...
- 巧解Tomcat中JVM内存溢出问题
你对Tomcat 的JVM内存溢出问题的解决方法是否了解,这里和大家分享一下,相信本文介绍一定会让你有所收获. tomcat 的JVM内存溢出问题的解决 最近在熟悉一个开发了有几年的项目,需要把数据库 ...
- 转载sublime text3中package Control
Sublime Text 3 安装Package Control 原来Subl3安装Package Control很麻烦,现在简单的方法来了 一.简单的安装方法 使用Ctrl+`快捷键或者通过View ...