理一理js中让人抓狂的this

其实一直都有用到this,但是平时也没有怎么去思考,所以有时候根本就不知道怎么去使用,有时候甚至是直接避开使用this,今天研究了一天,也看了不少的文章,做个简单的总结吧。

其实this,就是一个我们联系上下文的简称,就像在英文环境下我们会说:" mike is a good guy and he is reat" ,he其实就是指代我们上文提到的mike, 所以this的指向是要结合上下文来理解的,不同的执行环境,this的指向也随之不同,那怎么去判断执行环境呢?、

其实可以总结出的一点就是,函数是在哪里被运行的,this就指向该环境;

看看后面的例子就可以慢慢体会;

 var person = {
    firstName: "Penelope",
    lastName: "Barrymore",
    fullName: function () {
        ​// 这里就用this指代person
        console.log(this.firstName + " " + this.lastName);
        ​// 当然我们也可以这么写:
        console.log(person.firstName + " " + person.lastName);
    }
}

我把几种常见的情况列出来吧~~

  • 1 全局环境中的this。
  • 2 在事件绑定中的this。
  • 3 作为构造函数的调用
  • 4 如何灵活修改this指向
    • 使用call bind apply
    • 创建中间变量,将this临时存放给中间变量

在非严格模式下,全局中的this是指向window的;

先看下面这段代码:在console中直接测试

var x = "1";
console.log(this.x);  //1
console.log(this)    //window

再来看一下这个例子:

var firstname= "lili";
function showname(){
  console.log(this.firstname)
}
showname()   //输出的就是lili,此时是在全局中执行这个函数的,所以this指向的就是window
  • 2 在事件绑定中的this。

js中不可避免的就是需要对dom元素进行事件的绑定,那么绑定事件中的this就是指向被绑定对象本身;

先看个例子吧;

//这种方法还是一个道理,this还是指向被绑定的按钮本身
 Obtn1.onclick = show;
 function show() {
    console.log(this);   //这里的this就是指向这个Obtn1这个按钮
 }

// 在按钮的点击事件发生的时候,就会激发相应的事件发生,所以this还是指向Obtn1
  Obtn1.onclick = function () {
   console.log(this);
 }

//再来看看这种方法:这一次有点不同
//目标绑定的点击事件,函数内部输出的this还是指向Obtn1本身,但是在这里再去调用函数的话,show函数中的this就不再指向Obtn了,而是指向window,因为这时候调用函数就是在全局中调用了。
 Obtn1.onclick = function () {
      console.log(this)    //Obtn1
      show()                 //输出的是window
 }
 function show() {
    console.log(this)
 } 
  • 3 对象定义中的this指向

一般在对象定义中的this是指向对象本身;

先看个栗子。

 var person = {
    firstName   :"Penelope",
    lastName    :"Barrymore",
    // tshowFullname()是在person内定义的,而且执行的时候也是在person调用下才执行,所以这个时候的this是指向person的
    // "this" will have the value of the person object because the 
  //person object will invoke showFullName ()​
    showFullName:function () {
    console.log (this.firstName + " " + this.lastName);
    }
    }
    person.showFullName (); // Penelope Barrymore

在此处 ,我们已经提到了两种情况,那 我们这个时候不妨再看看一个例子,看看是否已经掌握了。

 var firstName = "Peter",
    lastName = "Ally";
​
    function showFullName () {
    // 这里面的"this"是指向全局的,因为这个函数是在全局中被定义,就像 firstName 和 lastName​也是在全局中被定义
    console.log (this.firstName + " " + this.lastName);
    }

    var person = {
    firstName   :"Penelope",
    lastName    :"Barrymore",
    showFullName:function () {
    // "this" 在这里其实是指向对象本身的,因为被调用的时候也是基于该对象的。
    console.log (this.firstName + " " + this.lastName);
    }
    }

  //我们来看看下面输出的结果吧
 //这种方法就是相当于在全局中调用了该方法,所以指向的是全局。
    showFullName (); // Peter Ally​
    window.showFullName (); // Peter Ally​
 // "this" 在 the showFullName () 方法里是指向该对象本身的,所以
 输出的是以下结果
   person.showFullName (); // Penelope Barrymore
  • 4 作为构造函数的调用

当一个函数被用来new一个对象的时候,这个函数就被称为构造函数,那么此时构造函数内的this就会指向该实例化的对象本身;

var x = 4;     //这是全局的变量
function test(){
  this.x = 1;
}
var o = new test();
alert(o.x)      //输出1

//我们再来做一点点操作;
o.x  = 3;
alert(o.x)     //输出的是3,所以构造函数中的this就是指向对象本身

其实this的指向主要还是要根据上下文,也就是我们所说的context。
当然我们也可以通过使用call bind apply方法来修改this的指向,因为this其实就是一个通用的一个缩略词,那么当我们在一个对象中使用或者在一个构造函数中使用了this,此时this都是指向该对象或者通过该构造函数实例化的对象;

那么,我们如何借用它们的方法呢?

其实构造函数的继承也是使用到该方法;就是我们可以用call bind apply来修改this的指向;

我们可以来看看这个例子:

function Fruits() {
    this.color="yellow",
    this.say=function(){
    console.log("My color is " + this.color);
 }
 }
  function apple(){
      this.color="red"
 }
var banana = new Fruits();
var apple1 = new apple();
banana.say();    //My color is yellow
//这里相当于将banana.say方法的this指向指到了apple实例化的对象本身,所以输出的就是下面的结果。
banana.say.call(apple1)   //My color is red

再来思考一下这个问题。前面我们提到,事件绑定的时候,this是指向该绑定对象本身的,那么有时候我们是直接将一个函数直接传给事件绑定对象,但是这时候this可能就会出错了,怎么解决呢?

还是一样,我们来看个例子吧;

 var user = {
   name:"lulei",
   age:10,
   say:function () {
    console.log("I am " + this.name,"I am  " + this.age +" years old")
 }
 }
var Obtn = document.querySelector(".btn");
Obtn.onclick = user.say;  //此时的this指向的就是Obtnl ,其实我们这么去写的话,是会直接输出undefined的;I am  I am  undefinedyears old

那么,怎么解决呢?

我们可以这样 :
把下面这个修改一下:

Obtn.onclick = user.say; 

这样修改之后,this就会指向该user了;

Obtn.onclick = user.say.call(user);  // I am lulei I am  10years old

除了这种方法之后,我们还可以通过创建中间变量的方法来修改this的指向;

这里我借用的是一个外文网站的例子 :

  var user = {
    tournament:"The Masters",
    data      :[
    {name:"T. Woods", age:37},
    {name:"P. Mickelson", age:43}
    ],
​
    clickHandler:function (event) {
    // 为了得到这个this对象,我们可以先将这个this对象赋值给一个中间变量
    // 这里的this还是指向的是user这个对象;
    var theUserObj = this;
    this.data.forEach (function (person) {
    // 在这个匿名函数内,this不再是user,而是指向了一个全局的变量了,也就是window,所以这个时候我们定义的中间变量theUserObj就可以拿来用了
    console.log (person.name + " is playing at " + theUserObj.tournament);
    })
    }
    }​
    user.clickHandler();
    // T. Woods is playing at The Masters​
    //  P. Mickelson is playing at The Masters

解决方法就是:

建立一个中间变量:
var theUserObj = this;

还有一种情况,就是我们将方法赋值给一个变量之后,该变量再去执行该方法之后,此时的this指向也发生了变化;

还是先看个例子吧;

// 这个是全局变量
    var data = [
    {name:"Samantha", age:12},
    {name:"Alexis", age:14}
    ];
​
    var user = {
    // 这是user对象上的数据属性
    data    :[
    {name:"T. Woods", age:37},
    {name:"P. Mickelson", age:43}
    ],
    showData:function (event) {
    var randomNum = ((Math.random () * 2 | 0) + 1) - 1; // random number between 0 and 1​
​
    // This line is adding a random person from the data array to the text field​
    console.log (this.data[randomNum].name + " " + this.data[randomNum].age);
    }
​    }

    // 将 user.showData赋值给一个变量
    var showUserData = user.showData;
    // 当我们执行showUserData的时候,输出的是全局变量中的data的值,而不是user内的值,也就是this的指向发生了改变,因为这个时候函数的执行已经是在全局中执行的了
    showUserData ();  // Samantha 12 (from the global data array)​

解决方法如下

    // 将数据绑定在user上
    var showUserData = user.showData.bind (user);
​
    // 现在我们得到的就是user的数据了,因为this已经被绑定在user上了
    showUserData ();  // P. Mickelson 43

总结:其实this的指向并不是在定义的时候确定的,而是在真正执行的时候决定的;有时候我们会用到匿名函数或者是回调函数,或者是会将该对象或者构造函数的方法借给其他对象使用,this的指向都会发生相对应的变化,但是我们都不用太着急,因为可以使用apply call bind 的方法去修正和更改this的指向;

PS:
这是我参考的学习链接:
1 国外的一篇,讲得很好,大部分从里面学习到:
http://javascriptissexy.com/understand-javascripts-this-with-clarity-and-master-it/
2 阮一峰的文章:
http://www.ruanyifeng.com/blog/2010/04/using_this_keyword_in_javascript.html
3 还有另外一篇国外的文章:
http://bjorn.tipling.com/all-this

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容