zoukankan      html  css  js  c++  java
  • Javascript中的this

    初学JS,对this有些困惑,在此做一下总结,欢迎指正交流。

    就和我们平常说话一样,“张三跑得飞快,因为快迟到了”,注意这个他。我们也可以说“张三跑得飞快,因为张三快迟到了”,但是一般不这么说。同样,在JS里,我们用this来指代对象。

    var person = {
        firstName: "Penelope",
        lastName: "Barrymore",
        fullName: function() {​
            // 我们使用this,类比上面例句中的他
            console.log(this.firstName + " " + this.lastName);​
            // 也可以这样写
            console.log(person.firstName + " " + person.lastName);
        }
    };

    如果我们用person.firstName和person.lastName,语句就不够明确,在其他我们没注意到的地方可能会存在全局变量也叫"person",所以我们使用this不仅起到简化指代的作用,更可以让语句变得明确,就像我们用"他"来特指谈到的那个张三而不是别的张三。另外,在大多数时候我们不会事先知道调用该函数的对象叫什么名字,而可以通过this来获取该对象的属性和方法。

    再来看一个简单例子:

    var person = {
        firstName: "Penelope",
        lastName: "Barrymore",
        // "this"被用在下面的showFullName方法里,这个方法定义在person对象里 
        showFullName: function() {
            console.log(this.firstName + " " + this.lastName);
        }​
    };​
    // "this"指向person对象,因为person对象调用了showFullName()方法
    person.showFullName(); // Penelope Barrymore

    一个简单的jQuery例子:

    $("button").click(function(event) {
        // $(this)指向$("button")对象​ // 因为button对象调用了click()方法 
        console.log($(this).prop("name"));
    });

    在这里,之所以$(this)指向$("button")是因为jQuery把$(this)绑定到触发click方法的对象上,虽然实际上定义这个$(this)的匿名函数是无法获取其外部函数的"this"的值的,我们会在后文加以解释。

    全局作用域中的this

    当代码在浏览器中执行时,所有的全局变量和函数都定义在window对象下,它是整个javascript应用或页面的容器。因此当我们在一个全局函数中使用this时,它指向window对象。

    var firstName = "Peter",
        lastName = "Ally";​
    function showFullName() {
        // 此函数中的"this"会指向window对象
        // 因为showFullName()定义在全局作用域下,和firstName、lastName一样​ 
        console.log(this.firstName + " " + this.lastName);
    }​;
    var person = {
        firstName: "Penelope",
        lastName: "Barrymore",
        showFullName: function() {
            // 这里的"this"指向person对象,因为showFullName方法被person对象调用​ 
            console.log(this.firstName + " " + this.lastName);
        }
    }​;
    
    showFullName(); // Peter Ally​ 
    // 所有的全局变量和函数都定义在window对象下,因此
    window.showFullName(); // Peter Ally​ 
    // 这里"this"指向person对象,因此
    person.showFullName(); // Penelope Barrymore

    那些"不守规矩"的this

    我们知道JS里的函数也是对象,所以函数可以有属性。在一个函数执行时,它会得到this属性(PS:随着深入学习,这句话不对,this并不是函数对象的属性,而是执行上下文的一个属性,this的值thisValue在进入上下文的时候确定,并在上下文运行期间永久不变,想要深入理解的同学可以去看汤姆大叔的深入理解javascript系列),其值为调用该函数的对象,我们姑且称这个函数为"this函数"。this总是指向一个对象,并且常常被用在函数或方法里,当然它也可以在函数外部全局作用域里使用。注意,直到this函数被调用时,this才被赋值。在大部分情况下,this指向调用this函数的对象,然而在某些情况下会有例外。

    1.作为被传递的回调函数里的this

    var user = {
        data: [{
            name: "T. Woods",
            age: 37
        }, {
            name: "P. Mickelson",
            age: 43
        }],
        clickHandler: function(event) {
            var randomNum = Math.floor(Math.random() * 2); // 0或1的随机数 
    // 从data数组中读取name和age
            console.log(this.data[randomNum].name + " " + this.data[randomNum].age);
        }
    }​; 
    // 结果为undefined,因为button对象中不存在data属性
    $("button").click(user.clickHandler); // Cannot read property '0' of undefined

     在这里,$("button")是一个对象,我们把user.clickHandler方法作为回调函数传递给它的click()方法,此时user.clickHandler方法里的this不再指向user对象,而是指向调用user.clickHandler方法的对象,即button对象。注意虽然我们使用user.clickHandler来调用clickHandler()方法(毕竟clickHandler是定义在user下的方法),clickHandler()方法本身是被button对象调用的,所以this指向button对象。

    解决方法

    现在我们很想让this.data能够表示user对象的data属性,我们可以使用Bind(),Apply()和Call()方法来专门改变this的值。

    在这个例子里,我们使用bind方法,将这一行

    $("button").click(user.clickHandler);

    改为以下这行,把clickHandler方法绑定到user对象

    $("button").click(user.clickHandler.bind(user)); // P. Mickelson 43

    2.闭包中的this

     另一个常见的情况就是内部函数(闭包)中的this。我们知道闭包不能通过this关键字来访问它的外部函数的this变量,这个this变量只能由外部函数自身来访问。

    var user = {
        tournament: "The Masters",
        data: [{
            name: "T. Woods",
            age: 37
        }, {
            name: "P. Mickelson",
            age: 43
        }],  ​
        clickHandler: function() {
            // 在这里使用this.data没有问题,因为"this"指向user对象,data是user对象的属性       ​
            this.data.forEach(function(person) {
                // 但在这个内部匿名函数里,"this"不再指向user对象 
                // 这个内部函数无法访问外部函数的"this" 
                console.log("What is This referring to? " + this); //[object Window]​ 
    
                console.log(person.name + " is playing at " + this.tournament);
                // T. Woods is playing at undefined​ 
                // P. Mickelson is playing at undefined​ 
            })
        }​
    };​
    user.clickHandler(); // What is "this" referring to? [object Window]

    这个匿名函数里的this无法访问外部函数的this,所以它被绑定到全局的window对象上。

    解决方法

    一个惯例方法是在进入forEach方法之前,我们把this的值赋给另一个变量。

    var user = {
        tournament: "The Masters",
        data: [{
            name: "T. Woods",
            age: 37
        }, {
            name: "P. Mickelson",
            age: 43
        }],
        clickHandler: function(event) {
            // 趁着在这里"this"还是指向user对象的,我们此时把"this"的值赋给that变量
            var that = this;
            this.data.forEach(function(person) {
                // 把this.tournament改为that.tournament​,由于作用域链,内部函数可以访问外部函数的that变量
                console.log(person.name + " is playing at " + that.tournament);
            })
        }​
    };​
    user.clickHandler();
    // T. Woods is playing at The Masters​ 
    //  P. Mickelson is playing at The Masters

    这个that变量可以是任意名字,一般我们取名为that。

    3.当方法被赋值给一个变量时的this

    // 全局变量data 
    var data = [{
        name: "Samantha",
        age: 12
    }, {
        name: "Alexis",
        age: 14
    }];​
    var user = {
        // user的属性data 
        data: [{
            name: "T. Woods",
            age: 37
        }, {
            name: "P. Mickelson",
            age: 43
        }],
        showData: function(event) {
            var randomNum = Math.floor(Math.random() * 2);
            ​
            console.log(this.data[randomNum].name + " " + this.data[randomNum].age);
        }​
    };​
    // 把user.showData赋值给一个变量
    var showUserData = user.showData;​
    
    //​ 调用showUserData时,取得的是全局data,而不是user对象的data
    showUserData(); // Samantha 12 

    解决方法

    可以用bind()方法来设置this的值

    // 把showData方法绑定到user对象 
    var showUserData = user.showData.bind(user);​
    // 现在我们取得的是来自user对象的data
    showUserData(); // P. Mickelson 43

    4.借用函数时的this

    // 现在有两个对象,一个有avg()方法,另一个没有​ 
    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 sumOfScores = this.scores.reduce(function(prev, cur, index, array) {
                return prev + cur;
            });​
            this.avgScore = sumOfScores / this.scores.length;
        }
    };​
    // 我们想让gameController借用avg()方法,求得自己scores的平均值
    // 同时不改变appController的avgScore
    
    // 下面这行肯定是行不通的
    gameController.avgScore = appController.avg();

    解决方法

    我们可以使用apply()方法

    // 注意我们在使用apply()方法,所以第二个参数必须是数组,它包含要传递给appController.avg()的参数​ 
    appController.avg.apply(gameController, gameController.scores);​
    
    // 成功得到gameController对象的avgScore的值,虽然借用的是appController对象的avg()方法​ 
    console.log(gameController.avgScore); // 46.4​ 
    // appController.avgScore仍然为null​ 
    console.log(appController.avgScore); // null

    译自http://javascriptissexy.com/understand-javascripts-this-with-clarity-and-master-it/

  • 相关阅读:
    TCP/IP三次握手四次挥手
    注解(JDK1.5之后)
    枚举(JDK1.5之后)
    局部内部类
    非静态内部类
    静态内部类
    匿名内部类
    接口
    根父类:java.lang.Object
    native关键字
  • 原文地址:https://www.cnblogs.com/coiorz/p/4592563.html
Copyright © 2011-2022 走看看