JS中this指向小结
关于 this 的指向,记住最核心的一句话:哪个对象调用函数,函数里面的this指向哪个对象。
普通函数中调用
这个情况没特殊意外,就是指向全局对象-window
var username = 'cn'
function fn() {
alert(this.username);
}
fu();//cn
let username = 'cn'
function fn() {
alert(this.username);
}
fn();//undefined
window.username = 'cn'
function fn() {
alert(this.username);
}
fn();//cn
对象函数调用
this是一个特殊变量,它始终指向当前对象,
window.b = 2222
let obj = {
a: 111,
fn: function () {
alert(this.a);//111
alert(this.b);//undefined
}
}
obj.fn();
对象函数调用常见问题
1、示例一:
var xiaoming = {
name: '小明',
birth: 1990,
age: function () {
var y = new Date().getFullYear();
return y - this.birth;
}
};
xiaoming.age; // function xiaoming.age()
xiaoming.age(); // 今年调用是25,明年调用就变成26了
将函数分开写
function getAge() {
var y = new Date().getFullYear();
return y - this.birth;
}
var xiaoming = {
name: '小明',
birth: 1990,
age: getAge
};
xiaoming.age(); // 25, 正常结果
getAge(); // NaN,getAge()中,this指向全局对象window
var fn = xiaoming.age; // 先拿到xiaoming的age函数
fn(); // NaN(在严格模式下会报错),要保证this指向正确,必须用obj.xxx()的形式调用!
重构方法:用var that = this;,你就可以放心地在方法内部定义其他函数,而不是把所有语句都堆到一个方法中。
var xiaoming = {
name: '小明',
birth: 1990,
age: function () {
var that = this; // 在方法内部一开始就捕获this
function getAgeFromBirth() {
var y = new Date().getFullYear();
return y - that.birth; // 用that而不是this
}
return getAgeFromBirth();
}
};
xiaoming.age(); // 25
2、示例二:在闭包函数中使用this对象常见问题
匿名函数的执行环境具有全局性,因此其this对象通常指向window
var name="The window";
var odject={
name:"My object",
GetNameFunc:function(){
return function(){
return this.name;
};
}
};
alter(object.GetNameFunc()()); //The window;
object.GetNameFunc(); //My object
(object.GetNameFunc=object.GetNameFunc)(); //The window ,
//赋值表达式是对函数的引用,return a=b;返回的是b,等同于执行了函数;
把外部作用域中的this对象保存到一个闭包能访问到的变量里,就可以让闭包访问该对象了
var name="The window";
var odject={
name:"My object",
GetNameFunc:function(){
return function(){
var that=this;
return this.name;
};
}
};
alter(object.GetNameFunc()()); //My object;
构造函数调用
let TestClass=function(){
this.name='111';
}
let subClass=new TestClass();
subClass.name='cn';
console.log(subClass.name);//cn
let subClass1=new TestClass();
console.log(subClass1.name)//111
箭头函数
ES6 提供了箭头函数,增加了我们的开发效率,但是在箭头函数里面,没有 this ,箭头函数里面的 this 是继承外面的环境。
let obj={
a:222,
fn:function(){
setTimeout(()=>{console.log(this.a)));
}
};
obj.fn();//222
//传给 setTimeout 的是箭头函数,然后箭头函数里面没有 this ,所以要向上层作用域查找,在这个例子上, setTimeout 的上层作用域是 fn。
//而 fn 里面的 this 指向 obj ,所以 setTimeout 里面的箭头函数的 this ,指向 obj 。所以输出 222 。
let obj={
a:222,
fn:function(){
setTimeout(function(){console.log(this.a)}) }};
obj.fn();//undefined
//虽然 fn() 里面的 this 是指向 obj ,
//但是,传给 setTimeout 的是普通函数, this 指向是 window , window 下面没有 a ,所以这里输出 undefined。
call和apply
在一个独立的函数调用中,根据是否是strict模式,this指向undefined或window,不过,我们还是可以控制this的指向的!
要指定函数的this指向哪个对象,可以用函数本身的apply方法,它接收两个参数,第一个参数就是需要绑定的this变量,第二个参数是Array,表示函数本身的参数;对普通函数调用,我们通常把this绑定为null。
用apply修复getAge()调用:
function getAge() {
var y = new Date().getFullYear();
return y - this.birth;
}
var xiaoming = {
name: '小明',
birth: 1990,
age: getAge
};
xiaoming.age(); // 25
getAge.apply(xiaoming, []); // 25, this指向xiaoming, 参数为空
另一个与apply()类似的方法是call(),唯一区别是:
apply()把参数打包成Array再传入;
call()把参数按顺序传入。
Math.max.apply(null, [3, 5, 4]); // 5
Math.max.call(null, 3, 5, 4); // 5
小结
this这个 keyword非常的困惑,但是其实有一个好方法可以理解.
- 检查 ’ . ’ 左边是谁invoke 这个函数. 例如 xiaoming.age(); age函数里面有this, 然后 '. ’ 旁边是xiaoming , 那么this就是指向xiaoming了.
- 如果点旁边没有,那就检查有没有用到 bind, apply, call 这三种, 有的话就是调用此方法的对象.
- 如果上面两个都没有就检查代码里面有没有用到new 这个keyword, 有的话那就是指向new旁边的函数对象.
- 上面三个都没有, 检查是不是有arrow function, 有arrow function的话就是, 那么指向是arrow function的lexical binding 的对象. 就是她的parent.
- 全部都没有如果不是strict mode那就是window对象了… strict就是 error (undefined).