Javascript "this" ตัวชี้ภายในฟังก์ชันซ้อน


100

ฉันมีคำถามเกี่ยวกับวิธีปฏิบัติต่อตัวชี้ "นี้" ในสถานการณ์จำลองฟังก์ชันซ้อนกัน

สมมติว่าฉันแทรกโค้ดตัวอย่างต่อไปนี้ลงในเว็บเพจ ฉันได้รับข้อผิดพลาดเมื่อเรียกใช้ฟังก์ชันซ้อน "doSomeEffects ()" ฉันตรวจสอบใน Firebug และระบุว่าเมื่อฉันอยู่ในฟังก์ชันที่ซ้อนกันนั้นตัวชี้ "นี้" จะชี้ไปที่วัตถุ "หน้าต่าง" ทั่วโลกซึ่งฉันไม่คาดคิด ฉันต้องเข้าใจบางอย่างไม่ถูกต้องเพราะฉันคิดว่าตั้งแต่ฉันประกาศฟังก์ชันซ้อนภายในฟังก์ชันของอ็อบเจ็กต์มันควรมีขอบเขต "โลคัล" ที่สัมพันธ์กับฟังก์ชัน (เช่นตัวชี้ "นี้" จะหมายถึงอ็อบเจ็กต์เองเช่น เป็นอย่างไรในคำสั่ง "if" แรกของฉัน)

คำแนะนำใด ๆ (ไม่มีการเล่นสำนวน) จะได้รับการชื่นชม

var std_obj = {
  options : { rows: 0, cols: 0 },
  activeEffect : "none",
  displayMe : function() {

    // the 'this' pointer is referring to the std_obj
    if (this.activeEffect=="fade") { }

    var doSomeEffects = function() {

      // the 'this' pointer is referring to the window obj, why?
      if (this.activeEffect=="fade") { }

    }

    doSomeEffects();   
  }
};

std_obj.displayMe();

คำถามของคุณคืออะไร?
Sarfraz

2
เมื่อใช้ภายในฟังก์ชันthisหมายถึงวัตถุที่เรียกใช้ฟังก์ชัน
ประมาณ

9
สิ่งที่คุณสามารถทำได้ในขอบเขตภายนอกคือสิ่งที่ต้องการvar self = this;แล้วอ้างถึงselfในฟังก์ชันด้านในผ่านการปิด
ไก่

1
doSomeEffectsไม่เกี่ยวข้องกับ obj ใด ๆ โดยเฉพาะดังนั้นจึงthisถือว่าเป็นหน้าต่างซึ่งเป็นแม่ขององค์ประกอบทั้งหมด
ประมาณ

3
@JoJoeDad ฉันจะพูดแบบนี้ทางการทูตได้อย่างไร? แต่คำตอบที่ได้รับจาก chuckj ด้านล่างนี้เป็นคำตอบสำหรับคำถามของคุณ เพื่อให้เข้าใจอย่างแท้จริงสิ่งที่เกิดขึ้นคุณควรอ่านบริบทการดำเนินการ , ห่วงโซ่ขอบเขตและคำหลักนี้ และจากลักษณะของคำตอบบางส่วนที่นี่คนอื่น ๆ ก็ควรอ่านเช่นกัน ฉันพยายามเผยแพร่จาวาสคริปต์ที่ดี นั่นคือเหตุผลที่ฉันสละเวลาในการให้ลิงก์เหล่านี้
onefootswill

คำตอบ:


122

ใน JavaScript thisวัตถุนั้นขึ้นอยู่กับการเรียกใช้ฟังก์ชันของคุณ

โดยทั่วไปมีสามวิธีในการตั้งค่าthisวัตถุ:

  1. someThing.someFunction(arg1, arg2, argN)
  2. someFunction.call(someThing, arg1, arg2, argN)
  3. someFunction.apply(someThing, [arg1, arg2, argN])

ในทุกตัวอย่างข้างต้นวัตถุจะthis someThingการเรียกใช้ฟังก์ชันโดยไม่มีวัตถุแม่นำหน้าโดยทั่วไปจะทำให้คุณได้รับวัตถุส่วนกลางซึ่งในเบราว์เซอร์ส่วนใหญ่หมายถึงwindowวัตถุ


ฉันเปลี่ยนรหัสthis.doSomeEffects();ตามคำตอบของคุณ แต่ก็ยังใช้ไม่ได้ ทำไม?
Arashsoft

1
@Arashsoft thisในจุดที่จะต้องthis.doSomeEffects() std_objตามที่อธิบายไว้ในคำตอบข้างต้นหากฟังก์ชันไม่มีการอ้างอิงวัตถุก็thisจะเป็นวัตถุหน้าต่าง
Shivam

48

เนื่องจากสิ่งนี้ดูเหมือนจะเป็นคำถามที่ได้รับการโหวตมากที่สุดในประเภทนี้ให้ฉันเพิ่มหลังจากหลายปีที่ผ่านมาโซลูชัน ES6 ที่ใช้ฟังก์ชันลูกศร:

var std_obj = {
  ...
  displayMe() {
    ...
    var doSomeEffects = () => {
                        ^^^^^^^    ARROW FUNCTION    
      // In an arrow function, the 'this' pointer is interpreted lexically,
      // so it will refer to the object as desired.
      if (this.activeEffect=="fade") { }
    };
    ...    
  }
};

6
ตอนนี้มีความคืบหน้าแล้ว!
Joshua Pinter

วิธีแก้ปัญหาที่ยอดเยี่ยมง่าย ๆ
Joshua Schlichting

ไม่รู้ตลอด 4 ปีที่ผ่านมาเป็นยังไงบ้าง
xorinzor

32

thisไม่ได้เป็นส่วนหนึ่งของขอบเขตการปิดสามารถคิดได้ว่าเป็นพารามิเตอร์เพิ่มเติมของฟังก์ชันที่ถูกผูกไว้ที่ไซต์การโทร thisหากวิธีการไม่ได้เรียกว่าเป็นวิธีการที่แล้ววัตถุทั่วโลกจะถูกส่งเป็น windowในเบราว์เซอร์วัตถุโลกเป็นเหมือน ตัวอย่างเช่นพิจารณา funciton ต่อไปนี้

function someFunction() {
}

และวัตถุต่อไปนี้

var obj = { someFunction: someFunction };

หากคุณเรียกใช้ฟังก์ชันโดยใช้ไวยากรณ์วิธีเช่น

obj.someFunciton();

จากนั้นถูกผูกไว้กับthisobj

หากคุณเรียก someFunction () โดยตรงเช่น

someFunction();

จากนั้นถูกผูกไว้กับวัตถุทั่วโลกที่เป็นthiswindow

วิธีแก้ไขที่พบบ่อยที่สุดคือการจับสิ่งนี้เข้าสู่การปิดเช่น

displayMe : function() {      

    // the 'this' pointer is referring to the std_obj      
    if (this.activeEffect=="fade") { }      
    var that = this;  
    var doSomeEffects = function() {      

      // the 'this' pointer is referring to global
      // that, however, refers to the outscope this
      if (that.activeEffect=="fade") { }      
    }      

    doSomeEffects();         
 }      

นั่น = นี่สมบูรณ์แบบ
Nather Webber

9

มีความแตกต่างระหว่างตัวแปรกล่องหุ้มและ "this" "this" ถูกกำหนดโดยผู้เรียกใช้ฟังก์ชันในขณะที่ตัวแปรที่ชัดเจนยังคงอยู่ภายในบล็อกการประกาศฟังก์ชันที่เรียกว่าสิ่งที่แนบมา ดูตัวอย่างด้านล่าง:

function myFirstObject(){
    var _this = this;
    this.name = "myFirstObject";
    this.getName = function(){
       console.log("_this.name = " + _this.name + " this.name = " + this.name);  
    }
}

function mySecondObject(){
    var _this = this;
    this.name = "mySecondObject";
    var firstObject = new myFirstObject();
    this.getName = firstObject.getName
}

var secondObject = new mySecondObject();
secondObject.getName();

คุณสามารถทดลองใช้งานได้ที่นี่: http://jsfiddle.net/kSTBy/

สิ่งที่เกิดขึ้นในฟังก์ชันของคุณคือ "doSomeEffects ()" ถูกเรียกอย่างชัดเจนซึ่งหมายถึงบริบทหรือ "this" ของฟังก์ชันคือหน้าต่าง ถ้า "doSomeEffects" เป็นเมธอดต้นแบบเช่น this.doSomeEffects เมื่อพูดว่า "myObject" ดังนั้น myObject.doSomeEffects () จะทำให้ "this" เป็น "myObject"


4
สำหรับคนขี้เกียจและใจร้อนบันทึกการสาธิตนี้:_this.name = myFirstObject this.name = mySecondObject
ptim

9

เพื่อให้เข้าใจคำถามนี้ลองรับผลลัพธ์สำหรับตัวอย่างข้อมูลต่อไปนี้

var myObject = {
    foo: "bar",
    func: function() {
        var self = this;
        console.log("outer func:  this.foo = " + this.foo);
        console.log("outer func:  self.foo = " + self.foo);
        (function() {
            console.log("inner func:  this.foo = " + this.foo);
            console.log("inner func:  self.foo = " + self.foo);
        }());
    }
};
myObject.func();

โค้ดด้านบนจะส่งออกสิ่งต่อไปนี้ไปยังคอนโซล:

outer func:  this.foo = bar
outer func:  self.foo = bar
inner func:  this.foo = undefined
inner func:  self.foo = bar

ในฟังก์ชันภายนอกทั้งนี้และตัวเองอ้างถึง myObject ดังนั้นทั้งสองจึงสามารถอ้างอิงและเข้าถึง foo ได้อย่างถูกต้อง

ในฟังก์ชันภายในนี้ไม่ได้หมายถึง myObject อีกต่อไป ด้วยเหตุนี้ this.foo จึงไม่ได้กำหนดไว้ในฟังก์ชันภายในในขณะที่การอ้างอิงถึงตัวแปรโลคัลยังคงอยู่ในขอบเขตและสามารถเข้าถึงได้ที่นั่น (ก่อน ECMA 5 สิ่งนี้ในฟังก์ชันด้านในจะอ้างถึงวัตถุหน้าต่างส่วนกลางในขณะที่ ECMA 5 สิ่งนี้ในฟังก์ชันภายในจะไม่ได้กำหนดไว้)


1
ในฟังก์ชันภายในthisหมายถึงวัตถุหน้าต่าง (ในสภาพแวดล้อมเบราว์เซอร์) หรือวัตถุ GLOBAL (ในสภาพแวดล้อม node.js)
raneshu

2
ทำไมถึงเกิดขึ้น?
setu

@raneshu "ใช้อย่างเข้มงวด" และthisไม่อ้างถึงหน้าต่างอีกต่อไป
rofrol

3

ตามที่ Kyle อธิบายไว้คุณสามารถใช้callหรือapplyระบุthisภายในฟังก์ชัน:

แนวคิดดังกล่าวนำไปใช้กับโค้ดของคุณ:

var std_obj = {
    options: {
        rows: 0,
        cols: 0
    },
    activeEffect: "none",
    displayMe: function() {

        // the 'this' pointer is referring to the std_obj
        if (this.activeEffect == "fade") {}

        var doSomeEffects = function() {
            // the 'this' pointer is referring to the window obj, why?
            if (this.activeEffect == "fade") {}
        }

        doSomeEffects.apply(this,[]);
    }
};

std_obj.displayMe();

JsFiddle


0

ฉันยังได้รับคำเตือน " การเข้าถึงข้อมูลอ้างอิงที่อาจไม่ถูกต้องไปยังฟิลด์ชั้นเรียนผ่านทางนี้ "

class MyListItem {
    constructor(labelPrm) {
        this._flagActive = false;
        this._myLabel = labelPrm;
        labelPrm.addEventListener('click', ()=>{ this.onDropdownListsElementClick();}, false);
    }

    get myLabel() {
        return this._myLabel
    }
    get flagActive(){
        return this._flagActive;
    }

    onDropdownListsElementClick(){console.log("Now'this'refers to the MyListItem itself")}}//end of the class

0

เนื่องจากไม่ได้กล่าวถึงฉันจะพูดถึงว่าการใช้.bind()เป็นวิธีแก้ปัญหา -


        doSomeEffects=doSomeEffect.bind(this);
        doSomeEffects();   
      }
    };

    std_obj.displayMe();

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

bad = { 
  name:'NAME', 
  z : function() { 
    function x() { console.log(this.name); }; 
    x() 
  } 
};
bad.z() // prints 'undefined'

good = { 
  name:'NAME', 
  z : function() { 
    function x() { console.log(this.name); }; 
    x=x.bind(this);
    x();
  } 
};
good.z() // prints 'NAME'

เป็นความจริงที่ว่าการใช้ฟังก์ชันลูกศร=>จะดูไม่แน่นอนและเป็นเรื่องง่ายสำหรับผู้เขียนโปรแกรม แต่ก็ควรจะเก็บไว้ในใจว่าขอบเขตของคำศัพท์มีแนวโน้มที่จะต้องทำงานมากขึ้นในแง่ของการประมวลผลและหน่วยความจำในการติดตั้งและบำรุงรักษาว่าขอบเขตของคำศัพท์เมื่อเทียบกับเพียงแค่การเชื่อมโยงการทำงานของด้วยตัวชี้ผ่าน this.bind()

ประโยชน์ส่วนหนึ่งของการพัฒนาคลาสใน JS คือการนำเสนอวิธีการที่thisน่าเชื่อถือและพร้อมใช้งานมากขึ้นเพื่อแยกออกจากการเขียนโปรแกรมเชิงฟังก์ชันและขอบเขตคำศัพท์และลดค่าใช้จ่าย

จาก MDN

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

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