一篇文章带你彻底了解何为this!

前言

在错综复杂的代码结构中,我们总能在很多地方看到this这个关键字,所以总是容易混淆,今天我就用一篇文章带大家来彻底了解其功能和用法。

this

this是我们的函数运行环境 指针,用直白的话来阐述其概念就是:你可以把this想象成一个指针,它总是指向某个对象,其指向的是调用函数的对象,接下来我们来看一个例子就明白了:

js 复制代码
var x = 2;
var obj = {
    x:1,
    foo:function(){
        console.log(this,this.x)
    }
}

obj.foo() 

此时我们的打印结果为{ x: 1, foo: [Function: foo] } 1,也就是this和this.name的打印结果,那就说明此时的this就等于obj ,因为foo()函数是被obj调用的,所以this指向obj。

我们再来看下一个例子:

js 复制代码
<body>
<script>
var x = 2;
var obj = {
    x:1,
    foo:function(){
        console.log(this,this.x)
    }
}
// obj.foo() 
var foo = obj.foo;
foo();
</script>
</body>

此时我们把上面的函数调用注释掉了,将其作为值赋给了foo变量,然后我们再执行foo函数,这时我们打开浏览器查看会发现:

它this指向的是windows,且输出的是全局环境中的x,值为2。这又是为什么呢,因为我们要时刻牢牢记住一个概念函数被谁调用this就指向谁 ,但是在我们这并没有发生任何函数调用,它只是作为一个普通的函数被执行了 ,所以它的this就会指向window。

这时我们还有一个小小的知识点就是,如果在严格模式下执行这个函数,那么它的this就会由window变为undefined ,这是因为this它既是JS中的goodpart它也是badpart,既然你都作为普通函数执行了,你就完全没必要指向任何对象呀。所以我们在<Script>文件第一行加入:

js 复制代码
"use strict"

然后再浏览器打开就会发现:

ok,接下来讲讲另一种this,就是通过new关键字构造函数的方式执行,此时this将会指向被实例化的对象,记住是实例化的对象本身,而不是变量名:

js 复制代码
function Person(name) {
// 调用此函数的name被赋值为传进来的参数name
    this.name = name; 
    console.log(this);
}
const person1 = new Person('Alice');
console.log(person1);

可以看到this打印的值为被实例化的对象本身。

小结

现在我们来小结一下,我们目前为止共讲了3种this,分别是:

  • 作为对象的方法被调用,this指向调用函数的对象
  • 作为普通函数执行,指向window,严格模式下为undefined
  • new构造函数调用执行,this指向实例化的对象

从这三种this我们可以看出,this本身的值并不是固定的,它取决于函数的调用方式 ,所以它的绑定是在函数执行前的一霎那被调用对象和方式所决定,而并不是在函数被定义时。

主动指定this

既然this都是被动被赋值的,那我们能不能主动指定this呢?答案是当然可以的,请看下面的例子:

js 复制代码
<script>
var name = "a"
var a = {
    name:"b",
    func1:function(){
        console.log(this.name)
    },
    func2:function(){
        setTimeout(function(){
            this.func1()
        },1000)
    }
}
a.func2();
</script>

这串代码会输出什么呢?答案是报错了 ,因为我们是执行func1(),此时它的this会指向全局,但是在全局中并没有定义func1()这个函数,此时我们就需要通过手段来指定this的指向。

.call()是函数对象原型上的方法,也就是Function().prototype.call,而Function是所有函数的构造函数,其余所有函数都是它的实例 ,就如同我们可以直接通过一个const function = new Function()和const arr = new Array()创建一个函数实例和数组实例一样。它的作用就是指定一个函数的this指向,这时我们来稍稍修改上面的代码:

js 复制代码
    func2:function(){
        setTimeout(function(){
            this.func1()
        }.call(a),1000)
   }

我们在函数的外部添加.call(a)方法后,它就会将this指向对象a,此时在浏览器中打开:

除了.call()之外,我们还可以将函数修改成箭头函数,因为箭头函数很简单,就是为了方便我们使用的,所以它上面并不会带有this ,那要怎么来确定调用它的函数是谁呢?虽然它本身没有this,但是它会继承其所在词法作用域的this ,也就是func2()的this a,这里的this.func1()也就变成了a.func()1。

js 复制代码
    func2:function(){
        setTimeout(() => {
            this.func1()
        },1000)
    }

此时,浏览器中也正确输出了b。因为它继承的是其所在词法作用域的this,而词法作用域是在编译的时候决定的,也就是说此时的this和之前提到的三种this并不一样,它并不是在函数执行前一刻被绑定的,而是在定义时就已经确定了。

ok接下来我们介绍最后一种方法,就是提前保存this,先看代码:

js 复制代码
<script>
    var name = "a";
    var a = {
        name:"b",
        func1:function(){
            console.log(this.name);
        },
        func2:function(){
        // 提前保存调用func2()的对象的this
            let_this = this;
            console.log("func2",this);
            setTimeout(function() { 
            // this丢失,使用提前保存的this
                _this.func1();
            },1000)
          }
        }
    a.func2();
</script>

我们在这里使用了一个变量来提前保存我们需要调用函数的对象的this ,然后在调用函数时将其作为this值赋给它,使其本身的this丢失,从而执行我们想要指定的对象来调用函数,同样这里输出的也是b。

小结

在这里我们同样介绍了三种方法,这三种主动指定this和一开始我们讲的执行前绑定this的最大区别就是,这三种做法都是提前指定好了this,再调用它,而不是在调用的前一刻才绑定。

结语

熟练掌握this的功能对我们在写复杂的代码时有着很大的帮助,并且在面试时也常会考到,如果文章中有问题欢迎指出在评论区交流,希望能对你有帮助。

相关推荐
郑州光合科技余经理37 分钟前
海外版外卖加盟:总站与分站配送规则怎么分开管
java·开发语言·前端·后端·uni-app·php·ai编程
小呆呆6661 小时前
副业搞起来,小说,漫画,漫剧的成本优化思路
前端·后端·面试
凤城老人1 小时前
从 PyQt6 到 Electron:给 Edge TTS 做一个“多角色配音机“的踩坑手记
javascript·typescript·electron
Dovis(誓平步青云)2 小时前
浇水提醒刚弹出又消失,植物状态别只存一个百分比
开发语言·前端·javascript·pdf·ecmascript·电脑
码艺-Alimjan2 小时前
Vben Admin 新增维吾尔语 Vben-Modal的关键坑之一
前端·javascript·vue.js
可乐鸡翅yeah_3 小时前
hls.js 手动自定义 http 请求 loader,修改请求头实战
开发语言·前端·javascript·网络协议·http·ecmascript·m3u8在线
IT_陈寒3 小时前
Vite静态资源导入这个坑我帮你们踩过了
前端·人工智能·后端
广州华水科技3 小时前
大坝安全监测解决方案:单北斗GNSS形变监测系统应用与维护
前端
yivifu4 小时前
中文古籍电子书注释集成
前端·javascript·python·beautifulsoup·epub
weixin_448290254 小时前
书庐开发实战教学
javascript·css·html