先简单的总结一下
JavaScript 中 this 不是固定不变的,它会随着执行环境的改变而改变
- 在对象方法中,this表示该方法所属的对象(this 指向调用它所在方法的对象)
- 如果单独使用this表示全局对象,非严格模式下(node环境下:Global;浏览器下:Window)
- 在函数使用中,this 指向函数的所属者(严格模式下:函数是没有绑定到 this 上,这时候 this 是未定义的undefined)
- 在事件中,this表示接收事件的元素(在 HTML 事件句柄中,this 指向了接收事件的 HTML 元素)
- 类似call()和apply()方法可以将this引用到任何对象中(apply 和 call 允许切换函数执行的上下文环境(context),即 this 绑定的对象,可以将 this 引用到任何对象)
终极秘籍:
-
如果一个函数中有this,但是它没有被上一级的对象所调用,那么this指向的就是window,这里需要说明的是在js的严格版中this指向的不是window,但是我们这里不探讨严格版的问题,你想了解可以自行上网查找。
-
如果一个函数中有this,这个函数有被上一级的对象所调用,那么this指向的就是上一级的对象。
-
如果一个函数中有this,这个函数中包含多个对象,尽管这个函数是被最外层的对象所调用,this指向的也只是它上一级的对象
针对以上三点举几个例子:
例1:
function a(){
var user = "阳光明媚";
console.log(this.user); //undefined
console.log(this); //Window
}
a();
例2:
function a(){
var user = "阳光明媚";
console.log(this.user); //undefined
console.log(this); //Window
}
window.a();
例1和例2是一样的,函数a是被Window对象所点出来:
例3:
var o = {
user:"阳光明媚",
fn:function(){
console.log(this.user); //阳光明媚
}
}
o.fn();
例3这里的this指向的是对象o,因为调用这个fn是通过o.fn()执行的,那自然指向就是对象o
例4:
var o = {
user:"阳光明媚",
fn:function(){
console.log(this.user); //阳光明媚
}
}
window.o.fn();
例3和例4几乎是一样的,但是这里的this为什么不是指向window,看下面的例子
例5:
var o = {
a:8,
b:{
a:15,
fn:function(){
console.log(this.a); //15
}
}
}
o.b.fn();
例6:
var o = {
a:8,
b:{
// a:15,
fn:function(){
console.log(this.a); //undefined
}
}
}
o.b.fn();
尽管对象b中没有属性a,这个this指向的也是对象b,因为this只会指向它的上一级对象,不管这个对象中有没有this要的东西。
还有一种比较特殊的情况
例7:
var o = {
a:8,
b:{
a:15,
fn:function(){
console.log(this.a); //undefined
console.log(this); //window
}
}
}
var j = o.b.fn;
j();
这里this指向的是window,是不是有懵逼?其实是因为你没有理解一句话,这句话同样至关重要。
this永远指向的是最后调用它的对象,也就是看它执行的时候是谁调用的,例子7中虽然函数fn是被对象b所引用,但是在将fn赋值给变量j的时候并没有执行所以最终指向的是window,这和例子4是不一样的,例子4是直接执行了fn。
this讲来讲去其实就是那么一回事,只不过在不同的情况下指向会有不同而已。
构造函数版this:
function Fn(){
this.user = "阳光明媚";
}
var a = new Fn();
console.log(a.user); //阳光明媚
这里之所以对象a可以点出函数Fn里面的user是因为new关键字可以改变this的指向,将这个this指向对象a,为什么我说a是对象,因为用了new关键字就是创建一个对象实例,理解这句话可以想想我们的例子4,我们这里用变量a创建了一个Fn的实例(相当于复制了一份Fn到对象a里面),此时仅仅只是创建,并没有执行,而调用这个函数Fn的是对象a,那么this指向的自然是对象a,那么为什么对象a中会有user,因为你已经复制了一份Fn函数到对象a中,用了new关键字就等同于复制了一份
当this碰到return时
function fn(){
this.user = '阳光明媚';
return {};
}
var a = new fn;
console.log(a.user); //undefined
function fn(){
this.user = '阳光明媚';
return function(){};
}
var a = new fn;
console.log(a.user); //undefined
function fn(){
this.user = '阳光明媚';
return 15;
}
var a = new fn;
console.log(a.user); //阳光明媚
function fn(){
this.user = '阳光明媚';
return undefined;
}
var a = new fn;
console.log(a.user); //阳光明媚
通过以上几个例子,也就是说如果返回值是一个对象,那么this指向的就是那个返回的对象,如果返回值不是一个对象那么this还是指向函数的实例。
function fn(){
this.user = '阳光明媚';
return undefined;
}
var a = new fn;
console.log(a); //fn {user: "阳光明媚"}
还有一点就是虽然null也是对象,但是在这里this还是指向那个函数的实例,因为null是基本数据类型。
function fn(){
this.user = '阳光明媚';
return null;
}
var a = new fn;
console.log(a.user); //阳光明媚
new操作符会改变函数this的指向问题
function fn(){
this.num = 1;
}
var a = new fn();
console.log(a.num); //1
为什么this会指向a?首先new关键字会创建一个空的对象,然后会自动调用一个函数apply方法,将this指向这个空对象,这样的话函数内部的this就会被这个空的对象替代。
let fn = function(){
alert(this.name)
}
let obj = {
name: '',
fn
}
fn() // 方法1
obj.fn() // 方法2
fn.call(obj) // 方法3
let instance = new fn() // 方法4
- 方法1中直接调用函数
fn()
,这种看着像光杆司令的调用方式,this
指向window
(严格模式下是undefined
)。 - 方法2中是点调用
obj.fn()
,此时this
指向obj
对象。点调用中this
指的是点前面的对象。 - 方法3中利用
call
函数把fn
中的this
指向了第一个参数,这里是obj
。即利用call
、apply
、bind
函数可以把函数的this
变量指向第一个参数。 - 方法4中用
new
实例化了一个对象instance
,这时fn
中的this
就指向了实例instance
。
如果同时发生了多个规则怎么办?其实上面四条规则的优先级是递增的:
fn() < obj.fn() < fn.call(obj) < new fn()
首先,new
调用的优先级最高,只要有new
关键字,this
就指向实例本身;接下来如果没有new
关键字,有call、apply、bind
函数,那么this
就指向第一个参数;然后如果没有new、call、apply、bind
,只有obj.foo()
这种点调用方式,this
指向点前面的对象;最后是光杆司令foo()
这种调用方式,this
指向window
(严格模式下是undefined
)。
es6中新增了箭头函数,而箭头函数最大的特色就是没有自己的this、arguments、super、new.target
,并且箭头函数没有原型对象prototype
不能用作构造函数(new
一个箭头函数会报错)。因为没有自己的this
,所以箭头函数中的this
其实指的是包含函数中的this
。无论是点调用,还是call
调用,都无法改变箭头函数中的this
。