this指向函数运行(调用)时所在的执行环境
即谁调this,this指谁
🌰例1
var obj = {
a: function() {
console.log(this);
}
}
obj.a() //{a: ƒ}
var b = obj.a;
b(); // window
超时调用(setTimeout回调)的代码都是在全局作用域环境中执行的,因此(setTimeout回调)函数中this的值
在非严格模式下指向window对象,在严格模式下是undefined
🌰例2
function foo() {
console.log("id1:", this.id);
console.log("this1:", this);
setTimeout(function() {
console.log("id2:", this.id);
console.log("this2:", this);
}, 0);
}
var id = 21;
foo();
// Chrome
// id1: 21
// this1: window
// id2: 21
// this2: window
foo.call({id: 42});
// Chrome
// id1: 42
// this1: {id: 42}
// id2: 21
// this2: window
箭头函数内部的this是指向词法作用域,由上下文确定。这个指向在定义时就已经确定,而不会在调用时
指向其执行环境的(变量)对象
因为箭头函数内部的this是指向外层代码块的this的,所以我们可以通过改变外层代码块的this的指向从而改变
箭头函数中this的指向
🌰例3
function foo() {
console.log("id1:", this.id);
console.log("this1:", this);
setTimeout(() => {
console.log("id2:", this.id);
console.log("this2:", this);
}, 0);
}
var id = 21;
foo();
// Chrome
// id1: 21
// this1: window
// id2: 21
// this2: window
foo.call({id: 42});
// Chrome
// id1: 42
// this1: {id: 42}
// id2: 42
// this2: {id: 42}
因为箭头函数(setTimeout回调)没有自己的this,导致其内部的this引用了外层代码块的this,即foo函数的this
(要注意:在定义阶段(调用函数前),foo函数的this的值并不确定,但箭头函数的this自定义阶段开始就指向foo函数的this了)
又因为使用call方法改变了foo函数运行(调用)时其函数体内this的指向(重新指向对象{id: 42})从而使箭头
函数中this的指向发生变化,最后输出例3所示结果。
🌰例4
var name = 'window'; // 其实是window.name = 'window'
var A = {
name: 'A',
sayHello: function(){
console.log(this.name)
}
}
A.sayHello();// 输出A
var B = {
name: 'B'
}
A.sayHello.call(B);//输出B
A.sayHello.call();//不传参数指向全局window对象,输出window.name也就是window
sayHello这个方法是定义在A对象中的,当我们使用call方法,把其指向B对象,最后输出了B;可以得出, sayHello的this只跟使用时的对象有关
这里可以理解为sayHello是一个普通函数,他内部的this即指向调用它的对象
改造一下:
var name = 'window';
var A = {
name: 'A',
sayHello: () => {
console.log(this.name)
}
}
A.sayHello(); // window
这里的箭头函数,也就是sayHello,所在的作用域其实是最外层的js环境,因为没有其他函数包裹;然后最外层的 js环境指向的对象是winodw对象,所以这里的this指向的是window对象。
对比例4,这里可以理解为sayHello是一个箭头函数,它没有自己的this,它的this必须是包裹它的函数(执行上下文) 的this(如例10),如果没有就是window
那如何改造成永远绑定A呢?
🌰例5
var name = 'window';
var A = {
name: 'A',
sayHello: function(){
var s = () => console.log(this.name)
return s//返回箭头函数s
}
}
var sayHello = A.sayHello();
sayHello();// 输出A
var B = {
name: 'B';
}
sayHello.call(B); //还是A
sayHello.call(); //还是A
这样就做到了永远指向A对象了,我们再根据“该函数所在的作用域指向的对象”来分析一下:
- 该函数所在的作用域:箭头函数s 所在的作用域是sayHello,因为sayHello是一个函数。
- 作用域指向的对象:A.sayHello指向的对象是A。
最后是使用箭头函数其他几点需要注意的地方:
- 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误( xx is not a constructor)。
- 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
- 不可以使用yield命令,因此箭头函数不能用作 Generator 函数。
下面开始千锤百炼的练习, fighting 💪🏻💪🏻💪🏻~~~
🌰例6
this.a = 20;
var test = {
a: 50,
init: () => {
console.log('out', this)
function go() {
console.log('inner', this)
console.log(this.a);
}
go();
},
};
test.init() // window window 20
🌰例7
this.a2 = 20;
var test2 = {
a2: 50,
init: function(){
console.log('out', this)
function go2() {
console.log('inner', this)
console.log(this.a2);
}
go2();
},
};
test2.init(); // {a2:50,init:f} window 20
🌰例8
this.a3 = 20;
var test3 = {
a3: 50,
init(){
console.log('out', this)
function go3() {
console.log('inner', this)
console.log(this.a3);
}
go3();
},
};
test3.init(); // {a3:50,init:f} window 20
🌰例9
this.a4 = 20;
var test4 = {
a4: 50,
init: () => {
console.log('out', this)
var go4 =() => {
console.log('inner', this)
console.log(this.a4);
}
go4();
},
};
test4.init() // window window 20
🌰例10
this.a5 = 20;
var test5 = {
a5: 50,
init: function(){
console.log('out', this)
var go5 =() => {
console.log('inner', this)
console.log(this.a5);
}
go5(); // {a5:50,init:f} {a5:50,init:f} 50
},
};
test5.init();
🌰例11
this.a6 = 20;
var test6 = {
a6: 50,
init(){
console.log('out', this)
var go6 =() => {
console.log('inner', this)
console.log(this.a6);
}
go6();
},
};
test6.init(); // {a6:50,init:f} {a6:50,init:f} 50
🌰例12
this.a = 20;
function go() {
console.log(this.a);
this.a = 30; //这句把window.a从20变成30
}
go.prototype.a = 40;
var test = {
a: 50,
init: function (fn) {
fn();
return fn;
},
};
console.log("实例", (new go()).a); // 40(还未执行到this.a=30,从原型链上找) 实例30(构造函数上的this.a优先级比原型链高)
test.init(go); // 20 (这里的执行之前window.a=20,执行完后把window.a从20变成30)
var p = test.init(go);// 30 (这里是window.a=30)
p(); //30 (这里是window.a=30)
❀ 参考文章: