js this 绑定机制深入详解


Posted in Javascript onApril 30, 2020

本文实例讲述了js this 绑定机制。分享给大家供大家参考,具体如下:

函数调用位置

与词法作用域相反的是,this的指向由函数运行时决定,它是动态的,随着函数调用位置变化而变化。

要理解 this,首先要理解调用位置:调用位置就是函数在代码中被调用的位置(而不是声明的位置)。只有仔细分析调用位置才能回答这个问题:这个this到底引用的是什么?

function baz() {
// 当前调用栈是:baz
// 因此,当前调用位置是全局作用域
console.log( "baz" );
bar(); // <-- bar的调用位置
}
function bar() {
// 当前调用栈是baz -> bar
// 因此,当前调用位置在baz中
console.log( "bar" );
foo(); // <-- foo的调用位置
}
function foo() {
// 当前调用栈是baz -> bar -> foo
// 因此,当前调用位置在bar中
console.log( "foo" );
}
baz(); // <-- baz的调用位置

多数现代桌面浏览器都内置了开发者工具,其中包含JavaScript调试器。你可以在工具中给函数的第一行代码设置一个断点,或者直接在第一行代码之前插入一条 debugger;语句。运行代码时,调试器会在那个位置暂停,同时会展示当前位置的函数调用列表,这就是你的调用栈。因此,如果你想要分析this的绑定,使用开发者工具得到调用栈,然后找到栈中第二个元素,这就是真正的调用位置。

this 绑定规则

函数的调用位置决定了this的绑定对象,当我们找到调用位置后,然后判断需要应用下面四条规则中的哪一条。

独立函数调用

独立函数调用,this 指向函数调用位置所在的包含环境对象。

function foo() {
console.log( this.a );
}
var a = 2;
foo(); // 2

作为对象的方法调用

当函数作为某个对象的方法被调用时,this 指向这个对象。

function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
};
obj.foo(); // 2

特别注意:虽然函数foo并不属于obj对象,但调用位置使用obj的上下文来调用函数。我一直在强调调用位置的重要性,因为你可能一不留神就会忽略掉它,看下面的列子:

function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
};
var bar = obj.foo; // 函数别名! 步骤1
var a = "oops, global"; // a是全局对象的属性
bar(); // "oops, global" 步骤2

在步骤1中,变量bar是obj.foo 的一个引用,它实际指向的是函数foo。所以使用bar()与直接使用foo()并没有不同。

使用 .call/ .apply 绑定

每创建一个函数,这个函数就有了两个继承而来的方法:call和apply。

它们的第一个参数是一个对象,它们会把这个对象绑定到this,接着在调用函数时指定这个 this 。因为你可以直接指定 this 的绑定对象,因此我们称之为显式绑定。

function foo() {
console.log( this.a );
}
var obj = {
a:2
};
foo.call( obj ); // 2

new绑定

使用 new 来调用函数,或者说发生构造函数调用时,会自动执行下面的操作。

  1. 创建(或者说构造)一个全新的对象。
  2. 这个新对象会被执行[[原型]]连接,即指向构造函数的原型Foo.prototype。
  3. 这个新对象会绑定到函数调用的 this 。
  4. 如果函数没有返回其他对象,那么 new 表达式中的函数调用会自动返回这个新对象。
function foo(a) {
this.a = a;
}
var bar = new foo(2);
console.log( bar.a ); // 2

使用 new 来调用 foo(..) 时,我们会构造一个新对象并把它绑定到 foo(..) 调用中的 this 上。

优先级

如果要判断一个运行中函数的this绑定,就需要找到这个函数的直接调用位置。找到之后就可以顺序应用下面这四条规则来判断 this 的绑定对象。

  1. 由 new 调用?绑定到新创建的对象。
  2. 由 call 或者 apply (或者 bind )调用?绑定到指定的对象。
  3. 由上下文对象调用?绑定到那个上下文对象。
  4. 默认:在严格模式下绑定到 undefined ,否则绑定到全局对象。

一定要注意,有些调用可能在无意中使用默认绑定规则。如果想“更安全”地忽略 this 绑定,你可以使用一个DMZ对象,比如 ø = Object.create(null) ,以保护全局对象。

感兴趣的朋友可以使用在线HTML/CSS/JavaScript代码运行工具:http://tools.3water.com/code/HtmlJsRun测试上述代码运行效果。

更多关于JavaScript相关内容可查看本站专题:《JavaScript常用函数技巧汇总》、《javascript面向对象入门教程》、《JavaScript错误与调试技巧总结》、《JavaScript数据结构与算法技巧总结》及《JavaScript数学运算用法总结》

希望本文所述对大家JavaScript程序设计有所帮助。

Javascript 相关文章推荐
JavaScript 动态添加表格行 使用模板、标记
Oct 24 Javascript
IE下支持文本框和密码框placeholder效果的JQuery插件分享
Jan 31 Javascript
使用AngularJS 应用访问 Android 手机的图片库
Mar 24 Javascript
js实现仿MSN带关闭功能的右下角弹窗代码
Sep 04 Javascript
jQuery插件uploadify实现ajax效果的图片上传
Jun 18 Javascript
js操作XML文件的实现方法兼容IE与FireFox
Jun 25 Javascript
jQuery插件FusionWidgets实现的AngularGauge图效果示例【附demo源码】
Mar 23 jQuery
jQuery实现多张图片上传预览(不经过后端处理)
Apr 29 jQuery
vue.js实现价格格式化的方法
May 23 Javascript
three.js实现3D影院的原理的代码分析
Dec 18 Javascript
JS实现点餐自动选择框(案例分析)
Dec 10 Javascript
javascript实现移动端触屏拖拽功能
Jul 29 Javascript
JS 图片压缩原理与实现方法详解
Apr 29 #Javascript
详解Vue3 Composition API中的提取和重用逻辑
Apr 29 #Javascript
浅谈Vue3 Composition API如何替换Vue Mixins
Apr 29 #Javascript
JS数组Reduce方法功能与用法实例详解
Apr 29 #Javascript
JavaScript实现放大镜效果代码示例
Apr 29 #Javascript
React组件设计模式之组合组件应用实例分析
Apr 29 #Javascript
Vue + Node.js + MongoDB图片上传组件实现图片预览和删除功能详解
Apr 29 #Javascript
You might like
PDO::query讲解
2019/01/29 PHP
Laravel validate error处理,ajax,json示例
2019/10/25 PHP
PHP的imageTtfText()函数深入详解
2021/03/03 PHP
JavaScript 特殊字符
2007/04/05 Javascript
JQUERY设置IFRAME的SRC值的代码
2010/11/30 Javascript
FireFox下XML对象转化成字符串的解决方法
2011/12/09 Javascript
JS实现淡蓝色简洁竖向Tab点击切换效果
2015/10/06 Javascript
基于jQuery实现美观且实用的倒计时实例代码
2015/12/30 Javascript
js实现点击按钮弹出上传文件的窗口
2016/12/23 Javascript
微信小程序实现弹出层效果
2020/05/26 Javascript
js中null与空字符串&quot;&quot;的区别讲解
2019/01/17 Javascript
JS实现checkbox互斥(单选)功能示例
2019/05/04 Javascript
Vue实现数据请求拦截
2019/10/23 Javascript
小程序分享链接onShareAppMessage的具体用法
2020/05/22 Javascript
详解vue-router的导航钩子(导航守卫)
2020/11/02 Javascript
[01:58]最残酷竞争 2016国际邀请赛中国区预选赛积分循环赛回顾
2016/06/28 DOTA
[57:24]LGD vs VGJ.T 2018国际邀请赛小组赛BO2 第二场 8.16
2018/08/17 DOTA
[47:52]DOTA2-DPC中国联赛正赛 iG vs LBZS BO3 第二场 3月4日
2021/03/11 DOTA
Python绘制KS曲线的实现方法
2018/08/13 Python
pygame游戏之旅 如何制作游戏障碍
2018/11/20 Python
python找出一个列表中相同元素的多个索引实例
2019/06/11 Python
如何用Python制作微信好友个性签名词云图
2019/06/28 Python
HTML5新增属性data-*和js/jquery之间的交互及注意事项
2017/08/08 HTML / CSS
Chantelle仙黛尔内衣美国官网:法国第一品牌内衣
2018/07/26 全球购物
Fanatics官网:运动服装、球衣、运动装备
2020/10/12 全球购物
int *p=NULL和*p= NULL有什么区别
2014/10/23 面试题
工作中个人的自我评价
2013/12/31 职场文书
同学聚会策划方案
2014/06/06 职场文书
新闻传播专业求职信
2014/07/22 职场文书
2014年教师节寄语
2014/08/11 职场文书
出纳试用期自我鉴定范文
2014/09/16 职场文书
教代会闭幕词
2015/01/28 职场文书
十一月早安语录:把心放轻,人生就是一朵自在的云
2019/11/04 职场文书
MySQL之MyISAM存储引擎的非聚簇索引详解
2022/03/03 MySQL
Nginx HTTP跳转至HTTPS
2022/05/15 Servers
彻底弄懂Python中的回调函数(callback)
2022/06/25 Python