JavaScript的this

this关键字一直在JavaScript中是一个神秘的东西,对非常有经验的JavaScript开发者,有时也很难说this到底指向什么。本文是整理一些的this指向问题以及自己的看法,希望有帮助。

this到底是个什么东西?一直让人琢磨不透?

很多人会有以下两种对于this的误会:

1.this在一个函数中指向自身。

2.this指向函数的作用域。

        其实,this是在运行时进行绑定的,并不是在编写时绑定的,它的上下文取决于函数调用时的各种条件。this的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。当一个函数调用时,会创建一个活动记录(有时候成为执行上下文)。这个记录会包含在哪里被调用(调用栈),函数的调用方式,传入的参数信息。this就是这个记录的一个属性,会在函数执行过程中用到。


1.默认绑定

最常见的函数调用类型:独立函数调用
function foo(){
	console.log(this.a);
}
var a = 2;
foo();  //2
调用foo()时,this.a被解析成了全局变量a,因为函数调用时应用了this的默认绑定,指向全局对象。

如果在严格模式下,则不能将全局对象用于默认绑定,因此this会绑定到undefined。
function foo(){
	'use strict'
	console.log(this.a);
}
var a = 2;
foo();  //TypeError:this is undefined

2.隐式绑定

通过 . 引用的函数,this会指向所引用的对象上去。
function foo(){
	console.log(this.a);
}
var obj = {
	a:2,
	foo:foo
}
obj.foo();  //2
当obj.foo引用foo函数,这里面地this就指向了Obj了。当函数引用有了上下文对象时,隐式绑定规则会把函数调用中的this绑定到这个上下文对象。

隐式丢失
一个常见的this绑定问题就是被隐式绑定的函数会丢失绑定对象,也就是说它会应用默认绑定,从而把this绑定到全局对象或undefined上,取决于是否是严格格式。
function foo(){
	console.log(this.a);
}
var obj = {
	a:2,
	foo:foo
}
var bar = obj.foo;  //函数别名
var a = "global";
bar();  // global
虽然bar是obj.foo的一个引用,但实际上,它的引用是foo函数本身,因此此时的bar()其实是一个不带任何修饰的函数调用,因此应用了默认绑定。

还有一种出乎意料的情况发生在传入回调函数时:
function foo(){
	console.log(this.a);
}
function doFoo(fn){
	//fn其实引用的是foo
	fn();
}
var obj = {
	a:2,
	foo:foo
}
var a = "global";
doFoo(obj.foo);  //global
参数传递其实就是一种隐式绑定,因此我们传入函数时也会被隐式赋值,所以结果和上一个例子一样。
还有一种情况也和上面的一样,调用回调函数的函数可能会修改this。

3.显式绑定

在分析隐式绑定时,必须在一个对象内部包含一个指向函数的属性,并通过这个属性间接引用函数,从而把this间接绑定到这个对象上。如果不想在对象内部包含函数引用,而是在某个对象上强制调用函数,怎么做?
JavaScript提供的绝大多数函数以及自己创建的所有函数都可以使用Call(。。。)和apply(。。。)方法。
他们的第一个参数是一个对象,是给this准备的,接着在调用函数时将其绑定到this。这种方式叫做显式绑定。
function foo(){
	console.log(this.a);
}

var obj = {
	a:2,
}
foo.call(obj);  //2
显示绑定仍然无法解决之前丢失绑定问题。
硬绑定
function foo(){
	console.log(this.a);
}

var obj = {
	a:2,
}
var bar = function(){
	foo.call(obj);  
}
bar();  //2
setTimeout(bar,100);  //2
//硬绑定的bar()不可能再修改它的this
bar.call(window);  //2
我们创建了函数bar(),并在它的内部手动调用了foo.call(obj),因此强制把foo的this绑定到了obj。无论之后如何调用函数bar,它总会手动在obj上调用foo。这种绑定是一种强制绑定,因此成为硬绑定。

4.new绑定

对一个构造函数进行调用new实例,this就指向这个实例对象。
使用new来调用函数,或者说发生构造函数调用时,会自动执行下面的操作:
1.创建或者说构造一个全新的对象
2.这个新对象会被执行原型连接
3.这个新对象会绑定到函数调用的this
4.如果函数没有返回其他的对象,那么new表达式中的函数调用会自动返回这个新对象。

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

5.箭头函数

ES6中介绍了一种无法使用上面的规则的一种特殊函数,叫做箭头函数。箭头函数是根据外层(函数或者全局)作用域来决定this。
function foo(){
	//返回一个箭头函数
	return (a)=>{
		//this继承自foo()
		console.log(this.a);
	}
}
var obj1 = {
	a:2
}
var obj2 = {
	a:3
}
var bar = foo.call(obj1);
bar.call(obj2); //2,不是3!
foo()内部创建的箭头函数会捕获调用时foo()的this。由于foo()的this绑定到obj1,bar(引用箭头函数)的this也会绑定到obj1,箭头函数的绑定无法被修改,new也不行!


判断this顺序
1.由new调用?绑定到新创建的对象。
2.由call或者apply(或者bind)调用?绑定到指定的对象。
3.由上下文对象调用?绑定到那个上下文对象。
4.默认:在严格模式下绑定到undefined,否则绑定到全局对象。



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值