JavaScript中this的指向问题

JavaScript中this的指向问题

参考资料:

JavaScript 的 this 原理 - 阮一峰的网络日志 (ruanyifeng.com)

Javascript 的 this 用法 - 阮一峰的网络日志 (ruanyifeng.com)

JS中的this指向问题(详细版)_LoveyL0201的博客-CSDN博客

《你不知道的JavaScript》-上,推荐读这本书

本文记录了JavaScript中this指向问题的学习心得,为以后的复习做准备。

讨论this的时候要注意是否在严格模式下。下面代码大部分情况下都不在严格模式下。

毫无疑问,在缺乏清晰认识的情况下,this 对你来说完全就是一种魔法–《你不知道的JavaScript》

this的原理

this指的是函数运行时所在的环境。看下述代码。

let obj = {
    bar: 1,
    f: function(){
        console.log(this.bar);
    }
}
obj.f();
var bar = 2;// 这里不能使用let,let声明的变量是不会称为window的对象的,当然也不能使用nodejs执行,nodejs是没有window的。
let f = obj.f;
f();

结果为:

1
2

通过这个例子我们就可以看出,对于obj.f()来说它的运行环境就是在obj这个对象里面,里面的this.bar = 1,对于f()来说,它的运行环境就是在全局作用域,那么它的this.bar = window.bar = 2

但是为什么是这样的呢?在那个环境执行到底是由什么决定的呢?阮一峰大佬为我们做了详细的解释。其实就是查看调用栈的位置。可以把调用栈想象成一个函数调用链。

内存的数据结构

JavaScript之所以有this,和内存脱不了关系。

let obj = {foo:5};

对于这段代码来说,JavaScript到底到底做了什么操作呢?

JavaScript引擎会现在堆里面生成一个对象{foo:5},然后在将它内存的地址赋值给栈里面的obj变量。

也就是说对于obj来说,它只保存了对象的地址,当访问对象属性或者方法时,它就是用这个地址找到堆中的对象,然后访问其属性或者方法。

原始的对象以字典结构保存,每一个属性名都对应一个属性描述对象。

{
  foo: {
    [[value]]: 5
    [[writable]]: true
    [[enumerable]]: true
    [[configurable]]: true
  }
}

对于对象的方法来说:

方法是一个函数,而函数在JavaScript是一个对象,那么它保存方式也不难想到,如果这个属性名对应的是一个函数,那么这个属性保存值就是这个函数在内存中的地址。

{
  foo: {
    [[value]]: 函数的地址
    ...
  }
}

环境变量

JavaScript 允许在函数体内部,引用当前执行环境的其他变量。

function f(){
     console.log(x);
}
f();

我们看看上述代码,在f()中没有x变量,那么它只有去的它所以运行的环境中找x变量。由于函数可以在不同的运行环境执行,所以需要有一种机制,能够在函数体内部获得当前的运行环境(context)。所以,this就出现了,它的设计目的就是在函数体内部,指代函数当前的运行环境。也就是你到底想要哪里得x变量,是之前函数环境中的x,还是当前环境中的x。如果是之前环境中的x,那么就是用console.log(x),如果是当前内环境中的,就是用console.log(this.x)

回到开头的代码,obj.foo()是通过obj找到foo,所以就是在obj环境执行。一旦var foo = obj.foo,变量foo就直接指向函数本身,所以foo()就变成在全局环境执行。

其实总的来说,谁调用就指向谁。

this指向情况

阮一峰大佬帮我们总结一些情况,我又在网络上收集一些情况。

this是 JavaScript 语言的一个关键字。它是函数运行时,在函数体内部自动生成的一个对象,只能在函数体内部使用。

总的来说,this就是函数运行时所在的环境对象

情况一:纯粹的函数调用(默认绑定)

这是函数的最通常用法,属于全局性调用,因此this就代表全局对象。

 var x = 1;
        function f(){
            console.log(this.x);
            console.log(this);
        }
        f();// 1 window对象

但是如果使用严格模式

'use strict'
        var x = 1;
        function f(){
            console.log(this.x);
            console.log(this);
        }
    f(); // 报错

这样就会报错,因为严格模式下,全局环境下的函数不再指向window,它的值是undefined

情况二:作为对象方法的调用(隐式绑定)

函数还可以作为某个对象的方法调用,这时this就指这个上级对象。

function f(){
    console.log(this.x);
}
var obj = {
    x : 1,
    f:f
}
var x = 2;
obj.f();//1

情况三:作为构造函数调用(new,优先级最高)

所谓构造函数,就是通过这个函数,可以生成一个新对象。这时,this就指这个新对象。

 	var x = 1;
	function F(){
    	this.x = 2;
	}
    let f = new F();
    console.log(f.x);
	x;//1,可以看到与全局环境下的x是没有关系的哈

new()的过程大概可以表示为:调用构造函数,在堆内为这个创建一个空对象,并使用this指向这个空对象,然后执行构造函数语句,最后返回this。(返回this是自动的,不需要在构造函数中写明)。

情况四:apply 、bind,call调用(显示绑定)

这几个函数可以改变函数的指向,如果不填指向谁,那么就指向window(也就是全局对象)。

可以使用apply,和call实现bind(),也就是返回一函数。

bind()还可以用来实现柯里化函数(先传如部分参数)。

总结一下上面四种情况:

谁调用我,我就指向谁。我在那个环境运行,我们指向谁。

情况五:箭头函数

箭头函数它没有自己的this,它this就是上下文中定义的this,也就是说头函数中所使用的this来自于函数作用域链。网上有些人说箭头函数的this是外层第一个普通函数的this。也就是说它会继承外层函数调用的this绑定。这个其实和ES6之前的let that = this是相同的。由于这种特性,箭头函数常用语回调函数中。

需要注意的是箭头函数的绑定无法再次修改。

[代码来自]((2条消息) JS中的this指向问题(详细版)_LoveyL0201的博客-CSDN博客)

 var div = document.querySelector('div'); 
    var o={
        a:function(){
            var arr=[1];
          //就是定义所在对象中的this
          //这里的this—>o
            arr.forEach(item=>{
              //所以this -> o
                console.log(this);
            })
        },
      //这里的this指向window o是定义在window中的对象
        b:()=>{
            console.log(this);
        },
        c:function() {
            console.log(this);
        }
    }
    div.addEventListener('click',item=>{
        console.log(this);//this->window 这里的this就是定义上文window环境中的this
    });
    o.a(); //this->o
    o.b();//this->window
    o.c();//this->o 普通函数谁调用就指向谁

情况六:事件绑定中的this

this指向事件源,也就是谁绑定了这个事件。应该就是隐式绑定。

情况七:定时器中的this

定时器中的this->window,因为定时器中采用回调函数作为处理函数,而回调函数的this->window。

绑定优先级

new > 显式绑定 > 隐士绑定 > 默认绑定 。

总结(来自《你不知道的JavaScript》)

  1. 由 new 调用?绑定到新创建的对象。
  2. 由 call 或者 apply(或者 bind)调用?绑定到指定的对象。
  3. 由上下文对象调用?绑定到那个上下文对象。
  4. 默认:在严格模式下绑定到 undefined,否则绑定到全局对象。

一定要注意,有些调用可能在无意中使用默认绑定规则(比如说赋值的时候)

如果想“更安全”地忽略 this 绑 定,你可以使用一个 DMZ 对象,比如 ø = Object.create(null)《这样创建不会有原型》,以保护全局对象。

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值