全面解析this指向问题!

全面解析this指向问题!this 是一种特别复杂得机制 是一个很特别得关键字 被自动定义在所有函数得作用域中 所以我们有必要去了解 并且搞懂它 文章目录调用位置绑定规则一 默认绑定严格模式二 隐式绑定隐式丢失三 显示绑定硬绑定 API 调用的 上下文 四 new 绑定箭头函数中的 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-->  //当前调用位置在bar中 console.log("foo"); } baz(); //<-- baz的调用位置 

可以把调用栈想象成一个函数调用链,就像我们在前面代码段的注释中所写的一样。还可以使用浏览器的调试工具进行查看。因此,想要分析this的绑定,使用开发者工具得到调用栈,然后找到栈中的第二个元素,这就是真正的调用位置!

绑定规则

一、 默认绑定

最常用的函数调用类型:独立函数调用,也可以看作无法应用其他规则时的默认规则

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

当我们调用foo()时,this.a被解析为全局变量a,因为函数调用时应用了默认绑定,因此this指向全局变量。在代码中,foo()是直接使用不带任何修饰的函数进行调用的,因此只能使用默认绑定!

严格模式

如果使用严格模式,则不能将全局对象用于默认绑定,因此this会被绑定到undefined:

 function foo(){ 
    "use strict" console.log(this.a); } var a=2; foo(); //TypeError:this is undefined; 

注意一个细节:虽然this的绑定规则完全取决于调用位置,但是只有foo()运行在非严格模式下时,默认绑定才能绑定到全局对象;但是在严格模式下调用foo()则不影响默认绑定

 function foo(){ 
    //处于非严格模式 console.log(this.a); } var a=2; (function(){ 
    'use strict' foo(); })(); //2 不影响默认绑定 

二、隐式绑定

第二种需要考虑的规则是调用位置是否有上下文对象,或者说是否被某个对象拥有或包含

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

调用位置使用obj上下文来引用函数,可以说函数被调用时obj对象拥有或者包含函数引用!当foo()被调用时,它的前面确实加上了对obj的引用。当函数引用有上下文对象时,隐式绑定规则会把函数调用中的this绑定到这个上下文对象,因为调用foo()时this被绑定到obj,因此this.a和obj.a时一样的!

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

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

隐式丢失

一个最常见的this绑定问题就是被隐式绑定的函数会丢失绑定对象,也就是说它会应用默认绑定,从而把this绑定到全局对象或者undefined上。

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

虽然bar是obj.foo的一个引用,但是实际上,它引用的是foo函数本身,因此此时的bar()其实是一个不带任何修饰的函数调用,应用了默认绑定

还有一种就是传入回调函数时:

 function foo(){ 
    console.log(this.a); } function foo1(fn){ 
    fn(); } var obj={ 
    a:2, foo:foo } var a="global"; foo1(obj.foo); //golbal 

参数传递其实就是一种隐式赋值,因此我们传入函数时也会被隐式赋值

三、显示绑定

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

硬绑定

 function foo(){ 
    console.log(this.a); } var obj = { 
    a:2 } var bar = function(){ 
    foo.call(obj); } bar(); //2 setTimeout(bar,1000); //2 //硬绑定的bar不可能再修改它的this bar.call(window); //2 

工作过程:我们创建了函数bar(),并在它的内部手动调用了foo.call(obj),因此强制把foo的this绑定到了obj。无论之后如何调用函数bar,它总会手动在obj上调用foo。这种绑定是一种显式的强制绑定,称为硬绑定!

硬绑定的典型应用场景就是创建一个包裹函数,负责接收参数返回值

 function foo(something){ 
    console.log(this.a,something); return this.a + something; } var obj = { 
    a:2 } var bar = function(){ 
    return foo.apply(obj,arguments); } var b = bar(3); //2 3 console.log(b); // 5 

另一种使用方法就是创建一个可以重复使用的辅助函数

 function foo(something){ 
    console.log(this.a,something); return this.a + something; } //简单的辅助绑定函数 function bind(fn,obj){ 
    return function(){ 
    return fn.apply(obj,arguments) } } var obj = { 
    a:2 } var bar = bind(foo.obj); var b = bar(3); //2 3 console.log(b); // 5 

API调用的“上下文”

第三方库的许多函数,以及js语言和宿主环境中许多新的内置函数,都提供了一个可选的参数,通常被称为”上下文“,其作用和bind(…)一样,确保你的回调函数指的this

function(){ 
    console.log(el,this.id); } var obj = { 
    id:"aada" } //调用foo时把this绑定到obj [1,2,3].forEach(foo,obj); //1 aada 2 aada 3 aada 

这些函数实际上就是通过call或者apply实现了显示绑定。

四、new绑定

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

  1. 创建(构造)一个全新的对象
  2. 新对象会被执行[[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上,new是最后一种可以影响函数调用时this绑定行为的方法,称之为new绑定

箭头函数中的this指向

箭头函数不适用this的四种标准规则,而是根据外层(函数或全局)作用域来决定this的

 function foo(){ 
    //返回一个箭头函数 return (a)=>{ 
    //this继承自foo() console.log(this.a); }; } var obj1 = { 
    a:2 }; var obj2 = { 
    a:3 }; var bar = foo.call(obj1); bar.call(obj2); //2,而不是3! 

foo内部创建的箭头函数会捕获调用时foo()的this,由于foo()的this绑定到obj1,bar(引用箭头函数)的this也会绑定到obj1,箭头函数的绑定无法被修改。(new 也不可以)

箭头函数最常用于回调函数中,例如事件处理器或者定时器

 function foo(){ 
    setTimeout(()=>{ 
    //这里的this在词法上继承自foo(); console.log(this.a); },1000); } var obj = { 
    a:2 }; foo.call(obj); //2 

ES6中的箭头函数并不会使用四条标准绑定规则,而是根据当前的词法作用域来决定this,具体来说,箭头函数会继承外层函数调用的this绑定(无论绑定到什么)。

判断this的优先级

这里就不再进行演示优先级的判断过程了,有兴趣的可以自己去测试一下,直接说一下this的四条绑定规则的优先顺序

  1. 函数是否再new中调用,如果是的话this绑定的是新创建的对象 ——new绑定 var bar = new foo()
  2. 函数通过call,apply或者硬绑定调用,this绑定的是指定的对象 ——显示绑定 var bar = foo.call(obj);
  3. 函数在上下文对象中调用,绑定的是上下文对象 —— 隐式绑定 var bar = obj.foo()
  4. 最后就是默认绑定了,如果是在严格模式下,绑定到undefined,否则绑定到全局对象 ——var bar = foo()

以上就是this四大绑定规则的优先级先后顺序啦!

如果本文对你有所帮助的话,记得收藏点赞哟!!!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/205019.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月19日 下午6:59
下一篇 2026年3月19日 下午7:00


相关推荐

  • Google Buzz 里 90% 的内容都是 “非人类”「建议收藏」

    Google Buzz 里 90% 的内容都是 “非人类”「建议收藏」距GoogleBuzz发布还不到10周,尽管Google一直在改进它,但似乎人们还是没有陷入Google的社交圈子。来自PostRank的统计,有90%的GoogleBuzz都是机器人发出来的,其中最多的是从twitter同步过来的内容,占到62%还多,而来自feeds的也有26%多,尽有不到11%的内容是真实的人肉发出来的。也就是说,GoogleBuzz的用户们还是习…

    2022年10月15日
    4
  • 多线程案例:生产者和消费者

    多线程案例:生产者和消费者

    2021年5月6日
    126
  • 最近邻插值、双线性插值、双三次插值

    最近邻插值、双线性插值、双三次插值 1.最近邻插值越是简单的模型越适合用来举例子,我们就举个简单的图像:3X3的256级灰度图,也就是高为3个象素,宽也是3个象素的图像,每个象素的取值可以是0-255,代表该像素的亮度,255代表最亮,也就是白色,0代表最暗,即黑色。假如图像的象素矩阵如下图所示(这个原始图把它叫做源图,Source):234  38   2267    44   1289    65 …

    2022年6月12日
    43
  • 华为服务器安装nas系统,云服务器搭建nas

    华为服务器安装nas系统,云服务器搭建nas云服务器搭建nas内容精选换一换在云服务器上搭建网站后,部分客户通过本地网络访问网站时出现偶发性无法访问的情况。确认客户使用的本地网络。若客户的本地网络是NAT网络(本地主机通过NAT功能使用公网IP地址访问弹性云服务器),可能会导致该问题。若客户的本地网络是NAT网络(本地主机通过NAT功能使用公网IP地址访问弹性云服务器),可能会导致该问题。执行以下命令,查看搭建网在云服务器上搭建网站后,部…

    2022年6月15日
    60
  • nginx做正向代理_正向代理和反向代理图

    nginx做正向代理_正向代理和反向代理图环境在一个网络环境中,只有一台服务器可以使用互联网,而其他内网服务器都可以访问到这台互联网服务器,于是,我们可以通过nginx的正向代理访问互联网地址此处举例:互联网服务器IP:192.168.1.100内网服务器IP:192.168.1.101部署安装包:nginx-1.20.2.tar.gzproxy_connect模块:gitclonehttps://github.com/chobits/ngx_http_proxy_connect_module.git操…

    2022年10月21日
    9
  • 解决tomcat输入localhost:8080报404错误

    解决tomcat输入localhost:8080报404错误一阵子没有玩 tomcat 今天想运行一下我之前的项目 首先在 ApacheTomcat bin 下找到 tomcat9w exe 点击 start 之后发现进度条走到一半就停了 也不报错 使用 start bat 倒是启动很快 无报错信息 怎么回事呢 于是我去控制台 cmd 打开服务 services msc 找到 ApacheTomcat 服务点击启动 这个时候报错了 wind

    2026年3月16日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号