再写this学习内容之前,先补充一个小知识点:在函数中变量没有声明直接赋值后,这个变量会成为全局变量
外面是可以访问到函数内的值的。
什么是this?
this是 JavaScript 语言的一个关键字。它是函数运行时,在函数体内部自动生成的一个对象,只能在函数体内部使用。
那么,this的值是什么呢?函数的不同使用场合,this有不同的值。总的来说,this就是函数运行时所在的环境对象(重点理解这句话)。
this的用法
分四种情况详细讨论
情况一:纯粹的函数调用
var a = 2;
function test() {
console.log(this.a); // 2
}
test();
这是函数的最通常用法,属于全局性调用,this指向的是全局对象window。如果严格模式,this为undefined。需要了解严格模式,可以戳这javascript严格模式
请看下面这种情况
var a = 1;
function test() {
a=2;
console.log(this.a); // 2
}
test();
这里的this依然指向window,但是值为什么是2?我在文章最上面做出了解释 ,函数体里面的a也是全局变量同名被覆盖。
情况二:作为对象方法的调用
函数还可以作为某个对象的方法调用,this指向这个上级对象(这个上级对象一定是“亲爸爸”)。
var a = 1
var b = {
a: 2,
fn: function () {
console.log(a) //1
console.log(this.a) //2
}
}
b.fn()
注意a和this.a的区别:a会向外层作用域找,找到全局对象上的a(如果全局对象上没有就会报错) ,此时this指向对象b。
修改一下:
var a = 1;
var b = {
a: 2,
c: {
a:3,
fn: function () {
console.log(this) // {a: 3, fn: ƒ}
console.log(this.a) //3
}
}
}
b.c.fn();
b调用c,c调用fn,所以this指向直接调用这,也就是c,所以结果为3。
下面再看:
var a = 1
var b = {
a: 2,
fn: function () {
console.log(this.a) //1
}
}
var fn1=b.fn//b对象将fn方法赋值给fn1变量,那么此时fn方法并没有被任何人调用,只是单纯的赋值。执行fn1方法之后,fn方法才算真正被调用
fn1()
这里只是将fn方法赋值给fn1变量,并没有执行。执行fn1时,fn方法才算调用执行,此时fn1是挂载再window对象上的,此时this指向window。
再看
var a = 1
var b = {
a : 2,
fn : function(){
setTimeout(function() {
console.log(this.a) //1
},100)
}
}
b.fn()
从上述中可以看到b.fn()里面执行了setTimeout函数,setTimeout内的this是指向了window对象,这是因为setTimeout()调用的代码运行在与所在函数完全分离的执行环境上。熟知EventLoop的人员应该明白,setTimeout函数其实调用的是浏览器提供的其他线程,当JS主线程走完之后,会调用任务队列的函数,也即是setTimeout函数,此时setTimeout是在window上调用的,那么this自然指向了window。不了解可以看浏览器的运行机制—3.浏览器的渲染进程
此时是否觉得:this指向就是那个对象调用函数,函数里面的this指向那个对象。
不急,继续往下看。
情况三 作为构造函数调用
所谓构造函数,就是通过这个函数,可以生成一个新对象(object)。这时,this就指这个新对象。如果不使用new调用,则和普通函数一样。
function A() {
this.a = 0
}
A.prototype = {
a: 1,
get() {
console.log(this.a)
}
}
var a = new A()
console.log(a);//A
a.get()//0
this指向a,a调用get方法,a对象上有a属性,值为0
情况四 apply和call 调用
apply和call函数都可以改变this指向。它的第一个参数就表示改变后的调用这个函数的对象。因此,此时this指的就是这第一个参数。
var x = 0;
function test() {
console.log(this.x);
}
var obj = {};
obj.x = 1;
obj.m = test;
obj.m.apply() // 0
当apply函数的参数为空时,默认调用全局对象。因此,这时的运行结果为0
,this指的是全局对象window。
箭头函数的this指向(ES6)
在前面,曾说过:this指向就是那个对象调用函数,函数里面的this指向那个对象(适用于es5的写法)
这里并不适用 ,箭头函数中的this指向是固定的,箭头函数根本没有自己的this,它只会从自己的作用域链的上一层继承this。正是因为它没有this,所以箭头函数也就不能用作构造函数,并且也就不能用call()、apply()、bind()这些方法去改变this的指向(改变无效,输出的仍是作用域链的上一层)。
var a = 1
var b = {
a: 2,
fn:()=>{
console.log(this.a); //1
}
}
b.fn()
箭头函数会继承作用域链中的上一层this指向,这里指向window对象。
const obj = {
a: function () {
console.log(this)
},
b: {
c: ()=>{
console.log(this);
},
d:function(){
return ()=>{
console.log(this);
}
}
}
}
obj.b.c() //window
obj.b.d()() //b
- 执行obj.b.c(),因为
c
是箭头函数写法,所以this指向上层作用域链,作用域包括函数(局部)作用域和全局作用域。因为上层没有函数作用域,所以指向全局作用域window - 执行obj.b.d()(),相当于执行
d
返回的是箭头函数,所以里面的this指向上层作用域链,也就是d
。因为d
是es5写法,依据谁调用指向谁
,此时d
中的this
指向b。所以输出为b。
总的来说,this就是函数运行时所在的环境对象(重点理解这句话)。
参考文章:
JavaScript 的 this 原理 - 阮一峰的网络日志