彻底搞清 this 指向问题,普通函数和箭头函数的this问题

1. 什么是 this(概念必不可少!!!)

  1. this是 js 中的一个关键字

  2. 它是函数运行时,在函数体内部自动生成的一个对象,它只有在函数内部时才可以使用。this就是函数运行时所处的环境对象(即执行期上下文)

  3. 执行期上下文:所有的函数在被调用时都会创建一个执行期上下文,执行期上下文中记录着函数的调用栈函数的调用方式等,而 this就是其中的一个属性

2. this 是什么时候绑定的

this是在函数被调用的时候绑定的 ! ! ! !

当没有调用函数的时候,this的指向是没有被绑定的,所以this的指向此时是无法判断的
例:

	function test() {
	  console.log(this);
	}
	test();	//window,调用的时候才绑定的window

再看一个稍微复杂一点的例子:一个直接在定义里面调用,另一个存到外面之后再调用

	function test() {
	  console.log(this);
	}
	
	var obj = {
	  name: '张三',
	  foo: test
	};
	
	obj.foo(); // 1. obj

	var foo = obj.foo;
	foo(); // 2. window

上面这个例子中都是同一个函数 test,但 this 的值一个是 obj,另一个是却是window呢

不知道有没有听过这样一个规则:谁调用,this就指向谁

所以第一个是 obj 调用的所以指向obj

第二个是将函数存到外面之后调用window.foo(), 所以指向window

知道这个规则可以解决遇到的大部分的this的指向问题,在没写这篇文章之前我凭借这个规则(咳咳咳,省略3000字)…
但是我们并不满足于此!!!

总结通过上面的例子我们可以得出的结论:

  1. this值的绑定和函数定义时的位置没有关系。
  2. this值的绑定和函数调用的方式有关,谁调用函数,this就绑定为谁(或者说是指向谁)。
  3. this是在运行时,才会别绑定的,光定义、不运行相当于不起作用

我们来看一下this到底是如何绑定的,来深刻的理解谁调用,this就指向谁

3. this 是如何绑定的

绑定的方法大概有以下这几种

  • 默认绑定,即函数独立调用,this 指向 window
  • 隐式绑定,即函数通过某个对象进行调用,那么 this就指向这个调用的对象
  • 显示绑定,即 call、apply、bind
  • new绑定
a. 默认绑定
  1. 定义一个函数直接调用(函数单独的被调用,并没有绑定到那个对象上)
    默认绑定的就是 window对象,例: window.test
	// 1、函数直接被调用
	function test() {`在这里插入代码片`
	 console.log(this)
	}
	test(); // window
  1. 一个函数调用另一个函数, 函数只是在另一个函数里面被单独的直接调用, 并没有绑定到那个对象上
    所以还是默认绑定到 window 对象上。
	function test1() {
	 console.log(this);
	 test2();  // window
	}
	
	function test2() {
	 console.log(this);
	}
	
	test1(); // window
  1. 将函数作为参数传递到另一个函数中
    这个实际上跟第二个例子是类似的,函数 foo 里面调用了bar。
	function foo(fun) {
	  fun();
	}
	
	function bar() {
	  console.log(this);
	}
	foo(bar); // window
b. 隐式绑定
  1. 在隐式绑定中,如果函数调用位置是在一串对象属性链中,this绑定的是最内层的对象。

  2. 当函数引用有上下文对象时,隐式绑定规则会把函数调用中的this绑定到这个上下文对象。

例子:下面这个应该很熟悉,实际上这个 obj 就是 this 的上下文对象

	var obj = {
	  name: 'obj',
	  foo() {
	    console.log(this); 
	  }
	};
	obj.foo(); // obj

再看一个 obj 调用 obj 的例子:

	var obj = {
	  name: 'obj',
	  foo() {
	    console.log(this)
	  }
	};
	
	var obj1 = {
	  name: 'obj1',
	  obj2: obj
	};
	obj1.obj2.foo(); // obj

乍一看不是很好理解,如果把它变成下面这个样子,是不是很好看出它的上下文对象是 obj2

	var obj1 = {
	  name: 'obj1',
	  obj2: {
	    name: 'obj',
	    foo() {
	      console.log(this);
	    }
	  }
	};
	obj1.obj2.foo(); // obj
c. 显示绑定

为什么会有显示绑定?因为它是有限制的。

隐性绑定的限制:
如果上下文不包含我们的函数,用隐性绑定是要出错的,不可能每个对象都要加这个函数 ,那样的话扩展,维护性太差了。

如果说两个obj 都需要 foo方法 那么我们怎么办?

call apply bind
这里我们就要用到 js 给我们提供的函数 call 和 apply 它们的作用都是改变函数的this指向,第一个参数都是设置this对象。调用后直接执行。

	function foo(){
	 console.log(this);
	}
	
	var obj = {
	 name: "Perry",
	}
	var obj1 = {
	 name: "Lisa",
	}
	foo(); // window
	foo.call(obj) // obj
	foo.call(obj1) // obj1
	foo.apply(obj) // obj
	foo.apply(obj1) // obj1

bind是将this绑定到某个obj上,但是不执行,需要我们自己手动的调用

	let f = foo.bind(this对象, [参数])
	f()

使用这三个显示绑定 this的注意点

  1. 使用过bind绑定this之后再通过call、apply改变 this的绑定是无效的(例1)
  2. 原函数 foo,对其使用bind进行绑定后的返回的函数,再进行bind绑定是无效的(例2)
  3. 进行绑定时,如果this传的是null或undefined,那么此时绑定的 this是无效的(例3)
	function foo() {
	  console.log(this);
	}
	var obj1 = {
	  name: 'obj1'
	};
	var obj2 = {
	  name: 'obj2'
	}
	// 例1
	var bar = foo.bind(obj1);
	bar(); // obj1
	bar.call(obj2); // obj1
	bar.apply(obj2); // obj1
	
	// 例2
	var bar = foo.bind(obj1);
	bar(); // obj1
	var bar1 = bar.bind(obj2);
	bar1(); // obj1
	
	// 例3
	foo.call(null); //window
	foo.apply(undefined); // window
	var fun = foo.bind(null); 
	fun(); // window
d. new
	function Foo() {
	  this.a = 10
	}
	let foo = new Foo()
	console.log(foo.a);

实际上就是指向构造函数,与原型链有关

4. this绑定优先级

new 绑定 > 显示绑定 > 隐式绑定 > 默认绑定

5. 箭头函数的this问题

箭头函数中的this指向的是定义时的this,而不是执行时的this。

箭头函数的 this 是不进行绑定的,即就是 通过call、apply、bind是不能改变其 this指向的

箭头函数的this是由定义时外围最接近一层的非箭头函数的 this来决定的,也就是平常所说的箭头函数的this是从上层作用域中去找的,一直找不到的话即为 window

var name = 'window';
var obj1 = {
  name: 'obj1',
  foo() {
    return () => {
      console.log(this.name);
    }
  }
};

var obj2 = {
  name: 'obj2'
};

obj1.foo()(); // obj1

// 箭头函数的 this 是不能进行绑定的,它的 this 只会从上层作用域中去查找,所以给它进行绑定 this 是无效的
obj1.foo().call(obj2); //obj1

// 这里改变的是箭头函数上层作用域中的 this,也就是 foo 的 this,所以此时的 this指向的才是 obj2
obj1.foo.call(obj2)(); // obj2
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值