'var that = this;' อะไร หมายถึงใน JavaScript?


351

ในไฟล์ JavaScript ที่ฉันเห็น:

function Somefunction(){
   var that = this; 
   ... 
}

อะไรคือวัตถุประสงค์ของการประกาศthatและมอบหมายthisสิ่งนี้ให้กับมัน?


3
เป็นไปได้ที่ซ้ำกันของvar self = this?
Bergi

6
การแฮ็ก "this" และ "that" นั้นไม่จำเป็นสำหรับฟังก์ชั่นลูกศร ด้วยฟังก์ชั่นลูกศร "นี้" ทำงานตามที่คาดไว้ ดูที่นี่สำหรับรายละเอียดเพิ่มเติมES6 ในเชิงลึก: ฟังก์ชั่นลูกศร
satguru srivastava

1
นี่คือแนวคิดของสิ่งนี้คือคำอธิบายscotch.io/@alZami/understanding-this-in-javascript
AL-zami

คำอธิบายที่ดีเกี่ยวกับพฤติกรรมที่ลึกลับนี้ตามบริบทที่นี่
RBT

คำอธิบายล่าสุดและการปรับปรุงสามารถพบได้ที่นี่
Sukrit Gupta

คำตอบ:


486

ฉันจะเริ่มคำตอบนี้ด้วยภาพประกอบ:

var colours = ['red', 'green', 'blue'];
document.getElementById('element').addEventListener('click', function() {
    // this is a reference to the element clicked on

    var that = this;

    colours.forEach(function() {
        // this is undefined
        // that is a reference to the element clicked on
    });
});

คำตอบของฉันเดิมแสดงให้เห็นนี้ด้วย jQuery ซึ่งแตกต่างกันเล็กน้อยเท่านั้น:

$('#element').click(function(){
    // this is a reference to the element clicked on

    var that = this;

    $('.elements').each(function(){
        // this is a reference to the current element in the loop
        // that is still a reference to the element clicked on
    });
});

เนื่องจากthisการเปลี่ยนแปลงบ่อยครั้งเมื่อคุณเปลี่ยนขอบเขตโดยการเรียกใช้ฟังก์ชันใหม่คุณจะไม่สามารถเข้าถึงค่าดั้งเดิมได้โดยใช้มัน aliasing มันจะช่วยให้คุณยังคงสามารถเข้าถึงค่าเดิมของthatthis

โดยส่วนตัวแล้วฉันไม่ชอบการใช้thatนามแฝง มันไม่ค่อยชัดเจนว่ามันหมายถึงอะไรโดยเฉพาะอย่างยิ่งถ้าฟังก์ชั่นมีความยาวมากกว่าสองบรรทัด ฉันมักจะใช้นามแฝงที่เป็นคำอธิบายมากกว่านี้ clickedElในตัวอย่างของฉันข้างต้นผมอาจต้องการใช้


149
var self = this;ฉันมักจะไปกับ คำว่าthatดูเหมือนจะบ่งบอกตัวแปรคืออะไร thisแต่
David Murdoch

13
@ David ใช่ฉันคิดว่ามันค่อนข้างทำให้เข้าใจผิด แต่ถ้าอย่างที่ Crockford พูดว่ามันเป็นแบบแผนก็ควรที่จะลงไปเส้นทางนั้น ฉันเห็นด้วยกับคุณโดยสิ้นเชิง แต่ก็สมเหตุสมผลดีกว่า
El Ronnoco

4
@El Ronnoco แต่ "เขามีผมหงอกและเคราที่เกลี้ยงเกลาและท่าทางของเขาทำให้นึกถึงชายชราผู้ไม่พอใจที่ตะโกนใส่เด็ก ๆ เพื่อลงจากสนามหญ้า" - blogging.compendiumblog.com/blog/software-for-humans/0/0/… ;-p
David Murdoch

7
@ElRonnoco: นั่นคือการอุทธรณ์ไปยังผู้มีอำนาจแม้ว่า ถ้าเราเพียงแค่ทำในสิ่งที่ "คนดัง" พูดว่าเราควรทำเรากำลังมุ่งหายนะ
Lightness Races ในวงโคจร

3
forEachฟังก์ชั่นใช้เวลาสองคืออาร์กิวเมนต์ตัวเลือกที่จะผูกพันของฟังก์ชั่น colours.forEach(function(){/* 'this' is bound correctly --> */}, this);ดังนั้นโน้ตควรจะเพิ่มที่var that = thisไม่ได้จริงforEachที่จำเป็นด้วย
Matt Clarkson

107

จากCrockford

โดยการประชุมเราทำให้เอกชนที่ ตัวแปร ใช้เพื่อทำให้วัตถุพร้อมใช้งานกับวิธีการส่วนตัว นี่คือวิธีแก้ปัญหาสำหรับข้อผิดพลาดในข้อมูลจำเพาะภาษา ECMAScript ซึ่งเป็นสาเหตุนี้จะถูกตั้งค่าไม่ถูกต้องสำหรับการทำงานภายใน

JS Fiddle

function usesThis(name) {
    this.myName = name;

    function returnMe() {
        return this;        //scope is lost because of the inner function
    }

    return {
        returnMe : returnMe
    }
}

function usesThat(name) {
    var that = this;
    this.myName = name;

    function returnMe() {
        return that;            //scope is baked in with 'that' to the "class"
    }

    return {
        returnMe : returnMe
    }
}

var usesthat = new usesThat('Dave');
var usesthis = new usesThis('John');
alert("UsesThat thinks it's called " + usesthat.returnMe().myName + '\r\n' +
      "UsesThis thinks it's called " + usesthis.returnMe().myName);

การแจ้งเตือนนี้ ...

ใช้ที่คิดว่ามันเรียกว่าเดฟ

ใช้นี่คิดว่ามันเรียกว่าไม่ได้กำหนด


2
ขอบคุณสรุปมันก็ดีพอสำหรับฉัน
คริส

3
ฉันอ่านแล้วไม่เข้าใจเพราะมันไม่มีรายละเอียดค้นหาบน Google พบหน้านี้ ที่ฉันชี้ไปที่ประโยคเดียวกันอีกครั้ง ดังนั้นการลงคะแนนเสียง
Aditya MP

3
นั่นคือประเด็นที่เป็นธรรมฉันจะบอกว่าคนที่ไม่คุ้นเคยกับ JavaScript จะมีปัญหาในการเข้าใจแนวคิดจากคำตอบของฉันคนเดียว ฉันตอบสั้นมาก (และฉันเชื่อมโยงไปยังหน้าที่คุณ Googled สำหรับ .. ) ฉันพูดได้ว่าคำตอบของ lonesomeday นั้นชัดเจนที่สุดแม้ว่าฉันจะยังคงชอบมันใน JS ธรรมดามากกว่าตัวอย่าง jQuery
El Ronnoco

16
ฉันไม่ทำผิด ดีใจที่ได้เห็นคนที่แสดงความคิดเห็นเมื่อ downvoting!
El Ronnoco

4
ปัญหากับคำตอบของ Crockford คือthatตัวแปรที่ไม่ได้ใช้ในตัวอย่างของเขาเลย มันทำให้ดูเหมือนว่าเพียงแค่สร้างตัวแปรการถือครองthisทำสิ่งที่เหลือของรหัส
r3m0t

86

นี่คือแฮ็คที่จะทำให้ฟังก์ชั่นภายใน (ฟังก์ชั่นที่กำหนดไว้ในฟังก์ชั่นอื่น ๆ ) ทำงานได้มากขึ้นกว่าที่ควร ในจาวาสคริปต์เมื่อคุณกำหนดฟังก์ชั่นหนึ่งภายในฟังก์ชั่นอื่นthisโดยอัตโนมัติได้รับการตั้งค่าให้ขอบเขตทั่วโลก สิ่งนี้อาจทำให้สับสนเนื่องจากคุณคาดหวังว่าthisจะมีค่าเหมือนกันกับในฟังก์ชั่นด้านนอก

var car = {};
car.starter = {};

car.start = function(){
    var that = this;

    // you can access car.starter inside this method with 'this'
    this.starter.active = false;

    var activateStarter = function(){
        // 'this' now points to the global scope
        // 'this.starter' is undefined, so we use 'that' instead.
        that.starter.active = true;

        // you could also use car.starter, but using 'that' gives
        // us more consistency and flexibility
    };

    activateStarter();

};

นี่เป็นปัญหาโดยเฉพาะเมื่อคุณสร้างฟังก์ชั่นเป็นวิธีการของวัตถุ (เช่นcar.startในตัวอย่าง) จากนั้นสร้างฟังก์ชั่นภายในวิธีการนั้น (เช่นactivateStarter) ในวิธีการระดับบนสุดthisชี้ไปที่วัตถุมันเป็นวิธีการ (ในกรณีนี้car) แต่ในฟังก์ชั่นด้านในthisตอนนี้ชี้ไปที่ขอบเขตทั่วโลก นี่คือความเจ็บปวด

การสร้างตัวแปรที่จะใช้โดยการประชุมในขอบเขตทั้งสองเป็นวิธีแก้ปัญหาที่พบได้ทั่วไปในจาวาสคริปต์ นี่คือเหตุผลที่ทำให้เกิดเสียงชื่อทั่วไปมากthatถูกนำมาใช้ เป็นการประชุมที่จดจำได้ง่ายสำหรับการเอาชนะข้อบกพร่องทางภาษา

เช่นเดียวกับคำแนะนำของ El Ronnoco ที่Douglas Crockfordคิดว่านี่เป็นความคิดที่ดี


8
ฉันคิดว่านี่เป็นคำตอบที่มีประโยชน์มากกว่าคำตอบที่ยอมรับ เพราะมันอธิบายเหตุผลที่ Crockford ได้ประดิษฐ์ "ที่" ในขณะที่คำตอบเกี่ยวกับ jQuery ไม่ได้
Konstantin Smolyanin

4
นี่เป็นตัวอย่างที่ดีกว่าแล้วคำตอบที่ยอมรับได้ มันอธิบายสิ่งที่มันเป็นเหมือน "ข้อผิดพลาดในข้อกำหนดภาษา ECMAScript ซึ่งทำให้สิ่งนี้ถูกตั้งค่าอย่างไม่ถูกต้องสำหรับฟังก์ชั่นด้านใน" กล่าวโดยดักลาส
kakacii

1
คุณอาจต้องการแก้ไขให้ถูกต้องตามหลักไวยากรณ์ ฉันรู้ว่ามันเป็นเหมือนตัวพิมพ์ผิด แต่อาจสร้างความสับสนให้กับผู้เริ่มต้นจาวาสคริปต์ได้เนื่องจากคำถามนี้เป็นแบบเริ่มต้นที่มากกว่า ฉันหมายความว่ามันควรจะเป็น: var car = {}; car.starter = {}; car.start = function () {... }
kakacii

1
@kakacii ขอบคุณ แก้ไขแล้ว
Waylon Flinn

9

การใช้งานthatนั้นไม่จำเป็นจริงๆถ้าคุณใช้วิธีแก้ปัญหาด้วยการใช้call()หรือapply():

var car = {};
car.starter = {};

car.start = function(){
    this.starter.active = false;

    var activateStarter = function(){
        // 'this' now points to our main object
        this.starter.active = true;
    };

    activateStarter.apply(this);
};

3

บางครั้งthisสามารถอ้างถึงขอบเขตอื่นและอ้างถึงสิ่งอื่นตัวอย่างเช่นสมมติว่าคุณต้องการเรียกเมธอดตัวสร้างภายในเหตุการณ์ DOM ในกรณีนี้thisจะอ้างถึงองค์ประกอบ DOM ไม่ใช่วัตถุที่สร้างขึ้น

HTML

<button id="button">Alert Name</button>

JS

var Person = function(name) {
  this.name = name;
  var that = this;
  this.sayHi = function() {
    alert(that.name);
  };
};

var ahmad = new Person('Ahmad');
var element = document.getElementById('button');
element.addEventListener('click', ahmad.sayHi); // => Ahmad

การสาธิต

วิธีการแก้ปัญหาข้างต้นจะthisทำให้thatเราสามารถเข้าถึงและเข้าถึงคุณสมบัติชื่อภายในsayHiวิธีการจากthatนั้นจึงสามารถเรียกได้โดยไม่มีปัญหาในการโทร DOM

อีกวิธีคือการกำหนดthatวัตถุที่ว่างเปล่าและเพิ่มคุณสมบัติและวิธีการนั้นแล้วส่งคืน แต่ด้วยวิธีนี้คุณจะสูญเสียprototypeตัวสร้าง

var Person = function(name) {
  var that = {};
  that.name = name;
  that.sayHi = function() {
    alert(that.name);
  };
  return that;
};

2

นี่คือตัวอย่าง `

$(document).ready(function() {
        var lastItem = null;
        $(".our-work-group > p > a").click(function(e) {
            e.preventDefault();

            var item = $(this).html(); //Here value of "this" is ".our-work-group > p > a"
            if (item == lastItem) {
                lastItem = null;
                $('.our-work-single-page').show();
            } else {
                lastItem = item;
                $('.our-work-single-page').each(function() {
                    var imgAlt = $(this).find('img').attr('alt'); //Here value of "this" is '.our-work-single-page'. 
                    if (imgAlt != item) {
                        $(this).hide();
                    } else {
                        $(this).show();
                    }
                });
            }

        });
    });`

ดังนั้นคุณจะเห็นว่าค่าของสิ่งนี้คือสองค่าที่แตกต่างกันขึ้นอยู่กับองค์ประกอบ DOM ที่คุณกำหนดเป้าหมาย แต่เมื่อคุณเพิ่ม "ที่" ในโค้ดด้านบนคุณจะเปลี่ยนค่าของ "this" ที่คุณกำหนดเป้าหมาย

`$(document).ready(function() {
        var lastItem = null;
        $(".our-work-group > p > a").click(function(e) {
            e.preventDefault();
            var item = $(this).html(); //Here value of "this" is ".our-work-group > p > a"
            if (item == lastItem) {
                lastItem = null;
                var that = this;
                $('.our-work-single-page').show();
            } else {
                lastItem = item;
                $('.our-work-single-page').each(function() {
                   ***$(that).css("background-color", "#ffe700");*** //Here value of "that" is ".our-work-group > p > a"....
                    var imgAlt = $(this).find('img').attr('alt'); 
                    if (imgAlt != item) {
                        $(this).hide();
                    } else {
                        $(this).show();
                    }
                });
            }

        });
    });`

..... $ (ว่า) .css ("background-color", "# ffe700"); // นี่คือค่าของ "that" คือ ".our-work-group> p> a" เพราะค่าของ var that = this; ดังนั้นแม้ว่าเราจะอยู่ที่ "this" = '.our-work-single-page' แต่เรายังสามารถใช้ "that" เพื่อจัดการองค์ประกอบ DOM ก่อนหน้า

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.