深入辨析JavaScript的this指向

一、为什么js需要this

在js中,this 是个特殊的关键字,通常用于指向当前执行上下文中的对象。

也就是说,this 的结果只有可能是某个object对象或者undefined,不可能是function

this能够提供对当前对象的引用,并且支持动态上下文,是面向对象思想的体现之一。

二、普通函数的this的指向和赋值时机

1、全局上下文

在全局作用域中(不在任何函数或对象中),this 指向全局对象。在浏览器中,this 指向window;在Node.js中,this 指向global。

在严格模式中,this 在全局上下文中指向 undefined。

赋值时机:代码在全局作用域中执行时,this 被赋值为全局对象。

2、对象的方法

当函数作为对象的方法调用时,this 指向调用该方法的对象

其中,全局作用域下的函数也符合这种情况,其内的 this 指向window或global。

赋值时机:函数被调用时。

function func1() {
  console.log(this);
}

func1(); // 在浏览器中输出: Window {...}(非严格模式)

const obj = {
  name: 'Alice',
  func2: function() {
    console.log(this.name);
  }
};

obj.func2(); // 输出: Alice

3、构造函数调用

当函数作为构造函数(使用 new 关键字)调用时,this 指向新创建的对象。

赋值时机:方法被调用时,this 被赋值为调用该方法的对象。

function Person(name) {
  this.name = name; // this 指向新创建的对象
}

const person = new Person('Alice');
console.log(person.name); // 输出: Alice

4、DOM 事件处理函数

当函数作为 DOM 事件处理函数时,this 指向触发事件的 DOM 元素。

赋值时机:事件触发时,this 被赋值为触发事件的 DOM 元素。

document.querySelector('button').addEventListener('click', function() {
  console.log(this); // this 指向被点击的按钮元素
});

三、箭头函数的this的指向和赋值时机

1、箭头函数没有自己的 this ,它会继承外层作用域的 this 

因此可以认为,箭头函数中的 this 在定义时就已经确定,而不是在调用时确定。

const obj = {
  name: 'Alice',
  greet: function() {
    setTimeout(() => {
      console.log(this.name); // this 继承自外层作用域
    }, 100);
  }
};

obj.greet(); // 输出: Alice

上面代码中的情况容易判断,此时箭头函数的 this 指向对象 obj 本身。

2、如果箭头函数写在普通函数中,甚至多层箭头函数调用呢?

结果是,箭头函数会逐层继承,直到非箭头函数。

const obj = {
  name: 'Alice',
  greet: function() {
    const outerFunc = () => {
      const innerFunc = () => {
        console.log(this.name); // this 继承自 greet 函数的 this
      };
      innerFunc();
    };
    outerFunc();
  }
};

obj.greet(); // 输出: Alice

3、如何理解【箭头函数中的 this 在定义时就已经确定】?

 outerFunc是在运行时才能确定 this 指向,那么innerFunc如何在定义时就能确定 this 的值呢?

function outerFunc() {
  const innerFunc = () => {
    console.log(this); // this 继承自 outerFunc 的 this
  };
  innerFunc();
}

outerFunc(); // 输出: Window {...}(非严格模式)或 undefined(严格模式)

事实是:innerFunc的 this 静态绑定了 outerFunc的 this 的引用

在onterFunc定义时,innerFunc的 this 并不是一个独立的值,而是直接指向 onterFunc的 this。等待onterFunc被调用时,其 this 被赋值,innerFunc的 this 也就获得了相同的指向。

4、为何要强调箭头函数继承的是外层【作用域】的 this 值?

因为普通函数是动态绑定的,只与调用时的上下文有关;而箭头函数是定义时静态绑定的,this 绑定到哪里需要看所在作用域

而作用域和上下文是两个不同概念,作用域指的是变量的可见性和生命周期,是静态的,在代码编写时就已经确定。

由于对象字面量没有自己的作用域,因此将 func2 从普通函数改为箭头函数后,输出会从 Alice 改为 Bob。

var name = 'Bob';
const obj = {
  name: 'Alice',
  func2: () => {
    console.log(this.name);
  }
};

obj.func2(); // 输出: Bob

综上,记忆【箭头函数没有自己的 this ,它会继承外层作用域的 this 值】比记忆【箭头函数中的 this 在定义时就已经确定】更加重要。

四、回调函数的this的指向和赋值时机

首先看一个例子:

var name = "lucy";
var obj = {
    name: "martin",
    say: function () {
        console.log(this.name);
    }
};

obj.say(); // 输出: martin
setTimeout(obj.say, 0); // 输出: lucy

可以看到,回调函数中的 this 指向了全局对象。

原因是:当obj.say作为回调传递时,它不再是作为obj的方法调用,而是作为一个独立的函数调用。setTimeout 在内部会直接调用传入的回调函数,而不是通过对象调用

这种情况本质上是 this 动态绑定的对象丢失了,可以通过修改为箭头函数来避免这种情况。

五、applycallbind 改变函数运行时的 this 指向

1、apply

作用:临时改变 this 指向一次,并立即执行

参数:① this 的目标指向对象;②函数参数数组

fn.apply(obj,[1,2]);

2、call  (与 apply 基本相同)

作用:临时改变 this 指向一次,并立即执行

参数:① this 的目标指向对象;②函数参数数组

fn.call(obj,[1,2]);

3、bind

作用:返回一个永久改变 this 指向的函数,不会立即执行

参数:① this 的目标指向对象;②函数参数数组(可分多次传入)

const bindFn = fn.bind(obj);
bindFn(1,2)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值