我所理解的JavaScript中的this指向


Posted in Javascript onSeptember 04, 2020

前言

JS 中的 this 指向是一个经常被问到的问题,网上也有很多文章是关于 this 的。本文整理一下我理解下的 this 以及一些我比较疑惑的关于 this 问题。

this 指向

有几个 this 的指向问题是几乎每篇文章都会说的,比如作为函数直接调用,作为对象的方法调用, new 运算符执行中的 this 行为。比较通用的说法是, this 指向的是直接调用该函数的对象。其实也很好理解,就是为什么需要 this 这个关键字,就是我们有需要在函数内部对调用函数的对象进行操作的需求。但是有时候我们遇到的情况并不是像书上或 mdn 上遇到的典型的情况, this 的行为可能就会让我们感到有点疑惑。

函数的直接调用

当我们直接调用一个已经声明的函数,那么在非严格模式下,该函数内部的 this 指向的是全局对象,浏览器环境下就是 window 对象。

function f1(){
 return this;
}
//在浏览器中:
f1() === window; //在浏览器中,全局对象是window

//在Node中:
f1() === global;

当函数是在全局环境下定义的时候,这种现象是可以理解的,因为全局环境下定义的函数其实就是挂载在全局对象上的一个属性,比附上面的 f1 也可以理解为 window.f1。但我认为严格模式下的行为才是更符合 this 这个关键字的目的的,特别是我们的函数可能是在非全局环境(比如另一个函数中)定义和调用的,这种情况下 this 还指向 window 是不太合理的。所以在严格模式下,一个函数直接调用,它的 this 指向的是 undefined,如果我们想要得到非严格模式下的结果,那我们调用函数的方法就要改为 window.f1(),而如果函数是在非全局环境下定义的话,那么始终返回的是 undefined。我认为这样的行为是更符合逻辑的。

'use strict'
function d () {
 function e() {
  console.log(this)
 }
 console.log(this)
}

d()
//undefined
//undefined

window.d()
//Window{}
//undefined

这里在全局模式下使用 use strict 只是为了测试,实际使用还是尽量放在函数内局部使用严格模式,全局下的严格模式很容易导致出错。

函数作为对象的属性调用

这也是在代码中非常常见的场景,我认为这是比函数调用更好理解,也更能帮助我们理解 this 行为的场景。简单的来说就是 this 指向的是 直接 调用函数的那个对象。并且要注意的是,这跟函数在哪里定义的是无关的,我们看 this,看的就是从哪里调用的函数。

//在对象内部定义
var o = {
 prop: 37,
 f: function() {
 return this.prop;
 }
};

console.log(o.f()); // 37

//在对象外部定义
var o = {prop: 37};

function independent() {
 return this.prop;
}

o.f = independent;

console.log(o.f()); // 37

//在对象内部定义,但是给外部变量引用并执行
var o = {
 prop: 37,
 f: function() {
  console.log(this)
 return this.prop;
 }
};
var prop = 100;
var m = o.f;
console.log(m());
//Window{}
//100

上面的段落我给 直接 这两个字加粗了,想要表达的意思是当我们通过多个对象的属性嵌套找到并调用函数,那么最后那个最接近函数的对象就是函数 this 的指向。

var o = {
 a:10,
 b:{
  a:12,
  fn:function(){
   console.log(this.a); //12
  }
 }
}
o.b.fn();

var o = {
 a:10,
 b:{
  // a:12,
  fn:function(){
   console.log(this.a); //undefined
  }
 }
}
o.b.fn();

为什么我说这个场景能够帮助我们理解,原因就是它反映出 this 这个关键字的本质。JS 中的函数也是一种对象,在我们的执行环境中的活动对象保存的也只是函数对象的一个引用,如果这个引用是保存在活动对象中的某个对象的属性中(即我们通过活动对象中的某个对象的属性找到该函数),那么函数执行的时候 this 就会指向这个对象,这也是为什么多层对象的调用,还是最靠近函数的那个对象作为 this。虽然在代码中我们的函数是在对象中定义的,但是实际在内存中,对象中只保存着函数的引用,函数自己是在一个单独的内存空间中。所以我们通过哪个对象找到函数并执行,函数中的 this 就指向这个对象。上面的直接调用 this 返回 undefined 也是说得通的。

通过原型的调用

有时我们是通过原型来执行公用的函数,此时已然符合我们上面的逻辑,我们通过哪个实例 找到 函数,那么 this 就指向那个实例。

var o = {
 f: function() { 
 return this.a + this.b; 
 }
};
var p = Object.create(o);
p.a = 1;
p.b = 4;

console.log(p.f()); // 5

箭头函数

箭头函数并没有自己的 this,箭头函数中的 this是它所在的执行环境中的 thismdn 写的是封闭的词法环境),当你遇到箭头函数中的 this 不确定的时候,你可以想象把这个箭头函数换成 console.log(this),这个 console 的输出就是箭头函数中 this 的值,并且箭头函数的 this 是绑定的,不会改变(有时候看上去改变了是所在的 context 改变了)。还有一点需要注意的是,用 callapplybind 来调用箭头函数,第一个参数是没有意义的,也就是无法改变 this,如果仍需要使用,第一个参数应该传 null。看 mdn 给出的示例。

var globalObject = this;
var foo = (() => this);
console.log(foo() === globalObject); // true

// 接着上面的代码
// 作为对象的一个方法调用
var obj = {foo: foo};
console.log(obj.foo() === globalObject); // true

// 尝试使用call来设定this
console.log(foo.call(obj) === globalObject); // true

// 尝试使用bind来设定this
foo = foo.bind(obj);
console.log(foo() === globalObject); // true

// 创建一个含有bar方法的obj对象,
// bar返回一个函数,
// 这个函数返回this,
// 这个返回的函数是以箭头函数创建的,
// 所以它的this被永久绑定到了它外层函数的this。
// bar的值可以在调用中设置,这反过来又设置了返回函数的值。
var obj = {
 bar: function() {
 var x = (() => this);
 return x;
 }
};

// 作为obj对象的一个方法来调用bar,把它的this绑定到obj。
// 将返回的函数的引用赋值给fn。
var fn = obj.bar();

// 直接调用fn而不设置this,
// 通常(即不使用箭头函数的情况)默认为全局对象
// 若在严格模式则为undefined
console.log(fn() === obj); // true

// 但是注意,如果你只是引用obj的方法,
// 而没有调用它
var fn2 = obj.bar;
// 那么调用箭头函数后,this指向window,因为它从 bar 继承了this。
console.log(fn2()() == window); // true

其他情况

还有一些情况我觉得比较简单,就一笔带过。
1. 当函数被用作事件处理函数时,它的 this 指向触发事件的元素。
2. 当代码被内联 on-event 处理函数调用时,它的this指向监听器所在的 DOM 元素,需要注意的是只有最外层的 this 是这样,如果里面还有嵌套函数,则嵌套函数的 this 在非严格模式下仍然指向全局对象。
3. 构造函数中的 this 请看之前的文章JavaScript中new操作符的解析和实现
4. bindcallapply 都一样,函数的 this 被绑定到第一个参数上。

总结

以上就是我所总结的 JS 中的 this 的一些要点,如果有什么遗漏或者错误的地方,欢迎指正。

到此这篇关于我所理解的JavaScript中的this指向的文章就介绍到这了,更多相关JavaScript this指向内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
驱动事件的addEvent.js代码
Mar 27 Javascript
jQuery autocomplete插件修改
Apr 17 Javascript
模仿jQuery each函数的链式调用
Jul 22 Javascript
XENON基于JSON变种
Jul 27 Javascript
js 控制下拉菜单刷新的方法
Mar 03 Javascript
JQuery触发事件例如click
Sep 11 Javascript
如何使用HTML5地理位置定位功能
Apr 27 Javascript
使用AngularJS制作一个简单的RSS阅读器的教程
Jun 18 Javascript
jquery ajax后台返回list,前台用jquery遍历list的实现
Oct 30 Javascript
Node.js利用debug模块打印出调试日志的方法
Apr 25 Javascript
JS实现数组删除指定元素功能示例
Jun 05 Javascript
vue 通过base64实现图片下载功能
Dec 19 Vue.js
JS运算符优先级与表达式示例详解
Sep 04 #Javascript
vue中的循环对象属性和属性值用法
Sep 04 #Javascript
JavaScript逻辑运算符相关总结
Sep 04 #Javascript
浅析JavaScript预编译和暗示全局变量
Sep 03 #Javascript
详解JavaScript作用域、作用域链和闭包的用法
Sep 03 #Javascript
JS变量提升及函数提升实例解析
Sep 03 #Javascript
Vue自定义组件双向绑定实现原理及方法详解
Sep 03 #Javascript
You might like
php 修改、增加xml结点属性的实现代码
2013/10/22 PHP
php数组保存文本与文本反编成数组实例
2014/11/13 PHP
详解PHP中array_rand函数的使用方法
2016/09/11 PHP
yii2 url重写并隐藏index.php方法
2018/12/10 PHP
javascript的事件描述
2006/09/08 Javascript
jQuery选择头像并实时显示的代码
2010/06/27 Javascript
悄悄用脚本检查你访问过哪些网站的代码
2010/12/04 Javascript
JavaScript 匿名函数(anonymous function)与闭包(closure)
2011/10/04 Javascript
js触发asp.net的Button的Onclick事件应用
2013/02/02 Javascript
谈谈关于JavaScript 中的 MVC 模式
2013/04/11 Javascript
jquery选择器、属性设置用法经验总结
2013/09/08 Javascript
Javascript中浮点数相乘的一个解决方法
2014/06/03 Javascript
简单的jquery左侧导航栏和页面选中效果
2014/08/21 Javascript
JS+CSS实现可拖动的弹出提示框
2015/02/16 Javascript
用户代理字符串userAgent可实现的四个识别
2015/09/20 Javascript
javascript实现在网页中运行本地程序的方法
2016/02/03 Javascript
ES6所改良的javascript“缺陷”问题
2016/08/23 Javascript
PHP实现本地图片上传和验证功能
2017/02/27 Javascript
webpack之devtool详解
2018/02/10 Javascript
JQuery animate动画应用示例
2019/05/14 jQuery
Nodejs使用archiver-zip-encrypted库加密压缩文件时报错(解决方案)
2019/11/18 NodeJs
[04:26]DOTA2上海特锦赛小组赛第二日 TOP10精彩集锦
2016/02/27 DOTA
[14:19]2018年度COSER大赛-完美盛典
2018/12/16 DOTA
Python语言实现机器学习的K-近邻算法
2015/06/11 Python
python 异常处理总结
2016/10/18 Python
用Cython加速Python到“起飞”(推荐)
2019/08/01 Python
CSS3制作漂亮的照片墙的实现代码
2016/06/08 HTML / CSS
法国时尚品牌乐都特瑞士站:La Redoute瑞士
2016/09/05 全球购物
波兰运动鞋网上商店:e-Sporting
2018/02/16 全球购物
WebSphere 应用服务器都支持哪些认证
2013/12/26 面试题
公司面试感谢信
2014/02/01 职场文书
新闻传媒系求职信范文
2014/04/19 职场文书
乡镇群众路线专项整治方案
2014/11/03 职场文书
2014年幼儿园教师工作总结
2014/11/08 职场文书
2014年监理工作总结范文
2014/11/17 职场文书
Java数组与堆栈相关知识总结
2021/06/29 Java/Android