【原】理解javascript中的this

  最近的文章基本都是总结javascript基础内容的,因为我觉得这些东西很重要。而且很多时候你觉得你理解了,其实并没有你自认为的那么理解。十月份没怎么写文章,因为国庆出去玩的比较久,心变野了,现在是时候重新回到学习的轨迹了,今天要总结的是javascript中的this.

 

介绍this之前,先来两句话,这两句话比较重要,可以说贯穿全文。

1、this的最终指向的是那个调用它的对象

2、如果一个函数中包含多个对象,尽管这个函数是被最外层的对象所调用,this指向的也只是它上一级的对象

 

下面通过一些代码示例来讲解this

1、window中的this

 

demo1

function test1(){
    this.x = 1;
    console.log(this.x);
  }
test1();  // 1

function test2(){
    var y = 2;
    console.log(this.y); //undefined
    console.log(this);  //Window
}
test2();

 上面的两个例子中,都是属于属于全局性调用,因此this就代表全局对象。也就是为什么test2打印出来的this.y会是undefined,因为this指向了 window.

 

2、在对象中使用this 

 

demo2

var x=0;
var obj={
    x:1,
    fn:function(){
        console.log(this.x);
    }
}
obj.fn();  // 1

这里的this指向对象obj,因为调用的fn是通过obj.fn()执行的。

不过这里有个注意点,就是this的指向在函数创建的时候是决定不了的,在调用的时候才能决定,谁调用的就指向谁,这个是比较重要的一点。为了验证这句话,稍微改造一下上面的demo2

 

demo3

var x=0;
var obj={
    x:1,
    fn:function(){
        console.log(this.x);
    }
}
obj.fn();  // 1  跟上面的代码一样

// 修改了这里
var obj2=obj.fn;
obj2();  //0

demo3中和demo2的不同之处在于,没有直接调用obj里面的fn函数,而是将其赋值给obj2,最终通过obj2来调用fn函数。

而obj2属于全局变量,因为fn里面的this指向了 window。这个也验证了上面那句话: this的指向在函数创建的时候是决定不了的,在调用的时候才能决定,谁调用的就指向谁

 

demo4

var obj = {
    x:1,
    y:{
        fn:function(){
            console.log(this.x); //undefined
        }
    }
}
obj.y.fn();

如果一个对象中又包含有其他对象,this仅仅会指向它的上一级对象。
就像demo4中,fn里面调用this.x,这个this只会指向fn的上一级对象y。y中没有x这个变量,所以返回的是undefined。

 

稍微改造一下demo4,再次来验证一下:this的指向在函数创建的时候是决定不了的,在调用的时候才能决定,谁调用的就指向谁

demo5

var obj = {
    x:1,
    y:{
        x:2,
        fn:function(){
            console.log(this.x); //undefined
        }
    }
}
var obj2=obj.y.fn;
obj2();  // 因为此时fn中的this指向了window

 

3、在构造函数中使用this

demo6

function Fn(){
    this.x=1;
}
var myFn=new Fn();
console.log(myFn.x); //1

如果函数倾向于和 new 关键词一块使用,则我们称这个函数是构造函数, 在函数内部,this 指向新创建的对象。也就是说,这个this不是指向函数Fn,而是指向它的实例 myFn。

 

4、call和apply,改变this指向(这里顺便把call和apply给介绍了

 

在 javascript 中,call 和 apply 都是为了改变某个函数运行时的上下文(context)而存在的,也就是说,是为了改变函数体内部 this 的指向。

call和apply的区别就在于传递方式的不同,call在接收指定参数的形式是 someMethod.call(obj, arg1, arg2);

而apply在接收指定参数时的形式是 someMethod.apply(obj, [arg1, arg2]).或者someMethod.apply(obj, arg1),但是这个arg1必须是一个类数组对象

 

demo7

var personA = {
    name: 'kobe',
    sayName: function (hobby){
        console.log(this.name + ' likes ' + hobby);
    } 
};

personA.sayName('basketball');   // 'kobe likes basketball'

var personB = {

    name: 'James'
}

personA.sayName.call(personB, 'basketball');  // 'James likes basketball'
personA.sayName.apply(personB, ['basketball']); // 'James likes basketball'

personA.sayName('basketball'); 这段代码中,调用sayName()这个方法的对象是personA,因此sayName()内部的this指向就是personA对象。
personA.sayName.call(personB, 'basketball'); 本来sayName方法的this指向是personA对象,但是调用call/apply后,this对象指向了personB对象。


也可以这么理解,personA.sayName.call(personB, 'basketball')其实就是 personB.sayName(‘basketball’);



demo8
function PersonA(name,hobby) {
   this.name = name,
   this.hobby = hobby,
   this.say = function() {
        console.log(name +" likes " + this.hobby + '.');
   }
}
function PersonB(name,hobby) {
   PersonA.call(this,name,hobby);
}

var Fn=new PersonB('James','basketball');
Fn.say(); // James likes basketball.

虽然我们没有在PersonB对象里添加任何属性和方法,但是我们使用call()继承了原本属于PersonA 的属性和方法。就可以做到 PersonA 函数所有能做到的事情。

这里额外再穿插一下appy的使用一些场景

 

apply应用场景1:数组合并

var list1 = [0,1,2];
var list2 = [3,4,5];
[].push.apply(list1,list2);

//或者 Array.prototype.push.apply(list1, list2); 

console.log(list1);//  [0,1,2,3,4,5]

 

apply应用场景2:获取数组中的最大值或者最小值

var arr = [0,1,2,15,6];
var getMax=Math.max.apply(this,arr);
console.log(getMax); //15

 

5、使用this的注意点

 

这里主要介绍 setTimeout或者setInterval中使用this的注意点

demo9

var name='james';
var person={
  name:'koBe',
  sayName:function(){
     setTimeout(function(){
        console.log(this.name);
     },0);
  }
}
person.sayName();  // james

因为setTimeout()这个异步函数调用的时候,内部的回调函数this的指向是window.刚好window中有一个 name为 james的标量。所以输出james.

 

那如何将setTimeout中的this指向 person呢。两种方式:

方法一:将this保存在一个变量中

demo10

var name='james';
var person={
  name:'koBe',
  sayName:function(){
    var that=this; //将this存储在that中。
     setTimeout(function(){
        console.log(that.name);
     },0);
  }
}
person.sayName();  // koBe

将this保存在that中,这样,setTimeout调用this.name就变成了that.name了。

 

方法二:使用bind

demo11 

var name='james';
var person={
  name:'koBe',
  sayName:function(){
     setTimeout(function(){
        console.log(this.name);
     }.bind(this),0);
  }
}
person.sayName();  // koBe

bind方法,起的作用和call,apply一样,都是改变函数/方法执行时,this的指向,确保这个函数/方法运行时this指向保持一致。

demo11中,通过bind方法将this对象绑定为person。那么回调函数在执行的时候,this指向还是person。

 

有误之处,欢迎指出

posted @ 2016-10-19 09:57  咸鱼老弟  阅读(831)  评论(0编辑  收藏  举报