理解 JS 回调函数中的 this
任何变量或对象都有其赖以生存的上下文。如果简单地将对象理解为一段代码,那么对象处在不同的上下文,这段代码也会执行出不同的结果。
例如,我们定义一个函数 getUrl
和一个对象 pseudoWindow
。
function getUrl() {
console.log(this.document.URL);
}
var pseudoWindow = {
document: {
URL: "I'm fake URL"
},
getUrl1: getUrl,
getUrl2: function (callback) {
callback();
this.func = callback;
this.func();
}
}
执行 getUrl()
,打印出当前页面的 URL。
执行 pseudoWindow.getUrl1()
,打印出 I'm fake URL
。
执行 pseudoWindow.getUrl2(getUrl)
,先打印出当前页面 URL,后打印 I'm fake URL
。
下面让我们用最简单粗暴的语言来解释以上代码。
概念
什么是 this?
this 就是函数调用使用的上下文。
什么是上下文?
上下文是在句号标记法中,句号前面的那个东西。
例如 pseudoWindow.getUrl1
,pseudoWindow
是 pseudoWindow.getUrl1()
的上下文。
什么是自由变量?
当一个变量没有绑定到任何上下文时(或者说绑定到顶级作用域时,例如浏览器中的 window),它就是自由变量。
什么是变量与对象?
变量就是代码中你所用的标识符,一个标识符就是一个变量,多个变量可能指向同一个对象。例如:
pseudoWindow.getUrl1 === getUrl // 得到 true
变量所处的上下文就是对象的作用域。
代码分解
调用 getUrl()
首先 getUrl
函数是定义在全局环境中,它是一个自由变量,在浏览器中(以下描述均为浏览器环境)它的上下文就是 window
,所以 window.getUrl()
和 getUrl()
是等价的。因此 this
指向 window
对象,打印出当前 URL。
调用 pseudoWindow.getUrl1()
首先 pseudoWindow
是一个对象,它可以充当上下文角色。我们给它定义了一个属性 getUrl1
,你可以将属性视为被绑定到某个上下文的变量,变量 getUrl1
本身又指向了变量 getUrl
所指向的对象,所以 pseudoWindow.getUrl1 === getUrl
才会为 true
。
当我们调用 pseudoWindow.getUrl1()
时,它的意思是执行 getUrl()
这段代码,执行代码所需的参数为空,上下文为 pseudoWindow
。
所以函数中的 this
指向了 pseudoWindow
,而 pseudoWindow
对象恰好又有 document
属性,该属性恰好又有 URL
属性,因此打印出 I'm fake URL
。
调用 pseudoWindow.getUrl2(getUrl)
同理我们又定义了一个变量 getUrl2
,并绑定到 pseudoWindow
对象身上,使之成为后者的一个属性。而这个属性本身又指向一个匿名函数,我们姑且称之为 A,该函数对象接受另一个函数对象作为回调函数。
因此执行 pseudoWindow.getUrl2(getUrl)
时,意思是执行代码 A,执行代码所需的参数为 getUrl
这段代码,上下文为 pseudoWindow
。
因此函数 A 中的 this
指向了 pseudoWindow
。
当程序执行到函数 A 内部的 callback()
时,因为变量 callback
没有绑定到任何上下文,因此它相当于一个自由变量,它的上下文就指向了 window
对象,因此首先打印出当前页面的 URL。
接下来 this.func = callback
意味着三件事:
- 我们新申明了一个变量
func
。 - 通过
=
操作符,我们将该变量指向了callback
所指向的函数对象。 - 通过
.
操作符,我们将该变量绑定到了this
对象上,使之成为后者的一个属性,而本例中this
指向的就是pseudoWindow
对象。
于是当程序执行到 this.func()
时,它的意思是执行 callback
这段代码,执行代码所需的参数为空,上下文为 pseudoWindow
。于是打印出了 I'm fake URL
。
这段代码带来的一个副作用是我们隐式地为 pseudoWindow
对象添加了一个新的属性 func
,如果我们想要通过回调的方式打印出 pseudoWindow
的 document.URL
属性,又不想对 pseudoWindow
对象造成任何影响,那么我们可以使用函数的 apply
方法。所有函数都有 apply
方法,它会将它接收的第一个参数设置为函数的上下文。
例如本例中我们可以改写代码成这样子:
var pseudoWindow = {
document: {
URL: "I'm fake URL"
},
getUrl1: getUrl,
getUrl2: function (callback) {
callback();
callback.apply(this);
}
}
严格地说,你应该先检查 callback 参数类型是否是函数对象。
总结
Javascript 支持将函数作为参数传递,回调函数变量指向的函数对象都未与任何上下文绑定,所有未与明确上下文绑定的变量都是自由变量,浏览器器中所有自由变量的上下文都是 window 对象。
理解 JS 回调函数中的 this的更多相关文章
- 如何理解JS回调函数
1.回调函数英文解释: A callback is a function that is passed as an argument to another function and is execut ...
- 理解JS回调函数
我们经常会用到客户端与Web项目结合开发的需求,那么这样就会涉及到在客户端执行前台动态脚本函数,也就是函数回调,本文举例来说明回调函数的过程. 首先创建了一个Web项目,很简单的一个页面,只有一个bu ...
- JS回调函数中的this指向(详细)
首先先说下正常的this指向问题 什么是this:自动引用正在调用当前方法的.前的对象. this指向的三种情况 1. obj.fun() fun中的this->obj,自动指向.前的对 ...
- 简单理解js回调函数
前言 其实回调函数简单通俗点就是当有a和b两个函数,当a作为参数传给b,并在b中执行,这时a就是一个回调(callback)函数,如果a是一个匿名函数,则为匿名回调函数那下面们来通过一个实例来具体解释 ...
- => 应用在js回调函数中
=> 可以简化以前的回调函数的调用,具体来说: 今后,几乎所有的回调函数都可用箭头函数简化 比如: 1. 所有回调函数都可: 去function改=> 2. 如果函数体只有一句话: 可省略 ...
- js回调函数(callback)理解
Mark! js学习 不喜欢js,但是喜欢jquery,不解释. 自学jquery的时候,看到一英文词(Callback),顿时背部隐隐冒冷汗.迅速google之,发现原来中文翻译成回调.也就是回调函 ...
- js回调函数的理解
js回调函数(callback)理解 Mark! 讲之前说一句 function say(){ alert(,,,,,,,,) } var say=function (){ alert(,,,,,,, ...
- 使用匿名函数在回调函数中正确访问JS循环变量
有时候, 需要以不同的参数调用某个URL,并且在回调函数中仍然可以访问正在使用的参数, 这时候, 需要使用闭包保存当前参数, 否则, 当回调函数执行时, 之前的参数很可能早已被修改为最后一个参数了. ...
- 妙谈js回调函数的理解!
很有共鸣,之前也是一直对回调函数感觉不明不白的,自己也看了不少解释说明.后来我觉得造成很多人对回调理解困难的一个原因就是,我在开发中见到的大多数使用了回调函数的情况都是直接上来就 传一个回调函数进去 ...
随机推荐
- paip.hibernate save 失败的解决
paip.hibernate save 失败的解决 打开log debug level,,看不见insert 代码... Hibernate select max(ID).txt ...
- SSL在https和MySQL中的原理思考
之前对HTTPS通信过程有过了解,HTTPS是应用HTTP协议使用SSL加密的版本,在TCP和HTTP之间增加SSL协议.通过握手阶段认证双方身份,协商对称秘钥对通信信息进行加密.此处只描述常用的服务 ...
- Android JNI HelloWorld实现
创建一个JNIDemo的Android工程 在项目下创建一个文件夹jni.(注意必须是jni目录) 在jni目录下创建两个文件:Android.mk 和 first_jni.c(.c文件的名字可以任意 ...
- DataGridViewComboBoxColumn值无效
值无效,可能是你下拉框选项,没有这样的值,而你却设置这个值. dataGridView1.Rows[i].Cells[].Value = "选项一"; 解决方法就是在窗体的构造函数 ...
- 修復jquery的tablesorter对加了千分位的数字无法正确排序的bug
找到函数: function getElementText(config, node) { var text = ""; if (!node) return "" ...
- centos7防火墙那些事
转发设置 firewall-cmd --permanent --add-forward-port=port=80:proto=tcp:toport=8080 firewall-cmd --perma ...
- Linux下tomcat部署
进入Tomcat下的bin目录 cd /usr/local/tomcat/bin 如果你想直接干掉Tomcat,你可以使用kill命令,直接杀死Tomcat进程 kill -9 7010 然后继续查看 ...
- 构建自己的NSZombie
当开启 xcode zombie 选项,发送消息到一个被 "释放了的对象" 时 ObjZomies *oz = [[ObjZomies alloc] init]; oz.nam ...
- 仿网易漂亮的TAB选项卡(标签)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- Swift编程语言SequenceType协议中的一些比较有用的接口
在Swift编程语言中,大部分容器类(比如Array.Dictionary)都实现了SequenceType协议.SequenceType协议中有不少有趣且简便的方法可用来实现我们不少实际需求.这里将 ...