1、对象中的方法中的this,指向调用它的对象,即 b ,所以 this.a 的输出结果是b对象中的a的值; 如果b对象中没有a,则this.a的输出结果是 undefined。
var o = {
a: 10,
b: {
a: 12,
fn: function(){
console.log(this.a); // 输出结果是 12
console.log(this); // 输出结果是 b 对象
}
}
}
//调用
o.b.fn();
var o = {
a: 10,
b: {
fn: function(){
console.log(this.a); // undefined
console.log(this); // b对象
}
}
}
//调用
o.b.fn();
2、改变调用方法,不直接调用:改用赋值后调用,此时this的指向为window,所以this.a的输出结果为 undefined,因为全局中没有全局变量a。
var o = {
a: 10,
b: {
a: 12,
fn: function(){
console.log(this.a); //undefined 若在对象o外定义a,则输出的就是其在外定义的值(全局变量)
console.log(this); // window
}
}
}
var j = o.b.fn; //只是将b对象下的方法赋值给j,并没有调用
j(); //调用,此时绑定的对象是window,并非b对象直接调用
3、在对象方法中调用时:
var point = {
x : 0,
y : 0,
moveTo : function(x, y) {
this.x = this.x + x;
this.y = this.y + y;
console.log(this.x); // 1
console.log(this.y); // 1
}
};
point.moveTo(1, 1)//this 绑定到当前对象,即 point 对象
4、作为函数调用时
function someFun(x) {
this.x = x;
}
someFun(5); //函数被调用时,this绑定的是全局对象 window,相当于直接声明了一个全局变量x,并赋值为5
console.log(x); // x 已经成为一个值为 5 的全局隐式变量
更复杂一点的情况:如下所示,point对象中的x、y没有被改变,并结果中多了两个新的全局变量x,y,值都为1。根本原因是point对象下的moveTo方法中的moveX与moveX方法在调用时都是全局调用,绑定的对象都是window。
var point = {
x : 0,
y : 0,
moveTo : function(x, y) {
// 内部函数
var moveX = function(x) {
this.x = x;
};
// 内部函数
var moveY = function(y) {
this.y = y;
};
moveX(x); // 这里是全局调用
moveY(y);
};
};
point.moveTo(1, 1);
console.log(point.x); // 0
console.log(point.y); // 0
可以通过下面情况输出结果说明:
var point = {
x : 0,
y : 0,
moveTo : function(x, y) {
this.x = x;
console.log(this.x); // 1
console.log(this); // point对象
// 内部函数
var moveX = function(x) {
this.x = x;
};
// 内部函数
var moveY = function(y) {
this.y = y;
}
moveX(x); // 这里是全局调用
moveY(y);
}
};
point.moveTo(1, 1);
console.log(point.x); // 1
console.log(point.y); // 0
console.log(x); // 1
console.log(y);// 1
像如上对象中函数方法所示,本意是改变point对象中的x、y的值,而非新建全局变量。所以可以通过以下方法避免上述情况:
var point = {
x : 0,
y : 0,
moveTo : function(x, y) {
var that = this; //内部变量替换
// 内部函数
var moveX = function(x) {
that.x = x;
// this.x = x;
};
// 内部函数
var moveY = function(y) {
that.y = y;
// this.y = y;
}
moveX(x); //这里依然是全局调用,但是在给变量赋值时,不再是this指向,而是that指向,而that指向的对象是 point。
moveY(y);
}
};
point.moveTo(1, 1);
console.log(point.x); // 1
console.log(point.y); // 1
console.log(x) // 报错 x is not defined
console.log(y) //
另附将moveX、moveY由内部函数改为非内部函数后的一种情况示例:这种情况下moveX、moveY方法的调用时绑定在moveTo对象上的,因为moveTo对象一开始是没有x、y变量的,所以执行 this.x = x、this.y = y之后,相当于在moveTo对象中新建了两个变量。
var point = {
x : 0,
y : 0,
moveTo : {
// 内部函数
moveX: function(x) {
console.log(this) // {moveX: ƒ, moveY: ƒ}
this.x = x;
},
// 内部函数
moveY: function(y) {
this.y = y;
}
}
};
point.moveTo.moveX(1);
point.moveTo.moveY(1);
console.log(point.moveTo); // {moveX: ƒ, moveY: ƒ, x: 1, y: 1}
console.log(point.x); // 0
console.log(point.y); // 0
console.log(x) // x is not defined
console.log(y) //
5、作为构造函数调用:
所谓构造函数,就是通过这个函数生成一个新对象(object)。当一个函数作为构造器使用时(通过 new 关键字), 它的 this 值绑定到新创建的那个对象。如果没使用 new 关键字, 那么他就只是一个普通的函数, this 将指向 window 对象。
这又是另一个经典话题:new 的过程
var a = new Foo("zhang","jake");
new Foo{
var obj = {};
obj.__proto__ = Foo.prototype;
var result = Foo.call(obj,"zhang","jake");
return typeof result === 'obj'? result : obj;
}
过程如下:
1)创建新对象 obj;
2)给新对象的内部属性赋值,构造原型链(将新对象的隐式原型指向其构造函数的显示原型);
3)执行函数 Foo,执行过程中内部 this 指向新创建的对象 obj(这里使用了call改变this指向);
4)如果 Foo 内部显式返回对象类型数据,则返回该数据,执行结束;否则返回新创建的对象 obj。
var name = "Jake";
function testThis(){
this.name = 'jakezhang';
this.sayName = function () {
return this.name;
}
}
console.log(this.name ); // Jake
new testThis();
console.log(this.name ); // Jake
var result = new testThis();
console.log(result.name ); // jakezhang
console.log(result.sayName()); // jakezhang
testThis(); //此时函数内部指向的是window,修改了name属性
console.log(this.name ); // jakezhang
每次new都相当于会新创建一个对象,然后这个this指向这个新创建的对象,而var result = new testThis();其实就相当于将新创建的这个对象赋值给result,this当然就指向它了。
function Point(x, y){
console.log(this);
this.x = x;
this.y = y;
this.moveTo = function(x,y){
this.x = x;
this.y = y;
console.log(this.x);//1 10
console.log(this.y);//1 10
}
}
var p1 = new Point(0,0); //注意这种形式方法的调用及apply、call的使用
console.log(p1.x)//0
console.log(p1.y)//0
var p2 = {
x:0,
y:0
}
p1.moveTo(1,1);
p1.moveTo.apply(p2,[10,10]);
console.log(x);// x is not defined
console.log(y);//
6、this在不同场景中的指向
1.匿名函数中的this指向全局对象
var a = 10;
var foo = {
a: 20,
fn: (function(){
console.log(this); // window
console.log(this.a); // 10
})()
}
2.setInterval和setTimeout定时器中的this指向全局对象
var a = 10;
var oTimer1 = setInterval(function(){
var a = 20;
console.log(this.a); // 10
clearInterval(oTimer1);
},100);
3.eval中的this指向调用上下文中的this
(function(){
eval("console.log(this)"); // Window
})();
function Foo(){
this.bar = function(){
eval("console.log(this)"); // Foo
}
}
var foo = new Foo();
foo.bar();
4.apply和call中的this指向参数中的对象
var a = 10;
var foo = {
a: 20,
fn: function(){
console.log(this.a);
}
};
var bar ={
a: 30
}
foo.fn.apply();//10(若参数为空,默认指向全局对象)
foo.fn.apply(foo);//20
foo.fn.apply(bar);//30
常遇见的this问题
第一题:
let a = 10
const b = 20
function foo () {
console.log(this.a)
console.log(this.b)
}
foo();
console.log(window.a)
使用let 或者 const,变量是不会被绑定到window上的,所以此时会打印出三个undefined
第二题:
var name = "windowsName";
function sayName() {
var name = "Jake";
console.log(this.name); // windowsName
console.log(this); // Window
}
sayName();
console.log(this) // Window
最后调用 sayName的地方 sayName();,前面没有调用的对象那么就是全局对象 window,这就相当于是window.sayName()。
第三题:
function foo() {
console.log( this.age );
}
var obj1 = {
age : 23,
foo: foo
};
var obj2 = {
age : 18,
obj1: obj1
};
obj2.obj1.foo(); // 23
这个没什么好讲的,就是谁调用就指向谁,obj1调用的foo,所以指向obj1.
第四题:
function foo () {
console.log(this.a)
};
var obj = { a: 1, foo };
var a = 2;
var foo2 = obj.foo;
obj.foo();//1
foo2();//2
使用另一个变量来给函数取别名会发生隐式丢失。
虽然foo2指向的是obj.foo函数,不过调用foo2的却是window对象,这样写,相当于直接把函数foo赋值给foo2,所以它里面this的指向是为window。
第五题:
function foo () {
console.log(this.a)
};
var obj = { a: 1, foo };
var a = 2;
var foo2 = obj.foo;
var obj2 = { a: 3, foo2: obj.foo }
obj.foo();
foo2();
obj2.foo2();
答案是1,2,3
obj.foo()中的this指向调用者obj
foo2()发生了隐式丢失,调用者是window,使得foo()中的this指向window
foo3()发生了隐式丢失,调用者是obj2,使得foo()中的this指向obj2.
第六题:
function foo () {
console.log(this.a)
}
function doFoo (fn) {
console.log(this)
fn()
}
var obj = { a: 1, foo }
var a = 2
doFoo(obj.foo)
这里把obj.foo当作参数而此时没有调用(只有在调用时,this才会确定),实际上就是把foo当成参数(注意:把obj.foo当成参数其实就是函数foo的地址,相当于直接把foo函数当成参数),在函数doFoo中调用fn(),就是在调用foo(),而此时,请注意,这个时候有xxx.foo()吗?没有,所以其实是没有其他的对象调用它的,真正调用的它的是window.所以此时的this指向window。
第七题:
let name = "zjk";
let o = {
name : "Jake",
sayName: function () {
console.log(this.name)
},
func: function () {
setTimeout( () => {
this.sayName()
},100);
}
};
o.func() // Jake
this指向的是func环境,而func环境中this是o,因为o调用了这个函数。
es5中的this要看函数在什么地方调用(即要看运行时),通过谁是最后调用它该函数的对象来判断this指向。但es6的箭头函数中没有 this 绑定,必须通过查找作用域链来决定其值,如果箭头函数被非箭头函数包含,则 this 绑定的是最近一层非箭头函数的 this,否则,this 为 undefined。箭头函数的 this 始终指向函数定义时的 this,而非执行时。
第八题:
var name = 'window'
var obj1 = {
name: 'obj1',
foo1: function () {
console.log(this.name)
return () => {
console.log(this.name)
}
},
foo2: () => {
console.log(this.name)
return function () {
console.log(this.name)
}
}
}
var obj2 = {
name: 'obj2'
}
obj1.foo1.call(obj2)()//obj2 obj2
obj1.foo1().call(obj2)//obj1 obj1
obj1.foo2.call(obj2)()//window window
obj1.foo2().call(obj2)//window obj2
obj1.foo1.call(obj2)()执行之后第一层函数的this就已经改变了,第二层的箭头函数的this其实就是外面一层的this所以和第一层的一样。
obj1.foo1().call(obj2)其实是先执行了obj1.foo1()这个函数,然后再执行call这个方法,这个时候obj1.foo1()早就执行完了所以先打印obj1,之后的call执行obj1.foo1()所返回的函数,但是这是箭头函数,call无法改变,所以还是obj1
obj1.foo2.call(obj2)(),obj1.foo2.call(obj2)相当于直接执行obj1.foo2(),call想改变this指向,但由于是箭头函数,所以无效,然后之后有一个()所以执行函数的第二层,但由于是这个时候其实是window在执行,所以this指向window。
obj1.foo2().call(obj2),先执行obj1.foo2(),由于是箭头函数,所以this和外层的this指向一致,外层是window.obj1,所以外层的this指向window,所以这里输出window,之后执行call(obj2)将obj1.foo2()返回的函数this指向改为了obj2,所以输出obj2