this指向问题分析

this指向总结:

  • 在函数体中,简单调用该函数时(非显式/隐式邦定下),严格模式下this绑定到undefined,否则绑定到全局对象window/global;
  • 一般构造函数new调用,绑定到新创建的对象上;
  • 一般由call/apply/bind方法显式调用,绑定到指定参数的对象上;
  • 一般由上下文对象调用,绑定在该对象上;
  • 箭头函数中,根据外层上下文绑定的this决定this指向。
一:全局环境下的this
	function f1(){
		console.log(this);
	}
	function f2(){
		'use strict'
		console.log(this);
	}
	f1();//window
	f2();//undefined
	const foo={
		bar:10,
		fn:function(){
			console.log(this);
			console.log(this.bar);
		}
	}
	foo.fn();//{bar:10,fn:f}  10
	var fn1=foo.fn;
	fn1();//window undefined

虽然fn函数在foo对象中作为方法被引用,但是在赋值给fn1之后,fn1的执行仍然是在window的全局环境中。
在执行函数时,如果函数中的this是被上一级的对象所调用,那么this指向的就是上一级的对象;否则只想全局对象。

二:上下文对象调用中的this

当存在更复杂的调用关系时,有如下例题:

    const person={
    		name:"Lucas",
    		brother:{
    			name:'Mike',
    			fn:function(){
    				return this.name;
    			}
    		}
    	}
    	console.log(person.brother.fn());//Mike

在这种嵌套的关系中,this指向最后调用它的对象,因此输出Mike.

	const o1={
		text:'o1',
		fn:function(){
			return this.text
		}
	}
	const o2={
		text:'o2',
		fn:function(){
			return o1.fn()
		}
	}
	const o3={
		text:'o3',
		fn:function(){
			var fn=o1.fn;
			return fn();
		}
	}
	console.log(o1.fn());//o1
	console.log(o2.fn());//o1
	console.log(o3.fn());//undefined
  • 第一个console最简单,this指向调用它的对象o1;
  • 第二个console的o2.fn(),最终还是调用o1.fn();
  • 最后一个在进行var fn=o1.fn;赋值之后,是”裸奔“调用,所以this指向window。

若需要console.log(o2.fn())输出o2,需要怎么操作:
1、改变this的指向,使用call/apply/bind,即

console.log(o1.fn.call(o2));

2、改写o2

	const o2={
		text:'o2',
		fn:o1.fn
		}
	}
三、call/appli/bind改变this指向

call/apply是直接进行相关函数调用;bind不会执行相关函数,而是返回一个新的函数,这个新的函数已经自动绑定了新的this指向。

	const func={
		name:'Lucas',
		logName:function(){
			console.log(this.name);
		}
	}
	const bar={
		name:'Mike'
	}
	func.logName.call(bar);
四、构造函数和this

new调用构造函数的具体过程:

var obj={};
obj.__proto__=Foo.prototype;
Foo.call(obj);

如果在构造函数中出现了显示return的情况,则分为两种场景:

function Foo(){
	this.user="Lucas";
	const o={};
	return o;
}
const instance=new Foo();
console.log(instance.user);//undefined

此时instance返回的是空对象o。

function Foo(){
	this.user="Lucas";
	return 1;
}
const instance=new Foo();
console.log(instance.user);//Lucas

此时instance返回的目标对象实例是this。
结论:如果构造函数中显式返回一个值,且返回的是一个对象,那么this就指向这个返回的对象;如果返回的不是一个对象,那么this仍然指向实例。

五、箭头函数中this指向

箭头函数使用this不适用以上标准规则,而是根据外层(函数或全局)上下文来决定。

const fnn={
	fn:function(){
		setTimeout(function(){
			console.log(this);
		})
	}
}
fnn.fn();//window

this出现在setTimeout()中的匿名函数里,因此this指向window对象。如果需要this指向fnn这个对象,可以巧用箭头函数。

const fnn={
	fn:function(){
		setTimeout(()=>{
			console.log(this);
		})
	}
}
fnn.fn();//{fn:f}
六、this的优先级
function foo(){
	console.log(this.a);
}
const obj1={
	a:1,
	foo:foo
}
const obj2={
	a:2,
	foo:foo
}
obj1.foo.call(obj2);
obj2.foo.call(obj1);

输出分别为2、1,也就是说call、apply的显示绑定一般来说优先级更高。

function foo(a){
	this.a=a;
}
const obj1={}
var bar=foo.bind(obj1);
bar(2);
console.log(obj1.a);//2

上述代码通过bind,将bar函数中的this绑定为obj1对象。执行bar(2)后,obj1.a的值为2。当再使用bar作为构造函数时:

var baz=new bar(3);
console.log(baz.a);

将会输出3,bar函数本身通过bind方法构造的函数,其内部已经将this绑定为obj1,它再作为构造函数,通过new调用时,返回的实例已经与obj1解绑。也就是说new绑定修改了bind绑定中的this,因此new绑定的优先级比显式bind绑定更高。

function foo(){
	return a=>{
		console.log(this.a);
	}
}
const obj1={
	a:2
}
const obj2={
	a:3
}
const bar=foo.call(obj1);
bar.call(obj2);

将会输出2,由于foo()的this绑定到obj1,bar()的this也会绑定到obj1,箭头函数的绑定无法修改。

var a=123;
const foo=()=>a=>{
	console.log(this.a);
}
const obj1={
	a:2
}
const obj2={
	a:3
}
const bar=foo.call(obj1);
bar.call(obj2);

箭头函数的绑定无法修改,此时this绑定到window,所以会输出123。但是如果把第一行a的赋值改为const a=123;则答案会输出undefined,原因是因为使用const声明的变量不会挂载到window全局对象中,因此this指向window时自然找不到a变量。

开放题分析

模拟原生bind():

Function.prototype.myBind=function(context){
	var me=this;
	var args=Array.prototype.slice.call(arguments,1);
	var F=function(){}
	F.prototype=this.prototype;
	var bound=function(){
		var innerArgs=Array.prototype.slice.call(arguments);
		var finalArgs=args.concat(innerArgs); 
		return me.apply(this instanceof F ? this:context || this,finalArgs);
	}
	bound.prototype=new F();
	return bound;
}
function foo(a){ 
	this.a=a;
}
const obj1={}
var bar=foo.myBind(obj1);
var baz=new bar(3);
console.log(baz.a);
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值