JS中this的4种绑定规则详解


Posted in Javascript onFebruary 04, 2020

JS中this是什么

理解this之前, 先纠正一个观点,this 既不指向函数自身,也不指函数的词法作用域。

如果仅通过this的英文解释,太容易产生误导了。

它实际是在函数被调用时才发生的绑定,也就是说this具体指向什么,取决于你是怎么调用的函数。也就是说谁调用的this,this就指向谁

JS中this说明

ES6中的箭头函数采用的是词法作用域。

为什么要使用this:使API设计得更简洁且易于复用。

this即不指向自身,也不指向函数的词法作用域。

this的指向只取决于函数的调用方式

this绑定规则

new > 显示绑定 > 隐式绑定 > 默认绑定

默认绑定

当独立函数调用时,不管是否在调用栈中,this都指向全局对象(浏览器中为window)

严格模式下,不能将全局对象用于默认绑定。

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

隐式绑定

当函数引用有上下文对象时,隐式绑定规则会把函数调用中的this绑定到这个上下文对象。

对象属性引用链中只有最后一层在调用位置中起作用。

要求:对象内部必须包含一个指向函数的属性,该对象可通过这个属性间接引用函数。

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

隐式丢失

function foo() {
  console.log( this.a );
}
var obj = {
  a: 2,
  foo: foo
};
var bar = obj.foo; // 这里bar将引用foo函数本身,所以不带有函数对象的上下文
var a = "oops, global"; // a是全局对象的属性
bar(); // "oops, global"

和回调函数的情况下(参数传递时的隐式赋值)

function foo() {
  console.log( this.a );
}
function doFoo(fn) {
  // 参数传递时,相当于fn = obj.foo,就和上个例子一样了
  fn(); // <-- call-site!
}
var obj = {
  a: 2,
  foo: foo
};
var a = "oops, global"; // `a` also property on global object
doFoo( obj.foo ); // "oops, global"

显式绑定

采用call()和apply(),通过传入一个对象(若为基本类型,会被封装函数转为对象—装箱),将this绑定到该对象。

硬绑定

function foo() {
  console.log( this.a );
}
var obj = {
  a: 2
};
var bar = function() {
  foo.call( obj );
};
bar(); // 2
setTimeout( bar, 100 ); // 2
// 硬绑定后bar无论怎么调用,都不会影响foo函数的this绑定
bar.call( window ); // 2

硬绑定的典型应用是如下的包裹函数:

function foo(something) {
  console.log( this.a, something );
  return this.a + something;
}
var obj = {
  a: 2
};
var bar = function() {
  return foo.apply( obj, arguments ); // 将obj对象硬编码进去
};
var b = bar( 3 ); // 2 3
console.log( b ); // 5

即将内部函数用apply硬绑定到某个对象,无论怎么调用这个包裹函数,都不会影响内部函数的this。

bind辅助函数如下:

function foo(something) {
  console.log( this.a, something );
  return this.a + something;
}
// simple `bind` helper
function bind(fn, obj) {
  return function() {
    return fn.apply( obj, arguments ); // 利用参数将obj传入进去
  };
}
var obj = {
  a: 2
};
var bar = bind( foo, obj ); // bind( foo, obj )会返回一个包裹函数
var b = bar( 3 ); // 2 3
console.log( b ); // 5

总结:上述包裹函数,想要包裹其他函数,只能一个一个重复写,硬编码的方式导致不能被重用,当某种功能需要多次重复使用时,将其抽象出来,成为函数。

new绑定

任何函数都可能被用作构造函数,当函数被new操作符“构造调用”时,会执行下面操作:

1. 创建一个新对象(若该函数不是JS内置的,则创建一个新的Object对象);

2. 将this绑定到这个对象;

3. 执行构造函数中的代码(为这个新对象添加属性);

4. 若函数没有返回其他对象,则自动返回这个新对象;若函数有return返回的是非对象,则还是自动返回这个新对象,即覆盖那个非对象。

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

补充说明

间接引用

function foo() {
  console.log( this.a );
}
var a = 2;
var o = { a: 3, foo: foo };
var p = { a: 4 };
o.foo(); // 3
(p.foo = o.foo)(); // 2,由于p.foo = o.foo的返回值是目标函数的引用,所以调用位置是foo(),而不是p.foo()或o.foo()

箭头函数:不使用这四个this规则,根据词法作用域来决定this。

function foo() {
  // 返回一个箭头函数
  return (a) => {
    // `this` here is lexically adopted from `foo()`
    console.log( this.a );
  };
}
var obj1 = {
  a: 2
};
var obj2 = {
  a: 3
};
// foo()不是箭头函数,他的this被绑定到obj1
var bar = foo.call( obj1 ); // foo.call( obj1 )返回箭头函数,所以bar为箭头函数
bar.call( obj2 ); // 2! 箭头函数的this无法被修改,new也不行

如下为和箭头函数一样的模式:

function foo() {
  var self = this; // lexical capture of `this`
  setTimeout( function(){
    console.log( self.a );
  }, 100 );
}
var obj = {
  a: 2
};

更多关于JS中this对象的相关文章大家可以点击下面的相关链接

Javascript 相关文章推荐
DOM_window对象属性之--clipboardData对象操作代码
Feb 03 Javascript
js去空格技巧分别去字符串前后、左右空格
Oct 21 Javascript
查询json的数据结构的8种方式简介
Mar 10 Javascript
extjs_02_grid显示本地数据、显示跨域数据
Jun 23 Javascript
浅析jQuery中使用$所引发的问题
May 29 Javascript
Javascript Event(事件)的传播与冒泡
Jan 23 Javascript
你真的了解BOM中的history对象吗
Feb 13 Javascript
node.js用fs.rename强制重命名或移动文件夹的方法
Dec 27 Javascript
React Navigation 使用中遇到的问题小结
May 08 Javascript
jQuery实现鼠标移入移出事件切换功能示例
Sep 06 jQuery
微信公众号平台接口开发 菜单管理的实现
Aug 14 Javascript
jquery轻量级数字动画插件countUp.js使用详解
Oct 17 jQuery
详解JavaScript中精度失准问题及解决方法
Feb 04 #Javascript
Preload基础使用方法详解
Feb 03 #Javascript
使用PreloadJS加载图片资源的基础方法详解
Feb 03 #Javascript
使用preload预加载页面资源时注意事项
Feb 03 #Javascript
jQuery实现小火箭返回顶部特效
Feb 03 #jQuery
JS常用正则表达式超全集(密码强度校验,金额校验,IE版本,IPv4,IPv6校验)
Feb 03 #Javascript
微信小程序实现上传多个文件 超过10个
Mar 30 #Javascript
You might like
smarty模板嵌套之include与fetch性能测试
2010/12/05 PHP
PHP中的Memcache详解
2014/04/05 PHP
高性能PHP框架Symfony2经典入门教程
2014/07/08 PHP
php+mysql查询实现无限下级分类树输出示例
2016/10/03 PHP
PHP面向对象五大原则之单一职责原则(SRP)详解
2018/04/04 PHP
最简单的js图片切换效果实现代码
2011/09/24 Javascript
javascript改变position值实现菜单滚动至顶部后固定
2013/01/18 Javascript
JavaScript显示当然日期和时间即年月日星期和时间
2013/10/29 Javascript
解析offsetHeight,clientHeight,scrollHeight之间的区别
2013/11/20 Javascript
js 去掉空格实例 Trim() LTrim() RTrim()
2014/01/07 Javascript
json格式数据的添加,删除及排序方法
2016/01/21 Javascript
前端性能优化及技巧
2016/05/06 Javascript
100行代码理解和分析vue2.0响应式架构
2017/03/09 Javascript
Angular2中select用法之设置默认值与事件详解
2017/05/07 Javascript
vue 自定义指令自动获取文本框焦点的方法
2018/08/25 Javascript
element-ui多文件上传的实现示例
2019/04/10 Javascript
vue读取本地的excel文件并显示在网页上方法示例
2019/05/29 Javascript
layui使用数据表格实现购物车功能
2019/07/26 Javascript
Vue通过provide inject实现组件通信
2020/09/03 Javascript
[02:09:59]火猫TV国士无双dota2 6.82版本详解(下)
2014/09/29 DOTA
[06:45]2018DOTA2亚洲邀请赛 4.5 SOLO赛 Sccc vs Maybe
2018/04/06 DOTA
在Python 2.7即将停止支持时,我们为你带来了一份python 3.x迁移指南
2018/01/30 Python
python使用celery实现异步任务执行的例子
2019/08/28 Python
如何使用python自带IDLE的几种方法
2020/10/10 Python
html5 sessionStorage会话存储_动力节点Java学院整理
2017/07/06 HTML / CSS
全球性的在线鞋类品牌:Public Desire
2019/04/03 全球购物
Carmen Sol官网:购买果冻鞋、手袋和配件
2021/01/01 全球购物
最新的大学生找工作自我评价
2013/09/29 职场文书
商务会议邀请函
2014/01/09 职场文书
《水上飞机》教学反思
2014/04/10 职场文书
先进员工获奖感言
2014/08/14 职场文书
机关驾驶员违规检讨书
2014/09/13 职场文书
2014医学院领导班子对照检查材料思想汇报
2014/09/19 职场文书
史上最牛的辞职信
2015/02/28 职场文书
Grafana可视化监控系统结合SpringBoot使用
2022/04/19 Redis
Linux磁盘管理方法介绍
2022/06/01 Servers