JavaScript — this的用法总结

About "this"

P1:如何使用this?

  • this 实际上是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用。

这话说起来容易,理解起来缺常常出现问题。请看如下几段代码

var a =1;
var obj = {
    a : 0,
    say: function(){ console.log(this.a) }
}
var fun = obj.say;
obj.say(); // 0
fun();     // 1

为什么会出现在这种情况?
明明我的fun和obj.say指向了同一个函数,而且都在window对象中调用,为什么出现了不同的结果?

本质上是这样的:

 obj.say()  等价于 obj.say.call(obj) //你不写.call(obj),浏览器自动帮你写
 fun() 等价于 fun.call(window) 
  1. 把obj当作this传入了say函数中,自然输出的是obj内的a
  2. 在window对象中执行fun(),就相当于把window当作this传入了该函数中,输出的就是window里的a!
  • 更加普遍化的,this其实时时刻刻都伴随着我们的代码

    • 你调用fun(),实际上是执行了fun.call(undefined)
    • 你调用obj.fun(),实际上执行了obj.fun.call(obj)

如果你传的是null 或者 undefined,那么默认将window传入(严格模式下默认是 undefined)

至此我们基本讲述了this的基本用法

P2:我们如何判断this指向?

  • 如果要判断一个运行中函数的 this 绑定,就需要找到这个函数的直接调用位置。找到之后就可以按优先顺序应用下面这四条规则来判断 this 的绑定对象。

    1. 由 new 调用,则绑定到新创建的对象。
    2. 由 call 或者 apply (或者 bind )调用?绑定到指定的对象。
    3. 由上下文对象调用?绑定到那个上下文对象。

      function say(){ console.log(this.name) }
      let person = { name:'Jam', age: 18, say: say}
      person.say() // 'Jam'
    4. 默认:在严格模式下绑定到 undefined ,否则绑定到全局对象。

P3:箭头函数中的this

  • 箭头函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象,箭头函数中没有自己的this的(箭头函数无法使用call,apply),而箭头函数会默认使用父级的this。

我不知道其他人怎样理解,但如此的定义确实让我难以理解。何为定义时所在的对象?如何使用父级的this?接下来我用实例带你理解。

let obj = {
    prin: function(){
        //父级作用域 此处保存着父级的this
        setTimeout(()=>{
            console.log(this.num);
        },1000)
    }
}
let obj1 = { num:1 };
let obj2 = { num:2 };
  1. 首先我们直接执行prin方法试一下

    obj.prin(); // undefined

    分析
    obj.prin()等价于obj.prin.call(obj),
    我们将obj作为this,传到prin函数中此时箭头函数向上查找父级作用域中保存的this,
    顺理成章的找到了obj,于是去obj的环境内部寻找num
    遗憾的是obj内部并没有num属性,故打印undefined

  2. 尝试改变父级中this的指向

    obj.prin.call(obj1); // 1
    obj.prin.call(obj2); // 2

    分析
    父级中的this被赋值为obj1,箭头函数顺着obj1寻找,找到了num属性,打印了它的值
    同理父级中的this被赋值为obj2,箭头函数顺着obj2寻找,找到了num属性,打印了它的值

  3. 那我们不如直接给obj一个num属性试试?

    obj.num = 0;
    obj.prin(); // 0

    分析
    当obj中拥有了属性num,那么箭头函数一找就找到了它!

  4. 我们再来最后一个例子加深理解!当我把prin函数也用箭头函数书写呢?

    var num = 10,
    //“祖级作用域” window
    var obj = {
        num : 5;
        prin: ()=>{
            //父级作用域 此处保存着父级的this
            setTimeout(()=>{
                console.log(this.num);
            },1000)
        }
    }
    obj.prin()

    你猜猜会输出什么呢?
    ·
    ·
    ·
    答案是10!

    分析
    setTimeout中的箭头函数到父级作用域中寻找this,发现父级作用域也是箭头函数,于是再向外查 找,找到了“祖级作用域”window,恰好window有num属性,于是setTimeout中的箭头函数快乐地把 window.num打印了出来!

P4:其他一些总结

  1. 使用let,const声明的变量可能会不符合上述结论。
    原因是:let,const 在全局作用域下声明的变量并不会像 var 一样,被添加为window的属性,所以即使在全局中声明了let number = 1, 箭头函数等等也不会在全局作用域中找到 number!
  2. image.png

本文借鉴了 @方应杭 老师的思想 https://zhuanlan.zhihu.com/p/...

Slow Done, Achieve More.

73 声望
13 粉丝
0 条评论
推荐阅读
[源码向] 以小白视角构建react库
创作本篇博客的初衷是,在浏览社区时发现了[链接]这篇宝藏文章,该博主基于react16之后的fiber架构实现了一套react的简易版本,非常有助于理解react工作原理。但是苦于只有英文版本,且偏向理论。

Oliver阅读 766

从零搭建 Node.js 企业级 Web 服务器(零):静态服务
过去 5 年,我前后在菜鸟网络和蚂蚁金服做开发工作,一方面支撑业务团队开发各类业务系统,另一方面在自己的技术团队做基础技术建设。期间借着 Node.js 的锋芒做了不少 Web 系统,有的至今生气蓬勃、有的早已夭折...

乌柏木149阅读 12.3k评论 10

正则表达式实例
收集在业务中经常使用的正则表达式实例,方便以后进行查找,减少工作量。常用正则表达式实例1. 校验基本日期格式 {代码...} {代码...} 2. 校验密码强度密码的强度必须是包含大小写字母和数字的组合,不能使用特殊...

寒青54阅读 7.8k评论 11

JavaScript有用的代码片段和trick
平时工作过程中可以用到的实用代码集棉。判断对象否为空 {代码...} 浮点数取整 {代码...} 注意:前三种方法只适用于32个位整数,对于负数的处理上和Math.floor是不同的。 {代码...} 生成6位数字验证码 {代码...} ...

jenemy46阅读 6k评论 12

从零搭建 Node.js 企业级 Web 服务器(十五):总结与展望
总结截止到本章 “从零搭建 Node.js 企业级 Web 服务器” 主题共计 16 章内容就更新完毕了,回顾第零章曾写道:搭建一个 Node.js 企业级 Web 服务器并非难事,只是必须做好几个关键事项这几件必须做好的关键事项就...

乌柏木66阅读 6.2k评论 16

再也不学AJAX了!(二)使用AJAX ① XMLHttpRequest
「再也不学 AJAX 了」是一个以 AJAX 为主题的系列文章,希望读者通过阅读本系列文章,能够对 AJAX 技术有更加深入的认识和理解,从此能够再也不用专门学习 AJAX。本篇文章为该系列的第二篇,最近更新于 2023 年 1...

libinfs39阅读 6.3k评论 12

封面图
从零搭建 Node.js 企业级 Web 服务器(一):接口与分层
分层规范从本章起,正式进入企业级 Web 服务器核心内容。通常,一块完整的业务逻辑是由视图层、控制层、服务层、模型层共同定义与实现的,如下图:从上至下,抽象层次逐渐加深。从下至上,业务细节逐渐清晰。视图...

乌柏木43阅读 7.4k评论 6

Slow Done, Achieve More.

73 声望
13 粉丝
宣传栏