执行环境
JS代码有三种运行环境
- Global Code : JavaScript代码开始运行的默认环境 this=> window
- Function Code: 代码进入一个JavaScript函数 this => 调用函数的对象
- Eval Code : 使用eval()执行代码
案例分析可以,很显然上述代码都是运行在Global Code环境中。而 obj
中的this
是根据代码的执行动态确定的。
当执行到console.log(obj.b1);
时,如果此时b1
对应的值是一个函数的话,那么函数中的this
就是obj
。而b1
的值是this.a
当获取 obj.b1
时,是在全局执行环境下运行的,因此this
是window
对象是没有this的,this是执行上下文里面的一个属性。因此有this的都有:全局执行环境、函数执行环境和eval执行环境。this的值可以是一个对象
注意:以下代码全基于浏览器非严格模式运行
咱先定义一个全局变量a,后面所有的示例都假定此全局变量已存在:
var a = 0;
案例一 :
// demo1
var obj = {
a: 1,
b1: this.a,
b2: () => this.a,
};
console.log(obj.b1);
console.log(obj.b2());
【运行结果】
0
0
案例二
// demo2
(function fn(){
var a = 2;
console.log(this.a);
const subf = ()=>this.a;
console.log(subf());
})()
立即执行函数在全局环境下执行,函数中的this
是window
【运行结果】
0
0
案例三
// demo3
var obj2 = {
a: 3,
b1: function(){ return this.a;}, // b1: 0x99
b2: function(){ return ()=>this.a}, // b2: 0x88
};
console.log(obj2.b1());
console.log(obj2.b2()());
obj2对象的b1对应的是一个函数,函数的this就是调用此函数的对象即obj2
【运行结果】
3
3
这个示例中箭头函数外的this是哪个?我们在箭头函数外面加个this.a看看:
var obj2 = {
a: 3,
b1: function(){ return this.a;}, // b1:0x99
b2: function(){ return ()=>this.a}, // b2: 0x88 b2()--> 0x77
};
console.log(obj2.b1());
console.log(obj2.b2()()); // 0x77()
箭头函数没有自己this,因此箭头函数内的this就是箭头函数外的那个this
【运行结果】
3
3
案例四
// demo4
var obj4 = {
a: 4,
b1: function(){ return this.a;},
b2: function(){ return ()=> () => () => this.a},
};
console.log(obj4.b1());
console.log(obj4.b2()()()());
运行结果:
4
4
案例五
// demo5
function f0() {
var a = 5;
setTimeout(function() {
var b1 = this.a;
console.log(b1);
}, 100);
setTimeout(function() {
var b2 = ()=> this.a;
console.log(b2);
}, 200);
}
f0();
setTimeout中的那个function运行于全局环境下,因此里面的this指向window。而箭头函数没有自己的this,所以第二个setTimeout中箭头函数的this也指向window.
【运行结果】
0
0
那么setTimeout的第一个参数使用箭头函数会是个什么情况?看下一个示例
案例六
// demo6
function f1() {
console.log(this.a); // 0
setTimeout(() => {
console.log(this.a); // 0
}, 100);
}
f1();
f1()在全局环境下执行, 里面的this是window即console.log(this.a);
结果为0。
这里箭头函数作为setTimeout的第一个参数,箭头函数外面的那个this是谁?把箭头函数换成this.fn():
function f1() {
console.log(this.a); // 0
setTimeout(this.fn() , 100);
}
我们这里不用了解this.fn()有什么功能,看到它我们就会明白了this.fn()中的this和前面一行中的this.a中的this是同一个this。
【扩展一下】
// 这样执行
f1.call({a: 7}); // 7 7
结果分析:
// demo6
function f1() {
console.log(this.a); // 因为通过call对this的绑定,f1中的this指向 {a: 7}
setTimeout(() => {
console.log(this.a); // 同样这里this指向 {a: 7}
}, 100);
}
同理:
var bindf1 = f1.bind({a: 8});
bindf1(); // 8 8
箭头函数没有自己的this,箭头函数中用this和普通语句中的this没什么区别,所以,你知道非箭头函数下怎么用this,就知道箭头函数下怎么用this。
关于 “箭头函数对this固定化,箭头函数中的this绑定定义时所在的作用域,箭头函数不能通过 call() 或 apply() 方法绑定this” 等描述,都源于箭头函数没有自己的this。
案例七
var a = 1
var obj = {
a: 2,
test: () => {
console.log(this.a)
}
}
obj.test()
【分析】:obj.test()
,是在所在的作用域是全局作用域,因此obj
里面的this
是window
, 而test
是obj
里面的一个箭头函数,箭头函数没有自己的this
,因此这里的this
就是离他最近的即obj
所在的作用域的this
,也就是window
【运行结果:】
1
var a = 1
var obj = {
a: 2,
innerObj: {
a: 3,
test: () => {
console.log(this.a)
}
}
}
obj.innerObj.test()
【要知道的】:
对象是没有
this
的,this
是执行上下文里面的一个属性。因此有this
的都有:全局执行环境、函数执行环境和eval
执行环境。this
的值可以是一个对象
【分析】:
obj.innerObj.test()
是在全局环境下运行的。所以obj
里面的this
指向的是window
。
innerObj
是obj
里面的一个对象,(对象是没有this
的)
innerObj
和obj
使用的this
都是全局执行环境下的this
即window
。所以innerObj
里面的this
是window
。
test
是innerObj
里面的一个箭头函数,箭头函数是没有自己的this
,因此它使用的是innerObj
里面的this
,即window
实际上
obj
、innerObj
、以及test
箭头函数使用的都是全局作用域下的this
【运行结果】:
1
var a = 1
var obj = {
a: 2,
test:function(){
console.log(this.a)
}
}
obj.test()
【分析】:
obj.test运行在全局环境下,obj里面的this是window。 test是obj里面的一个函数,(函数是有自己的this,且是在调用时确定)。这里是通过obj来调用test,因此test里面的this就是obj
【运行结果】:
2
var a = 1
var obj = {
a: 2,
test:function(){
a: 3
console.log(this.a)
var arrFoo = () => {
console.log(this.a)
}
arrFoo()
}
}
obj.test()
【分析】:
obj
里面的this
是window
test
函数里面的this
是obj
,所以第一次打印console.log(this.a)
是全局对象里面的2
arrFoo
是箭头函数,没有自己的this
,它使用的是距离它最近的作用域里面的this
,即test
里面的this
【运行结果】:
2
2
案例八:箭头函数中的this指向
function a() {
function b() {
console.log(this === window)
var c = () => {
console.log(this)
}
return c()
}
return b()
}
a()
【分析】:
a()
里面的this
是window
b()
虽然是在a()
里面调用,但是在调用时,是“纯函数的调用”,前面没有任何引用即b()
。所以b()
里面的this
是window
。而在b()
里面定义的箭头函数c()
。不管他在哪里调用,都没有自己的this
,因此箭头函数中的this
一定就是b()
里面的this
, 即window
.(和箭头函数在哪里调用,如何调用都没有关系)
【运行结果】:
true
Window {parent: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …}
var obj = {
a: 'xxx'
}
function a() {
function b() {
console.log(this === obj)
var c = () => {
console.log(this)
}
return c()
}
b.call(obj)
}
a()
【分析】:
a()
里面的this
是window
b()
里面的this
是obj
c()
里面的this
等于b()
里面的this
即obj
【运行结果:】
true
{a: “xxx”}
案例九:立即执行函数中的this
立即执行函数中的this始终指向window
n = 0 // 全局下的b
var obj = {
n: 'xxx'
}
function a() {
console.log('a()里面的this是 =>', this)
function b() {
console.log('b()里面的this是 =>', this);
(function(){console.log('立即执行函数里面的this是 =>',this)})() // b()里面的一个立即执行函数
}
b.call(obj)
}
a.call(obj)
【分析】:
a()里面的this是 obj
b()里面的this是 obj
b()里面立即执行函数的this是window。(因为立即执行函数是有系统自动调用的)
【执行结果】:
a()里面的this是 =>{n: “xxx”}
b()里面的this是 => {n: “xxx”}
立即执行函数里面的this是 => Window {parent: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …}
var a = 1
var obj = {
a: 2,
test: function () {
setTimeout(function () {
console.log(this.a)
}, 1000)
}
}
obj.test()
【分析:】
test()
里面的this
是obj
setTimeout
里面的函数是有系统调用的,因此this
是window
【运行结果】:
1
var a = 1
var obj = {
a: 2,
test: function () {
setTimeout(() => {
console.log(this.a)
}, 1000)
}
}
obj.test()
【分析】:
test()
里面的this
是obj
setTimeout
里面的箭头函数是没有this
的,即使箭头函数也是由系统自动调用,但是它没有自己的this
,因此箭头函数里面使用到的this
都是距离它最近的作用域中的this
即test()
里面的this
。
【运行结果】:
2