var self = นี่?


182

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

function MyObject() {
  this.doSomething = function() {
    ...
  }

  var self = this
  $('#foobar').bind('click', function(){
    self.doSomethng()
    // this.doSomething() would not work here
  })
}

มันใช้งานได้ แต่เป็นวิธีที่ดีที่สุดที่จะทำหรือไม่ มันดูแปลกสำหรับฉัน


15
คุณควรหลีกเลี่ยงselfเนื่องจากมีwindow.selfวัตถุและคุณสามารถใช้สิ่งนั้นได้โดยไม่ตั้งใจหากคุณลืมประกาศselfvar ของคุณเอง(เช่นเมื่อมีการย้ายรหัสไป) สิ่งนี้อาจสร้างความรำคาญในการตรวจพบ / แก้ไข _thisดีกว่าที่จะใช้สิ่งที่ต้องการ
Alastair Maw


3
จากบทช่วยสอน JavaScript : "ค่าของthisเป็นแบบไดนามิกใน JavaScript ซึ่งจะถูกกำหนดเมื่อมีการเรียกใช้ฟังก์ชันไม่ใช่เมื่อมีการประกาศ"
DavidRR

มีความเป็นไปได้ที่ซ้ำกันของ: สิ่งที่รองรับ JavaScript นี้สำนวน: var self = this?
DavidRR

self === thisสิ่งที่เป็นอยู่ในขอบเขตทั่วโลก ดังนั้นselfในบริบทท้องถิ่นที่เหมาะสมและเป็นไปตามรูปแบบ
programmer5000

คำตอบ:


210

คำถามนี้ไม่เฉพาะเจาะจงกับ jQuery แต่เฉพาะเจาะจงกับ JavaScript โดยทั่วไป ปัญหาหลักคือวิธี "แปร" ตัวแปรในฟังก์ชันฝังตัว นี่คือตัวอย่าง:

var abc = 1; // we want to use this variable in embedded functions

function xyz(){
  console.log(abc); // it is available here!
  function qwe(){
    console.log(abc); // it is available here too!
  }
  ...
};

เทคนิคนี้อาศัยการปิดตัว แต่มันไม่ทำงานthisเพราะthisเป็นตัวแปรหลอกที่อาจเปลี่ยนจากขอบเขตเป็นขอบเขตแบบไดนามิก:

// we want to use "this" variable in embedded functions

function xyz(){
  // "this" is different here!
  console.log(this); // not what we wanted!
  function qwe(){
    // "this" is different here too!
    console.log(this); // not what we wanted!
  }
  ...
};

พวกเราทำอะไรได้บ้าง? กำหนดให้กับตัวแปรและใช้ผ่านนามแฝง:

var abc = this; // we want to use this variable in embedded functions

function xyz(){
  // "this" is different here! --- but we don't care!
  console.log(abc); // now it is the right object!
  function qwe(){
    // "this" is different here too! --- but we don't care!
    console.log(abc); // it is the right object here too!
  }
  ...
};

thisไม่ซ้ำกันในส่วนนี้: argumentsเป็นตัวแปรหลอกอื่น ๆ ที่ควรได้รับการปฏิบัติเช่นเดียวกัน - โดยใช้นามแฝง


7
คุณควรผูก "นี้" เพื่อเรียกใช้ฟังก์ชันและไม่ผ่านมันไปรอบ ๆ หรือตัวแปรขวางขอบเขตกับมัน
ไมเคิล

10
@ ไมเคิลทำไมเป็นอย่างนั้น?
Chris Anderson

@ ไมเคิลคุณอาจทำงานใน
ไพพ์

@SkanderJenhani ที่แสดงความคิดเห็นคือ 8 ปี คำตอบของฉันจะแตกต่างกันมากในวันนี้
ไมเคิล

18

ใช่นี่เป็นมาตรฐานทั่วไป โคเดอร์บางตัวใช้ตัวเองส่วนคนอื่นใช้ฉัน มันใช้เป็นข้อมูลอ้างอิงกลับไปที่วัตถุ "ของจริง" ซึ่งตรงข้ามกับเหตุการณ์

มันเป็นอะไรบางอย่างที่ทำให้ฉันต้องใช้เวลาสักครู่กว่าจะได้มาจริง ๆ มันดูแปลก ๆ ในตอนแรก

ฉันมักจะทำสิ่งนี้ที่ด้านบนสุดของวัตถุของฉัน (ขอโทษรหัสสาธิต - มันเป็นแนวคิดมากกว่าสิ่งอื่นใดและไม่ใช่บทเรียนเกี่ยวกับเทคนิคการเขียนโค้ดที่ยอดเยี่ยม):

function MyObject(){
  var me = this;

  //Events
  Click = onClick; //Allows user to override onClick event with their own

  //Event Handlers
  onClick = function(args){
    me.MyProperty = args; //Reference me, referencing this refers to onClick
    ...
    //Do other stuff
  }
}

12
var functionX = function() {
  var self = this;
  var functionY = function(y) {
    // If we call "this" in here, we get a reference to functionY,
    // but if we call "self" (defined earlier), we get a reference to function X.
  }
}

แก้ไข: ทั้งๆที่ฟังก์ชั่นที่ซ้อนกันภายในวัตถุจะใช้เวลาในวัตถุหน้าต่างทั่วโลกมากกว่าวัตถุที่อยู่โดยรอบ


คุณดูเหมือนจะอธิบายสิ่งที่var self = thisทำ แต่นั่นไม่ใช่สิ่งที่คำถามกำลังถาม (คำถามกำลังถามว่าเป็นวิธีที่ดีที่สุดในการแก้ไขปัญหา) หรือไม่
Quentin

3
"ปัญหาหลักคือวิธี" เปลี่ยน "ตัวแปรในฟังก์ชันฝังตัว" ฉันเห็นด้วยกับข้อความนี้ในคำตอบที่ยอมรับ นอกจากนั้นไม่มีอะไรผิดปกติกับการฝึกซ้อม เป็นเรื่องธรรมดา ดังนั้นเป็นคำตอบของ "รหัสดูแปลกสำหรับฉัน" ส่วนฉันเลือกที่จะอธิบายวิธีการทำงาน ฉันเชื่อว่าเหตุผลที่ "รหัสดูแปลกสำหรับฉัน" มาจากความสับสนของวิธีการทำงาน
serkan

12

หากคุณกำลังทำ ES2015 หรือกำลังทำสคริปต์ประเภทและ ES5 คุณสามารถใช้ฟังก์ชั่นลูกศรในรหัสของคุณและคุณไม่ต้องเผชิญกับข้อผิดพลาดและสิ่งนี้อ้างอิงถึงขอบเขตที่คุณต้องการในอินสแตนซ์ของคุณ

this.name = 'test'
myObject.doSomething(data => {
  console.log(this.name)  // this should print out 'test'
});

5
ตามคำอธิบาย: ในลูกศร ES2015 ฟังก์ชั่นจับภาพthisจากขอบเขตการกำหนดของพวกเขา นิยามฟังก์ชั่นปกติไม่ได้ทำอย่างนั้น
defnull

@defnull ฉันไม่คิดว่ามันจะมีอะไรพิเศษ ฟังก์ชั่นสร้างขอบเขตบล็อก ดังนั้นการใช้เครื่องหมายลูกศรแทนฟังก์ชั่นช่วยให้คุณไม่สร้างขอบเขตดังนั้นสิ่งนี้จะยังคงอ้างอิงถึงตัวแปรภายนอกนี้
Nimeshka Srimal

4

วิธีการหนึ่งในการทำเช่นนี้คือการผูกการโทรกลับไปยังวัตถุของคุณทั้งหมดด้วยbindวิธีของจาวาสคริปต์

คุณสามารถทำได้ด้วยวิธีการตั้งชื่อ

function MyNamedMethod() {
  // You can now call methods on "this" here 
}

doCallBack(MyNamedMethod.bind(this)); 

หรือโทรกลับโดยไม่ระบุชื่อ

doCallBack(function () {
  // You can now call methods on "this" here
}.bind(this));

การทำสิ่งเหล่านี้แทนที่จะใช้เพื่อvar self = thisแสดงให้คุณเข้าใจว่าการเชื่อมโยงของthisพฤติกรรมในจาวาสคริปต์และไม่พึ่งพาการอ้างอิงการปิด

นอกจากนี้ตัวดำเนินการลูกศรไขมันใน ES6 นั้นก็เหมือนกับการเรียก.bind(this)ฟังก์ชันที่ไม่ระบุชื่อ:

doCallback( () => {
  // You can reference "this" here now
});

โดยส่วนตัวฉันคิดว่า. ผูก (นี่) เป็นเพียงการพิมพ์มากขึ้นเปลี่ยนการอ้างอิงนี้ให้ฉัน (หรืออะไรก็ตาม) แล้วใช้มันแทนทำให้รหัสสะอาด ลูกศรอ้วนเป็นอนาคต
davidbuttar

3

ฉันไม่ได้ใช้ jQuery แต่ในไลบรารีอย่าง Prototype คุณสามารถผูกฟังก์ชันกับขอบเขตเฉพาะได้ ดังนั้นในใจรหัสของคุณจะเป็นดังนี้:

 $('#foobar').ready('click', this.doSomething.bind(this));

เมธอด bind ส่งคืนฟังก์ชันใหม่ที่เรียกเมธอดดั้งเดิมด้วยขอบเขตที่คุณระบุ


คล้ายกับ: $('#foobar').ready('click', this.doSomething.call(this));ใช่ไหม
Muers

bindวิธีการไม่ทำงานในเบราว์เซอร์รุ่นเก่าจึงใช้$.proxyวิธีการแทน
Guffa


1

ฉันคิดว่ามันขึ้นอยู่กับว่าคุณจะทำอะไรในdoSomethingหน้าที่คุณ หากคุณจะเข้าถึงMyObjectคุณสมบัติโดยใช้คีย์เวิร์ดนี้คุณต้องใช้มัน แต่ฉันคิดว่าส่วนรหัสต่อไปนี้จะใช้งานได้หากคุณไม่ได้ทำสิ่งพิเศษโดยใช้object(MyObject)คุณสมบัติ

function doSomething(){
  .........
}

$("#foobar").ready('click', function(){

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