js中this的指向(彻底理解js中this的指向,不必硬背)

首先必须要说的是,this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁实际上this的最终指向的是那个调用它的对象(这句话有些问题,后面会解释为什么会有问题,虽然网上大部分的文章都是这样说的,虽然在很多情况下那样去理解不会出什么问题,但是实际上那样理解是不准确的,所以在你理解this的时候会有种琢磨不透的感觉),那么接下来我会深入的探讨这个问题

例子1:

function a(){var user = "追梦子";console.log(this.user); //undefinedconsole.log(this); //Window
}
a();

按照我们上面说的this最终指向的是调用它的对象,这里的函数a实际是被Window对象所点出来的,下面的代码就可以证明。

function a(){var user = "追梦子";console.log(this.user); //undefinedconsole.log(this);  //Window
}
window.a();

和上面代码一样吧,其实alert也是window的一个属性,也是window点出来的。

例子2:

var o = {user:"追梦子",fn:function(){console.log(this.user);  //追梦子}
}
o.fn();

  这里的this指向的是对象o,因为你调用这个fn是通过o.fn()执行的,那自然指向就是对象o,这里再次强调一点,this的指向在函数创建的时候是决定不了的,在调用的时候才能决定,谁调用的就指向谁,一定要搞清楚这个。

其实例子1和例子2说的并不够准确,下面这个例子就可以推翻上面的理论。

如果要彻底的搞懂this必须看接下来的几个例子

例子3:

var o = {user:"追梦子",fn:function(){console.log(this.user); //追梦子}
}
window.o.fn();

  这段代码和上面的那段代码几乎是一样的,但是这里的this为什么不是指向window,如果按照上面的理论,最终this指向的是调用它的对象,这里先说个而外话,window是js中的全局对象,我们创建的变量实际上是给window添加属性,所以这里可以用window点o对象。

  这里先不解释为什么上面的那段代码this为什么没有指向window,我们再来看一段代码。

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

  这里同样也是对象o点出来的,但是同样this并没有执行它,那你肯定会说我一开始说的那些不就都是错误的吗?其实也不是,只是一开始说的不准确,接下来我将补充一句话,我相信你就可以彻底的理解this的指向的问题。

  情况1:如果一个函数中有this,但是它没有被上一级的对象所调用,那么this指向的就是window,这里需要说明的是在js的严格版中this指向的不是window,但是我们这里不探讨严格版的问题,你想了解可以自行上网查找。

  情况2:如果一个函数中有this,这个函数有被上一级的对象所调用,那么this指向的就是上一级的对象。

  情况3:如果一个函数中有this,**这个函数中包含多个对象,尽管这个函数是被最外层的对象所调用,this指向的也只是它上一级的对象,**例子3可以证明,如果不相信,那么接下来我们继续看几个例子。

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

尽管对象b中没有属性a,这个this指向的也是对象b,因为this只会指向它的上一级对象,不管这个对象中有没有this要的东西。

还有一种比较特殊的情况,例子4:

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

这里this指向的是window,是不是有些蒙了?其实是因为你没有理解一句话,这句话同样至关重要。

  this永远指向的是最后调用它的对象,也就是看它执行的时候是谁调用的,例子4中虽然函数fn是被对象b所引用,但是在将fn赋值给变量j的时候并没有执行所以最终指向的是window,这和例子3是不一样的,例子3是直接执行了fn。

  this讲来讲去其实就是那么一回事,只不过在不同的情况下指向的会有些不同,上面的总结每个地方都有些小错误,也不能说是错误,而是在不同环境下情况就会有不同,所以我也没有办法一次解释清楚,只能你慢慢地的去体会。

构造函数版this:

function Fn(){this.user = "追梦子";
}
var a = new Fn();
console.log(a.user); //追梦子

  这里之所以对象a可以点出函数Fn里面的user是因为new关键字可以改变this的指向,将这个this指向对象a,为什么我说a是对象,因为用了new关键字就是创建一个对象实例,理解这句话可以想想我们的例子3,我们这里用变量a创建了一个Fn的实例(相当于复制了一份Fn到对象a里面),此时仅仅只是创建,并没有执行,而调用这个函数Fn的是对象a,那么this指向的自然是对象a,那么为什么对象a中会有user,因为你已经复制了一份Fn函数到对象a中,用了new关键字就等同于复制了一份。

  除了上面的这些以外,我们还可以自行改变this的指向,关于自行改变this的指向请看JavaScript中call,apply,bind方法的总结这篇文章,详细的说明了我们如何手动更改this的指向。

更新一个小问题当this碰到return时

function fn()  
{  this.user = '追梦子';  return {};  
}
var a = new fn;  
console.log(a.user); //undefined

再看一个

function fn()  
{  this.user = '追梦子';  return function(){};
}
var a = new fn;  
console.log(a.user); //undefined

再来

function fn()  
{  this.user = '追梦子';  return 1;
}
var a = new fn;  
console.log(a.user); //追梦子
function fn()  
{  this.user = '追梦子';  return undefined;
}
var a = new fn;  
console.log(a.user); //追梦子

什么意思呢?

  **如果返回值是一个对象,那么this指向的就是那个返回的对象,如果返回值不是一个对象那么this还是指向函数的实例。 **

function fn()  
{  this.user = '追梦子';  return undefined;
}
var a = new fn;  
console.log(a); //fn {user: "追梦子"}

  还有一点就是虽然null也是对象,但是在这里this还是指向那个函数的实例,因为null比较特殊。

function fn()  
{  this.user = '追梦子';  return null;
}
var a = new fn;  
console.log(a.user); //追梦子

知识点补充:

  1.在严格版中的默认的this不再是window,而是undefined。

  2.new操作符会改变函数this的指向问题,虽然我们上面讲解过了,但是并没有深入的讨论这个问题,网上也很少说,所以在这里有必要说一下。** **

function fn(){this.num = 1;
}
var a = new fn();
console.log(a.num); //1

  为什么this会指向a?首先new关键字会创建一个空的对象,然后会自动调用一个函数apply方法,将this指向这个空对象,这样的话函数内部的this就会被这个空的对象替代。

  注意: 当你new一个空对象的时候,js内部的实现并不一定是用的apply方法来改变this指向的,这里我只是打个比方而已.

  if (this === 动态的\可改变的) return true;

箭头函数中的this

箭头函数没有自己的this, 它的this是继承而来; 默认指向在定义它时所处的对象(宿主对象),而不是执行时的对象, 定义它的时候,可能环境是window; 箭头函数可以方便地让我们在 setTimeout ,setInterval中方便的使用this

要整明白这些, 我们需要首先了解一下作用域链:

当在函数中使用一个变量的时候,首先在本函数内部查找该变量,如果找不到则找其父级函数,

最后直到window,全局变量默认挂载在window对象下

来看看this的一个使用案例:

//定义一个对象var obj = {x:100, //属性xshow(){//延迟500毫秒,输出x的值setTimeout(//匿名函数function(){console.log(this.x);},500);}};obj.show();//打印结果:undefined

案例中,我们的obj对象中有一个属性x和一个属性show( )方法(用的是对象的简洁表示法,第九节已讲解,点击查看),show( )通过this打印出x的值。可最后一句代码执行后,我们打印出的结果是undefined。这是为什么呢?难道x的值不是100吗?

没错,x的值确实是100,但问题出在了this上,当代码执行到了setTimeout( )的时候,此时的this已经变成了window对象(setTimeout( )是window对象的方法),已经不再是obj对象了,所以我们用this.x获取的时候,获取的不是obj.x的值,而是window.x的值,再加上window上没有定义属性x,所以得到的结果就是:undefined。

我们可以用一些ES5中的知识来巧妙地避过这个坑,但是,我们今天主要介绍的是ES6的箭头函数。

箭头函数闪亮登场

如果使用箭头函数来编写同样的一段代码,得到的this.x又是另一番景象。我们来试试看:

//定义一个对象var obj = {x:100,//属性xshow(){//延迟500毫秒,输出x的值setTimeout(//不同处:箭头函数() => { console.log(this.x)},500);}};obj.show();//打印结果:100

同样的一段代码,唯一的不用就是setTimeout中,原本的匿名函数用箭头函数代替了,你可以往上翻,对比着看两段代码的不同之处。此外,最大的不同的是打印结果,用箭头函数编写的这段代码,成功地打印出了我们想要的结果:100。

为什么是这样?正如文章第三段所概括:

箭头函数中的this指向的是定义时的this,而不是执行时的this

当定义obj的show( )方法的时候,我们在箭头函数编写this.x,此时的this是指的obj,所以this.x指的是obj.x。而在show()被调用的时候,this依然指向的是被定义时候所指向的对象,也就是obj对象,故打印出:100。

重点: call() / apply() / bind() 方法对于箭头函数来说只是传入参数,对它的 this 毫无影响。

var adder = {base : 1,add : function(a) {var f = v => v + this.base;return f(a);},addThruCall: function inFun(a) {var f = v => v + this.base;var b = {base : 2};return f.call(b, a);}
};console.log(adder.add(1));         // 输出 2
console.log(adder.addThruCall(1)); // 仍然输出 2(而不是3,其内部的this并没有因为call() 而改变,其this值仍然为函数inFun的this值,指向对象adder

什么时候使用箭头函数

setTimeout(function () {console.log(this)
} , 1000);
setTimeout(() => {console.log(this)//这里this找的是window的this
}, 1000);

结论:箭头函数没有this,这里this引用的是最近作用域(aaa函数里的this)的this。

    const obj = {aaa(){setTimeout(function () {console.log(this)//window});setTimeout(() => {console.log(this)//obj});}}obj.aaa()

上述中第一个是window对象的this,第二个箭头函数的this是obj的。

    const obj = {aaa() {setTimeout(function () {setTimeout(function () {console.log(this) //window})setTimeout(() => {console.log(this) //window})})setTimeout(() => {setTimeout(function () {console.log(this) //window})setTimeout(() => {console.log(this) //obj})})}}obj.aaa()1.箭头函数没有自己的this, 它的this是继承而来; 默认指向在定义它时所处的对象(宿主对象),而不是执行时的对象2.箭头函数都是在方法内部以非方法的方式使用,指向定义它的那个对象3.作为方法的箭头函数this指向全局window对象4.当然我们也可以把箭头函数中的this直接拿到上下文环境中进行判断5. call() / apply() / bind() 方法对于箭头函数来说只是传入参数,对它的 this 毫无影响。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/27440.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

制服小程序的“滑手”:禁用页面左右滑动全攻略

哈哈,看来你已经很聪明地发现了小程序中左右滑动的“顽皮”行为!😄 没错,我们可以通过设置 disableScroll 属性来“管教”它,同时结合 CSS 样式让页面既禁得住横向“乱跑”,又能顺畅地上下滚动。你的方案已…

蓝桥备赛(六)- C/C++输入输出

一、OJ题目输入情况汇总 OJ(online judge) 接下来会有例题 , 根据一下题目 , 对这些情况进行分析 1.1 单组测试用例 单在 --> 程序运行一次 , 就处理一组 练习一:计算 (ab)/c 的值 B2009 计算 (ab)/c …

Golang的性能分析指标解读

Golang的性能分析指标解读 一、概述 语言)是一种由Google开发的开源编程语言,以其并发性能和高效的编译速度而闻名。对于程序员来说,了解如何对Golang应用程序进行性能分析是非常重要的,因为这能帮助他们发现潜在的性能瓶颈并对其…

【前端基础】Day 8 H5C3提高

目录 1. HTML5新特性 1.1 新增语义化标签 1.2 新增多媒体标签 1.3 新增input类型 1.4 新增表单属性 2. CSS3的新特性 2.1 新增选择器 2.1.1 属性选择器 2.1.2 结构伪类选择器 2.1.3 伪元素选择器(重点) 2.2 CSS3盒子模型 2.3 CSS3其他特性&a…

Android系统_Surface绘制流程

目录 一,框架图 二,核心代码 一,框架图 对上面这张图的总结 二,核心代码 /Volumes/aosp/android-8.1.0_r52/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp void SurfaceFlinger::onMessageReceived(int32_t what) {ATRACE_CALL();switch (what) {case Mes…

CDefView::_OnFSNotify函数分析

进入CDefView::_OnFSNotify函数时状态栏 _UpdateStatusBar函数之后增加一个对象,变成7个对象。 LRESULT CDefView::_OnFSNotify(LONG lNotification, LPCITEMIDLIST* ppidl) { LPITEMIDLIST pidl; LPCITEMIDLIST pidlItem; // we may be registered for no…

KVMOVS组网配置案例-4

0.配置要求 1.使用ovs创建一个网桥,网桥上添加宿主机eth0接口,接口为trunk,可转发所有vlan的数据2.使用KVM创建并图形安装两个linux虚拟机vm1,vm2。3.vm1关联ovs的vlan10接口,vm2关联ovs的vlan20接口。4.vm1获取vlan10的IP地址,v…

C#—Settings配置详解

C#—Settings配置详解 在C#项目中,全局配置通常指的是应用程序的设置(settings),这些设置可以跨多个类或组件使用,并且通常用于存储应用程序的配置信息,如数据库连接字符串、用户偏好设置等。 Settings配置…

Redis 哨兵模式

哨兵模式(Sentinel) 是 Redis 提供的一种高可用性(High Availability, HA)解决方案,用于监控和管理 Redis 主从架构中的主节点(Master)和从节点(Slave)。当主节点出现故障…

【大厂AI实践】美团:美团智能客服核心技术与实践

【大厂AI实践】美团:美团智能客服核心技术与实践 🌟 嗨,你好,我是 青松 ! 🌈 自小刺头深草里,而今渐觉出蓬蒿。 NLP Github 项目推荐: 【AI 藏经阁】:https://gitee.com…

linux离线安装miniconda环境

1 下载安装包 可以在官网下载最新版 https://www.anaconda.com/download/success#miniconda 或者在软件目录选择合适的版本 https://repo.anaconda.com/miniconda/ 安装包传入离线服务器 ./Miniconda3-py311_24.9.2-0-Linux-x86_64.sh2 运行安装包 ./Miniconda3-py311_24…

线程相关八股

1. 线程和进程的区别? 进程:进程可以简单理解为进行一个程序,比如说我们打开一个浏览器,打开一个文本,这就是开启了一个进程,一个进程想要在计算机中运行,需要将程序交给CPU,将数据…

水仙花数(华为OD)

题目描述 所谓水仙花数,是指一个n位的正整数,其各位数字的n次方和等于该数本身。 例如153是水仙花数,153是一个3位数,并且153 13 53 33。 输入描述 第一行输入一个整数n,表示一个n位的正整数。n在3到7之间&#x…

写一写idea中使用tomcat启动activiti过程

一 环境 tomcat 9.0.62 activiti的war包版本 7.1.0.M6 二 操作 官网下载:https://www.activiti.org/get-started 2.1 先在idea中编辑配置 2.2 点击加号然后选择tomcat本地进行确认 2.3 点击部署之后下边小加号 选择第二个之后就是选择自己想要使用tomcat启动的…

【每日一题 | 2025】2.24 ~ 3.2

个人主页:Guiat 归属专栏:每日一题 文章目录 1. 【2.24】P10424 [蓝桥杯 2024 省 B] 好数2. 【2.25】P8665 [蓝桥杯 2018 省 A] 航班时间3. 【2.26】P10905 [蓝桥杯 2024 省 C] 回文字符串4. 【2.27】P10425 [蓝桥杯 2024 省 B] R 格式5. 【2.28】P10426…

11.【线性代数】——矩阵空间,秩1矩阵,小世界图

十一 矩阵空间,秩1矩阵,小世界图 1. 矩阵空间交集 和 和集 2. 所有解空间3. r 1 r1 r1的矩阵4. 题目5. 小世界图 空间:组成空间的元素的线性组合都在这个空间中。 1. 矩阵空间 举例:矩阵空间( M M M 所有3x3的矩阵&…

我代表中国受邀在亚马逊云科技全球云计算大会re:Invent中技术演讲

大家好我是小李哥,本名叫李少奕,目前在一家金融行业公司担任首席云计算工程师。去年5月很荣幸在全球千万名开发者中被选为了全球亚马逊云科技认证技术专家(AWS Hero),是近10年来大陆地区仅有的第9名大陆专家。同时作为…

【Linux学习笔记】Linux基本指令及其发展史分析

【Linux学习笔记】Linux基本指令及其发展史分析 🔥个人主页:大白的编程日记 🔥专栏:Linux学习笔记 文章目录 【Linux学习笔记】Linux基本指令及其发展史分析前言一. Linux发展史1.1 UNIX发展的历史1.2 Linux发展历史1.3 开源1.4…

gradle libs.versions.toml文件

1.libs.versions.toml介绍2.创建libs.versions.toml文件3.libraries5.versions6.plugins7.bundles 1.libs.versions.toml介绍 下图是官网介绍 意思就是说项目所有插件和库的依赖版本都统一在这个文件配置。 文件中有以下四个部分 versions, 申明要使用的插件和库的版本号的…

【Git】Ubuntu 安装 Git Large File Storage(LFS)以及使用 Git LFS 下载

【Git】Ubuntu 安装 Git Large File Storage(LFS)以及使用 Git LFS 下载 1 安装1.1 使用脚本安装1.2 使用 packagecloud 安装 2 使用2.1 下载 1 安装 1.1 使用脚本安装 参考文档: Link 下载安装包: Link 解压安装包 tar -xzvf git-lfs-linux-amd64-v3.…