this 总是指向执行时的当前对象。JavaScript 的 this 总是指向一个对象,而具体指向哪个对象是在运行时基于函数的执行环境动态绑定的,而非函数被声明时的环境。也就是说 this 的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。
除了使用 with 和 eval 的情况,常用的可分为以下几种:
1、作为对象的方法调用
对象的方法被调用时,this 指向该对象。
var obj = {
a: 1,
getA() {
alert(this === obj); // 输出:true
alert(this.a); // 输出: 1
}
};
obj.getA();
2、作为普通函数调用
当函数不作为对象的属性被调用时,也就是我们常说的普通函数方式,此时的 this 总是指向全局对象。
这里注意对象的方法被单独拷贝出来后执行,那么原来的this会丢失,变成指向全局对象。
//在浏览器的JavaScript 里,这个全局对象是window 对象。
window.name = 'globalName';
var getName = function(){
return this.name;
};
console.log( getName() ); // 输出:globalName
//或者:
window.name = 'globalName';
var myObject = {
name: 'sven',
getName: function(){
return this.name;
}
};
var getName = myObject.getName;
console.log( getName() ); // globalName
3、构造器调用
通常情况下,构造器里的 this 就指向返回的这个对象;
//构造器里的this 就指向返回的这个对象,见如下代码:
var MyClass = function(){
this.name = 'sven';
};
var obj = new MyClass();
alert ( obj.name ); // 输出:sven
var MyClass = function(){
this.name = 'sven';
return { // 显式地返回一个对象
name: 'anne'
}
};
var obj = new MyClass();
alert ( obj.name ); // 输出:anne
var MyClass = function(){
this.name = 'sven'
return 'anne'; // 返回string 类型
};
var obj = new MyClass();
alert ( obj.name ); // 输出:sven
4、call 或 apply 调用
apply和call可以动态地改变传入函数的 this
var obj1 = {
name: 'sven',
getName: function(){
return this.name;
}
};
var obj2 = {
name: 'anne'
};
console.log( obj1.getName() ); // 输出: sven
console.log( obj1.getName.call( obj2 ) ); // 输出:anne
除此之外,this在箭头函数中的指向与上述不同
箭头函数的this取值,规则非常简单,因为this在箭头函数中,可以看做一个普通变量。箭头函数没有自己的this值,箭头函数中所使用的this都是来自函数作用域链,它的取值遵循普通变量一样的规则,在函数作用域链中一层一层往上找。
有了箭头函数,我们只要遵守下面的规则,this的问题基本上就可以迎刃而解了
那么,为什么要使用箭头函数呢?
当我们需要在对象方法中嵌套一个内层函数时,this就会给我们带来实际的困扰了比如:
// 使用临时变量self
var circle = {
radius: 10,
outerDiameter() {
var self = this;
var innerDiameter = function () {
console.log(2 * self.radius);
};
innerDiameter();
}
};
circle.outerDiameter(); // 打印20
outerDiameter函数是circle对象的方法,因此其this值就是circle对象。
如果能直接写this.radius多好啊,可惜不能这么写,因为内层函数innerDiameter并不会继承外层函数outerDiameter的this值。outerDiameter函数的this值就是circle对象,this.radius等于10。
解释:innerDiameter函数的this值不是circle对象,而是window,和变量不同,关键字this没有作用域的限制,嵌套的函数不会从调用它的函数中继承this。如果嵌套函数作为方法调用,其this的值指向调用它的对象。如果嵌套函数作为函数调用,其this值不是全局对象(非严格模式下)就是undefined(严格模式下)。很多人误以为调用嵌套函数时this会指向调用外层函数的上下文。如果你想访问这个外部函数的this值,需要将this的值保存在一个变量里,这个变量和内部函数都同在一个作用域内。通常使用变量self来保存this。验证一下:
// innerDiameter函数中的this是window
var circle = {
radius: 10,
outerDiameter() {
var innerDiameter = function () {
console.log(this === window);
};
innerDiameter();
}
};
circle.outerDiameter(); // 打印true
因此,如果直接在innerDiameter函数中使用this的话,并不能得到我们想要的结果:
// 使用普通函数
var circle = {
radius: 10,
outerDiameter() {
var innerDiameter = function () {
console.log(2 * this.radius);
};
innerDiameter();
}
};
circle.outerDiameter(); // 打印NaN
于是,我们不得不使用一个临时变量self将外层函数outerDiameter的this值搬运到内层函数innerDiameter。
.bind(this)
我们也可以使用.bind(this)来规避this变来变去的问题:
// 使用.bind(this)
var circle = {
radius: 10,
outerDiameter() {
var innerDiameter = function () {
console.log(2 * this.radius);
};
innerDiameter = innerDiameter.bind(this);
innerDiameter();
}
};
circle.outerDiameter(); // 打印20
但是,无论是使用临时变量self,还是使用.bind(this),都不是什么很简洁的方式。
总之,普通函数的this取值多少有点奇怪,尤其当我们采用面向对象的方式编程时,很多时候都需要用到this,大多数时候我们都不会去使用.bind(this),而是使用临时变量self或者that来搬运this的取值,一不小心就会蒙圈。
这时候箭头函数闪亮登场!!!✨
// 使用箭头函数
var circle = {
radius: 10,
outerDiameter() {
var innerDiameter = () => {
console.log(2 * this.radius);
};
innerDiameter();
}
};
circle.outerDiameter(); // 打印20
对于内层函数innerDiameter,它本身并没有this值,其使用的this来自作用域链,来自更高层函数的作用域。innerDiameter的外层函数outerDiameter是普通函数,它是有this值的,它的this值就是circle对象。因此,innerDiameter函数中所使用的this来自outerDiameter函数,其值为circle对象。
理论小结,反复查看,后面读代码的时候方便回顾
欢迎喜欢前端的各位一起交流讨论❤️