zoukankan      html  css  js  c++  java
  • 理解JavaScript中的“this”

    对于javascript的初学者来说,一般对“this”关键字都感到非常迷惑。本文的目的旨在让你全面的了解“this”,理解在每一个情景下如何使用“this”,希望通过本文,可以帮助同学们不在害怕“this”!!

     

    从生活中发现

    其实“this”就是我们平时用的一个代词。打个简单的比喻:

         “小豆豆是一个很幽默的人,非常喜欢看《暴走漫画》”

    但是你也可以这样写:

         “小豆豆是一个很幽默的人,小豆豆非常喜欢看《暴走漫画》”

    但是日常生活中我们会一直用这种方式来描述一个人吗?如果你的回答是Yes,好吧,估计再也没有人愿意跟你做朋友了,我没骗你…(开个玩笑:-) )。   所以,人类就发明了这样一种看似高端、洋气、上档次的代名词,说白了javascript中的“this”也就是这么一个东东。

    一个简单的例子:

    person = {
     firstName: "zhou",
     lastName: "Quan",
     
     fullName:function(){
     alert(person.firstName + " " + person.lastName);//Zhou Quan
     
     alert(this.firstName + " " + this.lastName);//Zhou Quan
     }
    }
     
    person.fullName();

    我们可以根据前面所说的方式来理解这个程序的结果, 但是这仅仅停留在表面而已,下面我们再进行更深一步的理解。

     

    this基础

    首先,我们知道javascript中所有的函数都拥有属性,就像一个对象也拥有属性一样。当一个函数被执行时,他就拥有了“this”属性--调用this所在函数的对象。this永远指向一个独一无二的对象,这个对象通常包含一个函数/方法,虽然它也可以被用在一个函数之外的全局作用域中(global scope)。注意:在严格模式(strict mode)中,在全局函数或者匿名函数中this的值为“undefined”(也就是说this没有绑定任何对象)。

    “this”被用在一个函数内部(假设是function A),那么它包含的值是调用“function A”的对象。我们需要this来访问调用“function A”的对象的属性和方法,特别是当我们不知道到这个对象名叫什么的时候,或者这个对象没有名字的时候。其实,this就是调用当前函数的对象的一个代名词!

    我们来举一个简单的例子:

    var person = {
     firstName: "zhou",
     lastName: "Quan",
     //this将为person对象,因为person对象会调用FullName方法
     FullName:function(){
     alert(this.firstName + " " + this.lastName); //zhou Quqn
     }
    }
    person.FullName();
     
    再来看一个jquery中this的简单应用:
    $ ("button").click (function (event) {
     // $(this) 的值将是 ($("button")) 对象
     // 因为这个button对象调用了 click () 方法
     console.log ($ (this).prop ("name"));
     });
    上面的代码中,$(this)绑定到了button对象,因为jquery库将$(this)绑定到调用点击事件的对象。

     

    this中存在的最大的问题

    this是没有被指定值的,直到有一个对象调用了this所在的这个函数(我们暂且给它一个称号:this Function)。

    也就是说,只有当有一个对象调用了this Function之后,this Fuction中的this才被指定!在大多数情况下,this Function被调用之后,this都会被指定一个值,但是,也有少数的情况下this是没有值的,这个问题我们将在后面进一步探讨。

     

    全局作用域(global scope)中的this

    当一段代码在浏览器中执行时,所有的全局变量和函数都是在“window”对象上定义的。因此,当”this”被用在全局函数中是,他指定的值是”window”对象。我们来看下面一段代码:

    var firstName = "Peter",
     lastName = "Ally";
     
     function showFullName () {
     // "this" inside this function will have the value of the window object
     // because the showFullName () function is defined in the global scope, just like the firstName and lastName
     console.log (this.firstName + " " + this.lastName);
     }
     
     var person = {
     firstName :"Penelope",
     lastName :"Barrymore",
     showFullName:function () {
     // "this" on the line below refers to the person object, because the showFullName function will be invoked by person object.
     console.log (this.firstName + " " + this.lastName);
     }
     }
     
     showFullName (); // Peter Ally
     
     // window is the object that all global variables and functions are defined on, hence:
     window.showFullName (); // Peter Ally
     
     // "this" inside the showFullName () method that is defined inside the person object still refers to the person object, hence:
     person.showFullName (); // Penelope Barrymore

     

    解决回调函数中的this问题

    当包含this的回调函数作为参数传递时我们会遇到这样这个问题:

    // We have a simple object with a clickHandler method that we want to use when a button on the page is clicked
     var user = {
     name:"zhouquan",
     age:21,
     clickHandler:function (event) {
     console.log (this.name + " " + this.age);
     }
     }
     
     // The button is wrapped inside a jQuery $ wrapper, so it is now a jQuery object
     // And the output will be undefined because there is no data property on the button object
     $("button").click (user.clickHandler); //undefined

    在上面的代码中,我们知道$(“button”)是一个对象,”user.clickHandler”作为click()方法的回调函数。user.clickHandler方法中的this不再是指向user对象,它现在指向的是调用点击事件的这个button对象。即使我们是使用“user.clickHandler”来调用clickHandler方法,但是clickHandler()方法是在button对象作为上下文(Context)的环境中运行的,所以,this指向的是button对象。

    从这一点上我们可以看出,当上下文(Context)改变--当我们在其他的对象上执行一个方法。“this”所指向的不在是以前的那个对象,而是现在调用这个方法的新的对象。

    为了解决这个问题,我们可以使用apply, call和bind的方法来指定“this”所指向的对象。所以上面代码的最后一行只需改为:

    $("button").click (user.clickHandler.bind (user)); //zhouquan 21

     

    解决闭包(closure)中的this问题

    另外一种容易让我们感到困扰的是当我们使用了一个内部函数(闭包)的时候。首先我们应该明确的一点就是:闭包中不能通过“this”关键字来访问外边函数的this变量,因为闭包中的this他指向的window对象。来看一个例子:

    var user = {
     country: "china",
     data:[
     {name:"ZhouYi", age:21},
     {name:"ZhouEr", age:22}
     ],
     
     clickHandler:function(){
     //在这个作用域中使用this是OK的,他指向的是user对象
     this.data.forEach(function(person){
     //但是这个内部函数的this不再指向user对象
     
     console.log("What is This referring to? " + this); //Object Window
     
     console.log(person.name + " is come from " + this.country); 
     //ZhouYi is come from undefined
     //ZhouEr is come from undefined
     })
     }
    };
    user.clickHandler();
    内部函数的this不能访问外部函数的this,所以内部函数的this就指向了全局window对象。为了解决这个问题,我们一般都是采用中间变量来保存外部函数的this:
    clickHandler:function(){
     //在这个作用域中使用this是OK的,他指向的是user对象
     //我们定义一个theUserObj来保存this的值
     var theUserObj = this;
     this.data.forEach(function(person){
     //现在我们再用theUserObj来访问user对象的属性就没问题了
     console.log(person.name + " is come from " + theUserObj.country); 
     //ZhouYi is come from china
     //ZhouEr is come from china
     })
     }
    上面的代码中,我们用一个中间变量“theUserObj”来保存外部函数的this,以便在forEach内部函数中可以通过它来调用user对象的属性。中间变量叫什么名字取决于你自己,大部分同学喜欢用“that”,个人觉得这个不够形象,所以我喜欢用theUserObj这种一看就明白的词。

     

    解决方法作为变量进行传递时的this问题

    当我们把一个对象里面的函数作为参数传递给另外一个对象时,this所指定的对象往往会超出我们的想象,来看个例子吧:

    var data = [
    {name: "ZhouYi", age:21},
    {name: "ZhouEr", age:22}
    ];
     
    var user = {
     //这里的data属性是属于user对象的
     data :[
     {name:"ZhouYi", age: 31},
     {name:"ZhouEr", age: 32}
     ],
     showData:function(){
     console.log(this.data[0].name + " " + this.data[0].age) ;
     }
    }
     
    //把user.showData赋值给一个变量
    var showUserData = user.showData;
    //当我们执行这个showUserData函数时,输出的数据来自全局的data(最外层定义的),而不是来自与user对象中的data.
    showUserData(); //ZhouYi 21
    解决这个问题的方式跟前面提到的解决回调函数中的this问题类似,我们还是采用apply, call和bind的方法来绑定指定的上下文,也就是this该指向哪个对象。
    var showUserDate = user.showData.bind(user);

     

    解决方法借用中的this问题

    何谓方法调用?打个简单的比方,有两个对象A和B,A、B有相同的属性,但是B比A还多了一个方法,这个方法是对B中的数据进行处理,现在我们想把B这种方法也给A用用:

    var gameController = {
     scores : [20, 34, 55, 46, 77],
     avgScore: null,
     players:[
     {name:"Tommy", playerID:987, age:23},
     {name:"Pau", playerID: 87, age: 33}
     ]
    }
     
    var appController = {
     scores: [900, 845, 809, 950],
     avgScore: null,
     avg: function(){
     var sumOfSores = this.scores.reduce(function(prev, cur, index, array){
     return prev + cur;
     });
     
     this.avgScore = sumOfSores /this.scores.length;
     }
     
    }
    //由于avg()是被appController调用的,所以avg中的this指向的是appController对象。
    gameController.avgScore = appController.avg();
    console.log(gameController.avgScore); //undefined
    如何解决这个问题呢? 为了让appController.avg()指向gameController对象,我们可以使用apply()方法:
     // Note that we are using the apply () method, so the 2nd argument has to be an array—the arguments to pass to the appController.avg () method.
     appController.avg.apply (gameController, gameController.scores);
     
     // The avgScore property was successfully set on the gameController object, even though we borrowed the avg () method from the appController object
     console.log (gameController.avgScore); // 46.4
     
     // appController.avgScore is still null; it was not updated, only gameController.avgScore was updated
     console.log (appController.avgScore); // null
    注意我们这里使用了两个参数,第一个参数表示this的上下文,也就是this所指向的对象;第二个参数表示要进行运算的数据。当然啦,我们还可以通过这种方式达到同样的效果:

    gameController.avg = appController.avg;
    gameController.avg();
    
    

     

    小结

    希望我写的这些东西可以给你带来一些帮助,如果你觉得哪个地方存在不足,请你在评论处提出来,我将及时完善,免得误导后面的同学;如果你觉得这篇文章讲得还可以,请帮我推荐给更多的有需要的同学阅读,谢谢!

    
    

    参考资料:http://javascriptissexy.com/understand-javascripts-this-with-clarity-and-master-it/

  • 相关阅读:
    MFC框架程序实现十一
    MFC框架程序实现八
    MFC框架程序实现十二
    在Visual C++中如何利用UDL文件来建立ADO连接
    OnePage收集 HA
    微博跳转的url HA
    淘宝iosapp调用规范 HA
    Excel操作相关 HA
    C#GDI+编程基础 HA
    html5deoms HA
  • 原文地址:https://www.cnblogs.com/MockingBirdHome/p/3376366.html
Copyright © 2011-2022 走看看