js的this详解(后面有题目和我自己的想法,方便理解)

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

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值