js中this关键字的指向问题

首先,搞清楚js中函数的几种调用方式
(1)普通函数调用
(2)作为方法来调用
(3)作为构造函数来调用
(4)使用apply和call来调用
(5)Function.prototype.bind()方法
(6)es6箭头函数

但是不管函数是按哪种方法来调用的,请记住一点:“谁调用这个函数或方法,this关键字就指向谁。”

(1)普通函数调用

var height=180;
function man(){
	this.name="张";
	age="10岁";
	console.log(this);//window
	console.log(this.name);//张
	console.log(this.age);//10岁
	console.log(this.height);//180
}
man();

在这段代码中,
1》man函数作为普通函数调用,实际上man是作为全局(顶层)对象window的一个方法来进行调用。即window.man()。所以输出:window。
2》那么man函数中的this就指向window,同时window还拥有了name属性,值为‘张’。所以输出:张。
3》age是局部变量,全局对象window可以输出局部变量,即拥有age属性。所以输出:10岁。
4》同理:var height=180;一开始定义了一个全局变量,相当于window的一个属性,即window.height=180;又在调用man()时,this指向window,所以会输出180。
小结:普通函数调用,是作为window对象的方法来调用的。显然this指向的是window对象。

(2)作为方法来调用

var name="张";
var man={
	name:"李",
	age:18,
	showMan:function(){
		console.log(this.name);//李
	}
}
man.showMan();//李
var showManA=man.showMan;
showManA();//张

1》man.showMan();即调用对象man的方法showMan,所以this指向man对象,则this.name相当于man.name。所以输出:李。
2》将man对象的方法赋值给showManA这个全局变量,即为window对象的属性。调用showManA(),相当于window.showManA(),所以this.name为window.name。输出:张。
换一种形式

var manA={
	name:"李",
	showMan:function(){
		console.log(this.name);
	}
}
var manB={
	name:"张",
	sayMan:manA.showMan
}
manA.showMan();//李
manB.sayMan();//张

1》manA.showMan(),this指向manA对象。
所以输出:李。
2》manB.sayMan(),虽然showMan方法是在manA这个对象中定义,但是调用却在manB对象中,所以this指向manB对象。
所以输出:张。

(3)作为构造函数调用

function Man(name){
	this.name=name;
}
var manA=Man("李");
//console.log(manA.name);//undefined
console.log(window.name);//李
//上面代码没有进行new操作,相当于window对象调用Man("李")方法,
//那么this指向window对象,并进行赋值操作window.name="李"

var manB=new Man("李");
console.log(manB.name);//李

new操作符

//模拟new操作符(实例化对象)的内部过程
function Man(name){
	this.name=name;
}
function man(name){
	var obj={};
	obj.__proto__=Man.prototype;//原型继承
	Man.call(obj,name);
	return obj;
}
var manB=man("李");
console.log(manB.name);

(4)call/apply方法的调用

“改变this关键字的指向”
在JS里函数也是对象,因此函数也有方法。
从Function.prototype上继承Function.prototype.call/Function.prototype.apply方法。
Obj.method.apply(AnotherObj,arguments);

var name="李";
var Man={
	name:"张",
	showMan:function(){
		console.log(this.name);
	}
}
Man.showMan.call();//李
//这里call方法里面的第一个参数为空,默认指向window。
//虽然showMan方法定义在Man对象里面,但是使用call方法后,将showMan方法里面的this指向了window。因此最后会输出"李";
function ManA(name1,name2){
	this.name1=name1;
	this.name2=name2;
	this.change=function(x,y){
		this.name1=x;
		this.name2=y;
	}
}
 var manA=new ManA("张","李");
 var ManB={
 	name1:"王",
 	name2:"赵"
 };
 manA.change.call(manB,"wang","li");
 console.log(manB.name1);//wang
 console.log(manB.name2);//li
//ManB调用ManA的change方法,将manA中的this绑定到对象ManB上。

(5)Function.prototype.bind()方法

var name="李";
function Man(name){
	this.name=name;
	this.sayName=function(){
		setTimeout(function(){
			console.log("我的名字是"+this.name);
		},50)
	}
}
var man=new Man("张");
man.sayName();//我的名字是李
//这里的setTimeout()延时函数,相当于window.setTimeout(),由window这个全局对象对调用,因此this的指向为window, 则this.name则为李

那么如何输出“张”呢?

var name="李";
function Man(name){
	this.name=name;
	this.sayName=function(){
		setTimeout(function(){
			console.log("我的名字是"+this.name);
		}.bind(this),50)//注意这个地方使用的bind()方法,绑定setTimeout里面的匿名函数的this一直指向Man对象
	}
}
var man=new Man("张");
man.sayName();//我的名字是张

setTimeout(function(){console.log(this.name)}.bind(this),50);,匿名函数使用bind(this)方法后创建了新的函数,这个新的函数不管在什么地方执行,this都指向的Man,而非window。
注意:
setTimeout/setInterval/匿名函数执行的时候,this默认指向window对象,除非手动改变this的指向。在《javascript高级程序设计》当中,写到:“超时调用的代码(setTimeout)都是在全局作用域中执行的,因此函数中的this的值,在非严格模式下是指向window对象,在严格模式下是指向undefined”。本文都是在非严格模式下的情况。

var name="李";
function Man(){
	this.name="张";
	this.showName=function(){
		console.log(this.name);
	}
		setTimeout(this.showName,50)
}
var man=new Man();//李
//在setTimeout(this.showName,50)语句中,会延时执行this.showName方法。
//50ms后,执行this.showName方法,this.showName里面的this此时便指向了window对象。

修改上述代码

var name="李";
function Man(){
	this.name="张";
	var that=this;
	this.showName=function(){
		console.log(that.name);
	}
		setTimeout(this.showName,50)
}
var man=new Man();//张
//这里在Man函数当中将this赋值给that,即让that保存Man对象,因此在setTimeout(this.showName,50)执行过程当中,console.log(that.name)即会输出Man对象的属性"张"。

匿名函数

var name="李";
var man={
	name:"张",
	showName:function(){
		console.log(this.name);
	},
	sayName:function(){
		(function(callback){
			callback();
		})(this.showName)
	}
}
man.sayName();//李
//匿名函数的执行同样在默认情况下this是指向window
var name="李";
var man={
	name:"张",
	showName:function(){
		console.log(man.name);
	},
	sayName:function(){
		(function(callback){
			callback();
		})(man.showName)
	}
}
man.sayName();//李
//匿名函数的执行同样在默认情况下this是指向window的,除非手动改变this的绑定对象

eval函数

var name="李";
var man={
	name:"张",
	showName:function(){
		eval("console.log(this.name)");
	}
}
man.showName();//张
var a=man.showName;
a();//李
//该函数执行的时候,this绑定到当前作用域的对象上

(6)箭头函数

es6里面this指向固定化,始终指向外部对象,因为箭头函数没有this,因此它自身不能进行new实例化,同时也不能使用call, apply, bind等方法来改变this的指向。

function Timer(){
	this.seconds=0;
	setInterval(()=>this.seconds++,1000);
}
var timer=new Timer();
setTimeout(()=>console.log(timer.seconds),3000);//3
//在构造函数内部的setInterval()内的回调函数,this始终指向实例化的对象,并获取实例化对象的seconds的属性,每1s这个属性的值都会增加1。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值