工作中经常会用到let _this = this;为什么要这么写呢?于是我决定在空闲时间一查究竟:

this 指向问题是入坑前端必须了解知识点,现在迎来了ES6时代,因为箭头函数的出现,所以感觉有必要对 this 问题梳理一下,遂有此文 在非箭头函数下, this 指向调用其所在函数的对象,而且是离谁近就是指向谁(此对于常规对象,原型链, getter & setter等都适用);构造函数下,this与被创建的新对象绑定;DOM事件,this指向触发事件的元素;内联事件分两种情况,bind绑定, call & apply 方法等, 容以下一步一步讨论。箭头函数也会穿插其中进行讨论;

首先先讲一下关于vue使用时this指向的问题(后面我会介绍所有this指向的情况):

	<!DOCTYPE html>
	<html>
	<head>
	    <meta charset="UTF-8">
	    <title></title>
	    <script src="http://libs.baidu.com/jquery/2.0.0/jquery.js"></script>
	    <script src="https://unpkg.com/vue@2.5.9/dist/vue.js"></script>
	</head>
	<div id="app" style="width: 100%;height: auto;font-size:20px;">
	    <p id="id1"></p>
	    <p id="id2"></p>
	</div>
	<script type="text/javascript">
	    var message = "Hello!";
	    var app = new Vue({
	        el:"#app",
	        data:{
	            message: "你好!"
	        },
	        created: function() {
	          this.showMessage1();    //this 1
	          this.showMessage2();   //this 2
	        },
	        methods:{
	            showMessage1:function(){
	                setTimeout(function() {
	                   document.getElementById("id1").innerText = this.message;  //this 3
	                }, 10)
	            },
	            showMessage2:function() {
	                setTimeout(() => {
	                   document.getElementById("id2").innerText = this.message;  //this 4
	                }, 10)
	            }
	        }
	    });
	</script>
	</html>

	第一个输出英文"Hello!”,第二个输出中文“你好!”。这说明了showMessage1()里的this指的是window,而showMessage2()里的this指的是vue实例。

※  对于普通函数(包括匿名函数),this指的是直接的调用者,在非严格模式下,如果没有直接调用者,this指的是window。showMessage1()里setTimeout使用了匿名函数,this指向
window。
※  箭头函数是没有自己的this,在它内部使用的this是由它定义的宿主对象决定。showMessage2()里定义的箭头函数宿主对象为vue实例,所以它里面使用的this指向vue实例。
**注**:
		【普通函数的this】
		         普通函数的this是由动态作用域决定,它总指向于它的直接调用者。具体可以分为以下四项:
		this总是指向它的直接调用者, 例如 obj.func() ,那么func()里的this指的是obj。
		在默认情况(非严格模式,未使用 'use strict'),如果函数没有直接调用者,this为window
		在严格模式下,如果函数没有直接调者,this为undefined
		使用call,apply,bind绑定的,this指的是绑定的对象;
		
		绑定vue实例到this的方法,为了避免this指向出现歧义,有两种方法绑定this。
**1,使用bind**
		showMessage1()可以改为:
		showMessage1:function(){
		    setTimeout(function() {
		       document.getElementById("id1").innerText = this.message;  //this 3
		    }.bind(this), 10)
		}
		对setTimeout()里的匿名函数使用bind()绑定到vue实例的this。这样在匿名函数内的this也为vue实例。

**2,把vue实例的this赋值给另一个变量再使用**
		showMessage1()也可以改为
		showMessage1:function(){
		    var self = this;
		    setTimeout(function() {
		       document.getElementById("id1").innerText = self.message;  //改为self
		    }.bind(this), 10
		}

下面是一白查询总结的关于所有this指向的问题,供大家参考:

1,全局环境下,this 始终指向全局对象(window), 无论是否严格模式;

	console.log(this.document === document); // true
	// 在浏览器中,全局对象为 window 对象:
	console.log(this === window); // true
	this.a = 37;
	console.log(window.a); // 37

2,函数直接调用,普通函数内部的this分两种情况,严格模式和非严格模式。

	非严格模式下,this 默认指向全局对象window
	function f1(){
	  return this;
	}
	
	f1() === window; // true
	而严格模式下, this为undefined
	
	function f2(){
	  "use strict"; // 这里是严格模式
	  return this;
	}
	
	f2() === undefined; // true

3,对象中的this,对象内部方法的this指向调用这些方法的对象,

	函数的定义位置不影响其this指向,this指向只和调用函数的对象有关。
	多层嵌套的对象,内部方法的this指向离被调用函数最近的对象(window也是对象,其内部对象调用方法的this指向内部对象, 而非window)。
	//1
	var o = {
	  prop: 37,
	  f: function() {
	    return this.prop;
	  }
	};
	console.log(o.f());  //37
	var a = o.f;
	console.log(a()):  //undefined
	
	var o = {prop: 37};
	
	function independent() {
	  return this.prop;
	}
	
	o.f = independent;
	
	console.log(o.f()); // logs 37
	
	//2
	o.b = {
	  g: independent,
	  prop: 42
	};
	console.log(o.b.g()); // logs 42

4,原型链中this,原型链中的方法的this仍然指向调用它的对象,与以上讨论一致;看个例子,

	var o = {
	  f : function(){ 
	    return this.a + this.b; 
	  }
	};
	var p = Object.create(o);
	p.a = 1;
	p.b = 4;
	
	console.log(p.f()); // 5
可以看出, 在p中没有属性f,当执行p.f()时,会查找p的原型链,找到 f 函数并执行,但这与函数内部this指向对象 p 没有任何关系,只需记住谁调用指向谁。

以上对于函数作为getter & setter 调用时同样适用。

5,构造函数中this,构造函数中的this与被创建的新对象绑定。

注意:当构造器返回的默认值是一个this引用的对象时,可以手动设置返回其他的对象,如果返回值不是一个对象,返回this。

6,call & apply

	当函数通过Function对象的原型中继承的方法 call() 和 apply() 方法调用时, 其函数内部的this值可绑定到 call() & apply() 方法指定的第一个对象上, 如果第一个参数不是对象,JavaScript内部会尝试将其转换成对象然后指向它。

7,bind 方法

bind方法在ES5引入, 在Function的原型链上, Function.prototype.bind。通过bind方法绑定后, 函数将被永远绑定在其第一个参数对象上, 而无论其在什么情况下被调用。

8,DOM事件处理函数,当函数被当做监听事件处理函数时, 其 this 指向触发该事件的元素 (针对于addEventListener事件)

  // 被调用时,将关联的元素变成蓝色
    function bluify(e){
      //在控制台打印出所点击元素
      console.log(this);
      //阻止时间冒泡
      e.stopPropagation();
      //阻止元素的默认事件
      e.preventDefault();      
      this.style.backgroundColor = '#A5D9F3';
    }
    // 获取文档中的所有元素的列表
    var elements = document.getElementsByTagName('*');

    // 将bluify作为元素的点击监听函数,当元素被点击时,就会变成蓝色
    for(var i=0 ; i<elements.length ; i++){
      elements[i].addEventListener('click', bluify, false);
    }
	以上代码建议在网页中执行以下,看下效果。

9,内联事件,内联事件中的this指向分两种情况:

	当代码被内联处理函数调用时,它的this指向监听器所在的DOM元素
	当代码被包括在函数内部执行时,其this指向等同于 ****函数直接调用****的情况,即在非严格模式指向全局对象window, 在严格模式指向undefined

10,setTimeout & setInterval,对于延时函数内部的回调函数的this指向全局对象window(当然我们可以通过bind方法改变其内部函数的this指向)

	//默认情况下代码
	function Person() {  
	    this.age = 0;  
	    setTimeout(function() {
	        console.log(this);
	    }, 3000);
	}
	
	//通过bind绑定
	function Person() {  
	    this.age = 0;  
	    setTimeout((function() {
	        console.log(this);
	    }).bind(this), 3000);
	}
	
	var p = new Person();//3秒后返回构造函数新生成的对象 Person{...}

11,箭头函数中的 this,由于箭头函数不绑定this, 它会捕获其所在(即定义的位置)上下文的this值, 作为自己的this值

	所以 call() / apply() / bind() 方法对于箭头函数来说只是传入参数,对它的 this 毫无影响。
	考虑到 this 是词法层面上的,严格模式中与 this 相关的规则都将被忽略。(可以忽略是否在严格模式下的影响)
	因为箭头函数可以捕获其所在上下文的this值 所以:
	function Person() {  
	    this.age = 0;  
	    setInterval(() => {
	        // 回调里面的 `this` 变量就指向了期望的那个对象了
	        this.age++;
	    }, 3000);
	}
	
	var p = new Person();
	以上代码可以得到我们所以希望的值,下图可以看到,在setTimeout中的this指向了构造函数新生成的对象,而普通函数指向了全局window对象

总结不易,如果发现错误的地方,欢迎留言指正出来。。。

Logo

前往低代码交流专区

更多推荐