在js对象中,this在函数外部的上下文环境中,指向全局对象。this在函数内部时,指向取决于函数最终运行的方式。
函数外部
this在函数外部的上下文环境中,指向全局对象。
1. 全局环境
在全局运行的上下文中(在任何函数体外部),this 指代全局对象,无论是否在严格模式下。
var name = '小王';
console.log(this.name === window.name); // true
2. 函数外部的上下文环境
this在函数外部的上下文环境中,指向全局window。
var age = 17;
var obj = {
objage: this.age//this => window
}
obj.objAge; //17
尽管在另一个对象字面量里面,还是指向全局。
var age = 17;
var obj = {
age: 19,
objn:{
objage: this.age//this => window
}
}
obj.objn.objAge; //17
函数内部
this在函数内部时,指向取决于函数最终运行的方式。
1.直接调用
函数在 window环境下被调用,此时函数内的 this 指向 window。
function foo(){
console.log(this);//this => window
}
foo();//window
2.对象中的函数
1. 对象调用函数
对象中的函数被对象 obj 调用,此时函数内部的 this 指向对象 obj ,this.name = obj.name,而 this.age 不存在,输出undefined。
var name = '小王';
var age = 17;
var obj = {
name: '小张',
objage: this.age,//this => window
foo: function(){
console.log(this.name + "年龄" + this.age);//this => obj
}
}
obj.objAge; //17
obj.foo(); //小张年龄undefined
2. 对象调用就近原则
obj.objn.foo(); 可以看成是 foo() 前最靠近它的对象也就是 objn 调用它,所以函数内部 this 指向 objn。
var name = '小王';
var age = 17;
var obj = {
name: '小张',
age: 19,
objn:{
name: '小李',
foo: function(){
console.log(this.name + "年龄" + this.age);//this => objn
}
}
}
obj.objn.foo(); //小李年龄undefined
3. 对象外部调用函数
其实这样调用对象内部的函数就相当于把 obj.foo 这个函数赋值给了变量 a ,这时 foo 的 this 指向谁就得看 a() 怎么运行了,a() 在window 环境下运行,就相当于直接调用函数的那种情况,this 指向 window 。
var name = '小王';
var age = 17;
var obj = {
name: '小张',
foo: function(){
console.log(this.name + "年龄" + this.age);
}
}
var a = obj.foo;
a(); //小王年龄17 ===> 此时foo 函数内部的 this 指向 window
||
var a = obj.foo; == var a = function(){console.log(this.name + "年龄" + this.age);}
手动指定this
call、apply、 bind 用来手动的指定函数最终执行时的 this 的指向。
call、apply:当一个函数的函数体中使用了 this 关键字时,通过所有函数都从 Function 对象的原型中继承的 call() 方法和 apply() 方法调用时,它的值可以绑定到一个指定的对象上。
bind:调用 fn.bind(someObject) 方法会创建并返回一个与 fn 具有相同函数体和作用域的函数,但是在这个新函数中,this 将永久地被绑定到了 bind 的第一个参数,无论这个函数是如何被调用的。
1. call、apply、 bind对比
除了 bind 方法后面多了个 () 外 ,结果返回都一致。由此得出结论,bind 返回的是一个新的函数,你必须调用它才会被执行。
var name = '小王';
var age = 17;
var obj = {
name: '小张',
objage: this.age,
foo: function(){
console.log(this.name + "年龄" + this.age);
}
}
var objn = {
name: '小李',
age: 22
}
obj.foo.call(objn);    // 小李年龄22
obj.foo.apply(objn);    // 小李年龄22
obj.foo.bind(objn)(); // 小李年龄22
bind 返回的新函数的 this 永远的绑定到了 bind 的第一个参数上,无论这个函数是如何被调用的。
function foo() {
return this.name;
}
var foon = foo.bind({name: '小张'});
foon(); // 小张
var obj = {
name: '小王',
foon: foon
};
obj.foon(); // 小张
尽管在此使用 bind 改变也没用!!(call、apply 也是不能改变的)
function foo() {
return this.name;
}
var foon = foo.bind({name: '小张'});
var foox = foon.bind(obj)
var obj = {
name: '小王',
foox: foox
};
obj.foox(); // 小张
2. 对比call、apply、bind 传参情况下
call 和 bind 的参数都是一个一个传的。而 apply 则是全部放在一个数组里面传过去。
var name = '小王';
var age = 17;
var obj = {
name: '小张',
objage: this.age,
foo: function(a, b){
console.log(this.name + "年龄" + this.age , "来自" + a + "去往" + b);
}
}
var objn = {
name: '小李',
age: 22
}
obj.foo.call(objn, "北京", "上海");    // 小李年龄22来自北京去往上海
obj.foo.apply(objn, ["北京", "上海"]);   // 小李年龄22来自北京去往上海
obj.foo.bind(objn, "北京", "上海")(); // 小李年龄22来自北京去往上海
obj.foo.bind(objn, ["北京", "上海"])(); // 小李年龄22来自北京,上海去往undefined
其他
下面这个代码会报错,因为函数在定时器中运行,就相当于普通函数,这是 this 指向 window 。
var obj = {
name: '小张',
foo1: function(){
console.log(this.name);
},
foo2: function(){
setTimeout(function() {
console.log(this);//window
this.foo1();//this => window
},1000);
}
}
foo2 作为对象的方法,被对象调用时 this 指向对象,使用 apply(this)\call(this) 也就是手动的指向对象 obj 。解决定时器丢失 this 问题还可以在定时器和 foo2 之间定义一个 var that = this 保存一下 this 的引用,这里其实是闭包, that 不会被注销掉,会一直保存。除此之外,还可以在定时器中使用箭头函数,箭头函数会丢失 this ,所以箭头函数的 this 指向由外层决定,也就是指向外层。
var obj = {
name: '小张',
foo1: function(){
console.log(this.name);
},
foo2: function(){
console.log(this);//obj
setTimeout(function() {
console.log(this);//obj
this.foo1();//this => obj
}.apply(this),1000);
}
}
总结
this 在函数外部(全局环境)中,指向 window 。
this 在函数内部环境中,不是由定义函数决定的,而是取决于最终函数被运行的方式。
call、apply、 bind 可以手动指定 this 。bind 返回新函数,this 将永久地被绑定到了 bind 的第一个参数,无论这个函数是如何被调用的。而 call 和 bind 的参数直接传,apply 需要打包成一个数组。
定时器内部运行函数相当于运行普通函数,this 指向 window,解决方案可使用 call、apply 或闭包或者箭头函数。