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 相关文章推荐
js 父窗口控制子窗口的行为-打开,关闭,重定位,回复
Apr 20 Javascript
JS下载文件|无刷新下载文件示例代码
Apr 17 Javascript
利用a标签自动解析URL分析网址实例
Oct 20 Javascript
深入理解JavaScript系列(33):设计模式之策略模式详解
Mar 03 Javascript
Bootstrap每天必学之折叠
Apr 12 Javascript
老生常谈 js中this的指向
Jun 30 Javascript
js内置对象处理_打印学生成绩单的简单实现
Sep 24 Javascript
15个非常实用的JavaScript代码片段
Dec 18 Javascript
JS实现的自动打字效果示例
Mar 10 Javascript
Vue.js 中的 $watch使用方法
May 25 Javascript
vue实现学生录入系统之添加删除功能
Jul 11 Javascript
详解Vue中$props、$attrs和$listeners的使用方法
Feb 18 Vue.js
详解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
PHP 第二节 数据类型之字符串类型
2012/04/28 PHP
PHP中的替代语法简介
2014/08/22 PHP
php二维码生成
2015/10/19 PHP
Windows平台实现PHP连接SQL Server2008的方法
2017/07/26 PHP
页面中body onload 和 window.onload 冲突的问题的解决
2009/07/01 Javascript
JQuery autocomplete 使用手册
2010/04/01 Javascript
基于jquery实现的表格分页实现代码
2011/06/21 Javascript
表单类各种类型(文本框)失去焦点效果jquery代码
2013/04/26 Javascript
常规表格多表头查询示例
2014/02/21 Javascript
滚动条响应鼠标滑轮事件实现上下滚动的js代码
2014/06/30 Javascript
一道常被人轻视的web前端常见面试题(JS)
2016/02/15 Javascript
JavaScript简单实现鼠标移动切换图片的方法
2016/02/23 Javascript
Ajax使用原生态JS验证用户名是否存在
2020/05/26 Javascript
Angular 中 select指令用法详解
2016/09/29 Javascript
javascript深拷贝的原理与实现方法分析
2017/04/10 Javascript
js 用于检测类数组对象的函数方法
2017/05/02 Javascript
详解原生js实现offset方法
2017/06/15 Javascript
浅谈Vue路由快照实现思路及其问题
2018/06/07 Javascript
详解Vue iview IE浏览器不兼容报错(Iview Bable polyfill)
2019/01/07 Javascript
jQuery zTree树插件的使用教程
2019/08/16 jQuery
[54:25]Ti4 循环赛第三日LGD vs MOUZ
2014/07/12 DOTA
使用Python编写一个简单的tic-tac-toe游戏的教程
2015/04/16 Python
Django自定义分页与bootstrap分页结合
2021/02/22 Python
python利用微信公众号实现报警功能
2018/06/10 Python
django框架cookie和session用法实例详解
2019/12/10 Python
详解从Django Allauth中进行登录改造小结
2019/12/18 Python
python matplotlib工具栏源码探析二之添加、删除内置工具项的案例
2021/02/25 Python
canvas因为图片资源不在同一域名下而导致的跨域污染画布的解决办法
2019/01/18 HTML / CSS
马来西亚银饰品牌:JEOEL
2017/12/15 全球购物
施华洛世奇波兰官网:SWAROVSKI波兰
2019/06/18 全球购物
巴西化妆品商店:Lojas Rede
2019/07/26 全球购物
校园网站的创业计划书范文
2013/12/30 职场文书
前台文员职责范本
2014/03/07 职场文书
营业员岗位职责范本
2015/04/14 职场文书
JavaScript数组reduce()方法的语法与实例解析
2021/07/07 Javascript
win10系统xps文件怎么打开?win10打开xps文件的两种操作方法
2022/07/23 数码科技