javascript中this的9种情况

44 阅读1分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第14天,点击查看活动详情

this

JavaScript 中的this 总是指向一个对象,而具体指向哪个对象是在运行时基于函数的执行环境动态绑定的,而非函数被声明时的环境。 一起来看一下javascript中this的各种情况吧。

一定不要看在哪里定义,只要看将来在哪里,如何被调用。

对象调用函数

let obj = {
   name: 'Tom',
   sayHi: function(){
      console.log(`${this.name} say hi.`);
   }
};
obj.sayHi();

this指向 .前的对象。

var fun1 = obj.sayHi();
fun1();

fun1中的this指向window。

例子:
document.getElementById 这个方法名实在有点过长,我们大概尝试过用一个短的函数来代替它,比如:

var getId = document.getElementById;
getId("#app");

在浏览器中执行过后就会发现,这段代码抛出了一个异常。这是因为许多引擎的document.getElementById 方法的内部实现中需要用到this。这个this 本来被期望指向document,当getElementById 方法作为document 对象的属性被调用时,方法内部的this 确实是指向document 的。

var getId = document.getElementById.bind(document);
var div = getId('app');
console.log(div.id) // 输出: div1

构造函数中

new Constructor(...)

this指向 new正在创建的新对象。

function Person(name){
  this.name = name;
}

let p1 = new Person("Tom");
console.log(p1);
function Person(name){
  this.name = name;
}

let p1 = new Person
console.log(p1);

但用new 调用构造器时,还要注意一个问题,如果构造器显式地返回了一个object 类型的对象,那么此次运算结果最终会返回这个对象,而不是我们之前期待的this:

var MyClass = function(){
  this.name = 'sven';
  return { // 显式地返回一个对象
  	name: 'anne'
  }
};

var obj = new MyClass();
alert ( obj.name ); // 输出:anne

如果构造器不显式地返回任何数据,或者是返回一个非对象类型的数据,就不会造成上述问题:

var MyClass = function(){
  this.name = 'sven'
  return 'anne'; // 返回string 类型
};
var obj = new MyClass();
alert ( obj.name ); // 输出:sven

对象原型上的函数

Constructor.prototype.fun = function(){}

原型对象中的方法,都是"子对象.fun()" 方式调用,所以,this指向调用这个func函数.前的子对象。

func(),匿名函数自调,回调

func();

(function(){ this... })(...);
                 
arr.forEach(function(){ this ... });

上三种情况this都指向window,严格模式下,指向undefined。

element.onclick = function(){...}

或者:

element.addEventListener("click", function(){...});

DOM事件处理函数里的this指向当前发出事件的.前的DOM元素。

这里可以改成箭头函数吗?

肯定不能:一旦改成箭头函数,this指向外层的window。功能就会出错。

window.id = 'window';
document.querySelector("#app").onclick = function(){
  console.log(this.id);
  var callback = function() {
    console.log(this.id);
  }

  callback();
};

改为:

window.id = 'window';
document.querySelector("#app").onclick = function(){
  console.log(this.id);
  var that = this;
  var callback = function() {
    console.log(that.id);
  }

  callback();
};
window.id = 'window';
document.querySelector("#app").onclick = function(){
  console.log(this.id);
  var callback = () =>  {
    console.log(this.id);
  }

  callback();
};

Vue中this默认都指向当前Vue组件实例对象

<button @click="func"></button>

export default {
   methods:{
       func(e){
					
			 }
	 }
}

如果想获得click事件触发的DOM元素,可以使用 e.target。

Vue3中使用composition API 去尽量避免使用this。

React中的this

没箭头函数,有了箭头函数之后:

import React, { Component } from 'react';
import store from '../redux';

export default class ReduxPage extends Component {
    constructor(props){
        super(props);
        // this.addBtnClick.bind(this);
    }

    // 早期
    // addBtnClick = function(){
	  //   store.dispatch({
    //       type: 'ADD',
    //       payload: 2
    //   });
    // }

    addBtnClick = ()=>{
        store.dispatch({
            type: 'ADD',
            payload: 2
        });
    }
  
    componentDidMount(){
        store.subscribe(() => {
            this.forceUpdate();
        });
    }

    render() {
        const { count } = store.getState().counter;
        
        return (
            <div>
                <div>
                    <span>{count}</span>
                    <button onClick={ this.addBtnClick }>增加</button>
                </div>
            </div>
        )
    }
}
import logo from './logo.svg';
import './App.css';
import ReduxPage from './components/ReduxPage';

function App() {
  return (
    <div className="App">
      <ReduxPage/>
    </div>
  );
}

export default App;

React中函数式组件,并没有组件实例this,去掉this的困惑。

箭头函数中的this

箭头函数中的this,指向函数之外最近的作用域中的this。

几乎所有的匿名函数都可以使用箭头函数来简化;箭头函数是对大多数匿名函数的简写。

// var log = function(msg) {
//     console.log(msg, this);
// }
var log = (msg) =>{
  console.log(msg, this);
}

log('呵呵');
let obj = {
  name: 'Tom',
  sayHi: function() {
    console.log(`${this.name} say hi.`);
  }
};
obj.sayHi();

// 下面name为空
let obj = {
  name: 'Tom',
  sayHi: () => {
    console.log(`${this.name} say hi.`);
  }
};
obj.sayHi();

箭头函数,对象的 { } 不是作用域,所以sayHi内的this会指向window。

总结:

  1. 函数中不包含this,或者希望函数内的this和外部this保持一致,可以改为箭头函数;
  2. 不希望函数内的this与函数外的this保持一致,都不能改为箭头函数;

刚才说过对象中的方法就不能改为箭头函数:

let obj = {
  name: 'Tom',
  sayHi() {
    console.log(`${this.name} say hi.`);
  }
};

既没有function,也没有 =》,只是简写,不影响this指向。

let obj = {
  name: 'Tom2',
  friends: ["Jerry", "Kate"],
  sayHi() {
    this.friends.forEach((item) => {
      console.log(`${this.name} say hi to ${item}`);
    });
  }
};
obj.sayHi();

问题:
箭头函数没有作用域,或箭头函数不是作用域? ×

  1. 箭头函数只是让this指向外部作用域的this;
  2. 箭头函数内的局部变量,仅再函数内可用,所以箭头函数是有作用域的,影响的只是this;

箭头函数底层相当于.bind(),永久绑定外部this。所以,call无法替换箭头函数中的this。

call, apply,bind

使用call,apply和bind可用改变函数中的this指向。

被bind永久绑定的this,即使使用call,也无法再替换为其他对象了。