ความแตกต่างระหว่างสิ่งนี้กับตัวเองใน JavaScript


118

ทุกคนรับรู้thisในจาวาสคริปต์ แต่ก็มีบางกรณีที่selfพบในป่าเช่นที่นี่

ดังนั้นความแตกต่างระหว่างthisและselfใน JavaScript คืออะไร?



8
@dystroy: มีหนึ่ง: window.self( === window). แม้ว่า OP อาจหมายถึงชื่อตัวแปรเล็กน้อย ...
Bergi

2
@dystroy: อันที่จริงฉันไม่คิดว่าเขาจะหมายถึงมันจริงๆ แต่จริงๆแล้วในขอบเขตทั่วโลก (และสภาพแวดล้อมของเบราว์เซอร์) this === selfเป็นเรื่องจริง :-)
Bergi

2
อัตนัยกัน: aliasing thisจะselfไม่ใช่การปฏิบัติที่ดีในปัจจุบันเมื่อมันเป็นเรื่องธรรมดาที่จะมีรหัสที่มีจำนวนมาก (ดีมากกว่าหนึ่งไม่ดีพอ) ระดับของการเรียกกลับรังเป็นผลมาจากการเขียนโปรแกรมไม่ตรงกัน ใช้ชื่อที่สื่อความหมายแทน การพูดชื่อthisอย่างเป็นกลางนั้นไม่มีข้อมูลและเป็นเพียงการเลือกชื่อที่ไม่บาดหูเนื่องจากบริบทคำศัพท์ของนิยามคลาสทำให้คุณสมบัตินั้น
มิลลิโมส

2
นี่เป็นคำถามที่ถูกต้องและมีประโยชน์ควรเปิดอีกครั้ง
danza

คำตอบ:


127

เว้นแต่จะตั้งค่าอื่น ๆ ค่าของselfเป็นwindowเพราะJavaScriptช่วยให้คุณเข้าถึงทรัพย์สินใด ๆxของการwindowเป็นแค่แทนx window.xดังนั้นจึงselfเป็นจริงwindow.selfซึ่งแตกต่างthisจาก

window.self === window; // true

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

function foo() {
    console.log(
        window.self === window, // is self window?
        window.self === this,   // is self this?
        this === window         // is this window?
    );
}
foo(); // true true true

หากคุณกำลังใช้ฟังก์ชันในบริบทอื่นthisจะอ้างถึงบริบทนั้น แต่selfจะยังคงเป็นwindowเช่นนั้น

// invoke foo with context {}
foo.call({}); // true false false

คุณสามารถหาwindow.selfที่กำหนดไว้ในW3C 2006 ร่างการทำงานสำหรับวัตถุหน้าต่าง ที่นี่


34
เพื่อความสมบูรณ์selfมีประโยชน์ในบริบทของ WebWorker เมื่อไม่สามารถเข้าถึงหน้าต่างได้ ( developer.mozilla.org/en-US/docs/Web/Guide/Performance/… ) การใช้selfแทนwindowช่วยให้คุณเข้าถึงวัตถุส่วนกลางในแบบพกพา
lqc

24

เพิ่มเติมเล็กน้อยเนื่องจากผู้คนอาจพบสิ่งนี้ในบริบทของพนักงานบริการซึ่งในกรณีนี้มีความหมายแตกต่างกันเล็กน้อย

คุณอาจเห็นสิ่งนี้ในโมดูลผู้ปฏิบัติงานบริการ:

self.addEventListener('install', function(e) {
  console.log('[ServiceWorker] Install');
});

ในที่นี้หมายถึงตัวเอง WorkerGlobalScope และนี่คือวิธีมาตรฐานในการตั้งค่าผู้ฟังเหตุการณ์

จากMozilla docs :

ด้วยการใช้ self คุณสามารถอ้างถึงขอบเขตส่วนกลางในลักษณะที่ไม่เพียง แต่จะทำงานในบริบทของหน้าต่าง (ตัวเองจะแก้ไขเป็น window.self) แต่ยังอยู่ในบริบทของผู้ปฏิบัติงานด้วย (จากนั้นตนเองจะแก้ไขเป็น WorkerGlobalScope.self)


ขอบคุณ! ฉันกำลังมองหาคำตอบนี้ :)
agpt

23

แม้ว่าฉันจะมาสาย แต่ฉันเจอตัวอย่างหนึ่งที่จะเป็นประโยชน์ในการทำความเข้าใจthisเพิ่มเติม:

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();

O / P

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

ก่อน ECMA 5 thisในฟังก์ชันภายในจะอ้างถึงวัตถุหน้าต่างส่วนกลาง ในขณะที่ ECMA 5 thisในฟังก์ชันภายในจะไม่ได้กำหนดไว้


สิ่งนี้ถูกกำหนดไว้ในบริบทเสมอ สิ่งที่ไม่ได้กำหนดคือ this.foo. นั่นคือความแตกต่างอย่างมากและเพื่อให้บรรลุพฤติกรรมที่คุณกล่าวถึงก่อน ECMA 5 คุณสามารถใช้ฟังก์ชันลูกศรหรือตามที่คุณระบุกำหนดให้ตัวเองอยู่นอกฟังก์ชันภายในและใช้ตัวเองภายในแทนวิธีนี้วิธีที่สะอาดกว่าคือฟังก์ชันลูกศร .
Dejazmach

2

การอ้างอิงถึง ECMA 5 จำเป็นต้องมีการชี้แจง

ฉันคิดว่ามันหมายถึง ECMA-262 Edition 5 ควรสังเกตว่า ECMA-262 (aka ECMAScript หรือ Javascript ที่ถูกต้องน้อยกว่า) เป็นภาษาสคริปต์ทั่วไปที่ถูกนำไปใช้ในอินเทอร์เน็ตเบราว์เซอร์ จากมาตรฐาน Edition 5.1:

ขั้นตอนต่อไปนี้จะดำเนินการเมื่อการควบคุมเข้าสู่บริบทการดำเนินการสำหรับโค้ดฟังก์ชันที่มีอยู่ในอ็อบเจ็กต์ฟังก์ชัน F ผู้เรียกจัดเตรียม thisArg และผู้เรียกให้ argumentsList:

  1. หากรหัสฟังก์ชันเป็นรหัสที่เข้มงวดให้ตั้งค่า ThisBinding เป็น thisArg
  2. ไม่เช่นนั้นหาก thisArg เป็นโมฆะหรือไม่ได้กำหนดให้ตั้งค่า ThisBinding เป็นวัตถุส่วนกลาง
  3. หาก Type (thisArg) ไม่ใช่ Object ให้ตั้งค่า ThisBinding เป็น ToObject (thisArg)
  4. อื่นตั้งค่า ThisBinding เป็น thisArg
  5. ... (ไม่เกี่ยวกับ "นี้")

คำว่า "global object" หมายถึงวัตถุใด ๆ ก็ตามที่อยู่บนสุดของห่วงโซ่ขอบเขต สำหรับเบราว์เซอร์สิ่งนี้จะเป็นอ็อบเจ็กต์ "หน้าต่าง" แต่นี่เป็นทางเลือกในการใช้งาน (โฮสต์สคริปต์ Windows มีอ็อบเจ็กต์ส่วนกลางที่มองไม่เห็น แต่ไม่มีโหมดที่เข้มงวดดังนั้นการอ้างอิงที่ไม่มีเงื่อนไขจึงเข้าถึงคุณสมบัติของมันและไม่มี "ตัวเอง" ทั่วโลก) นอกจากนี้ต้องเปิดใช้งาน "โหมดเข้มงวด" อย่างชัดเจนมิฉะนั้นจะไม่ทำงาน (มาตรา 14.1 ของมาตรฐาน) ด้วยเหตุนี้ "this" ที่ไม่ได้กำหนดจะยังคงแก้ไขให้กับวัตถุส่วนกลาง (หน้าต่าง) ใน "ECMA 5" ที่โหมดเข้มงวดไม่ทำงาน

ดังนั้นคำตอบสำหรับคำถามคือ:

"this" หมายถึงวัตถุที่เรียกใช้ฟังก์ชันเสมอ หากอ็อบเจ็กต์ไม่ได้เรียกใช้ฟังก์ชัน (เช่นไม่ใช่การเรียกใช้เมธอด) ดังนั้น "this" (ที่ส่งผ่านไปยังฟังก์ชัน) จะเป็น "ไม่ได้กำหนด" อย่างไรก็ตามหากไม่ใช้โหมดเข้มงวด "this" ที่ไม่ได้กำหนดจะถูกตั้งค่าเป็นวัตถุส่วนกลาง (กฎ 2 ด้านบน)

"ตัวเอง" ไม่มีความหมายทางวากยสัมพันธ์พิเศษเป็นเพียงตัวระบุ เบราว์เซอร์มักจะกำหนด window.self (เป็นเพียงคุณสมบัติของ global window object) = window ส่งผลให้มีการอ้างถึง "ตัวตน" อย่างไม่มีเงื่อนไขเหมือนกับ "หน้าต่าง" UNLESS "self" ได้รับการกำหนดใหม่ภายในขอบเขตที่ปิดล้อม (เช่น "var self = this;" ด้านบนขอให้โชคดีในการนิยาม "this" ใหม่)

ดังนั้นคำอธิบายทั้งหมดของตัวอย่างข้างต้นคือ:

outer func:  this.foo = bar
// "this" refers to the invoking object "myObject"
outer func:  self.foo = bar
// "self" resolves to the variable in the local scope which has been set to "this" so it is also "myObject"
inner func:  this.foo = undefined
// "this" refers to the invoking object (none) and so is replaced by the global object (strict mode must be off). "window" has no foo property so its "value" is undefined.
inner func:  self.foo = bar
// self resolves to the variable in the enclosing scope which is still "myObject"

รูปแบบที่น่าสนใจของตัวอย่างจะสร้างการปิดโดยส่งคืนการอ้างอิงไปยังฟังก์ชันภายใน

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);
    return function() {
        console.log("inner func:  this.foo = " + this.foo);
        console.log("inner func:  self.foo = " + self.foo);
    };
  }
};
var yourObject = {
 foo: "blat",
 func: myObject.func() // function call not function object
};
console.log("----");
yourObject.func();

การผลิต

outer func:  this.foo = bar
outer func:  self.foo = bar
----
inner func:  this.foo = blat
inner func:  self.foo = bar

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

ในการสรุปเนื้อหาโดยสรุป "this" ถูกกำหนดโดยมาตรฐานภาษา "self" ถูกกำหนดโดยผู้ใดก็ตามที่กำหนด (runtimeplementer หรือ end programmer)


0

ค้นหาชุดค่าผสมของคอนโซล 'window', 'self' และ 'this' ด้านล่างในขอบเขตส่วนกลาง (สภาพแวดล้อมของเบราว์เซอร์) เพื่อดูว่ามีการอ้างอิงถึงที่ใด

console.log( window ); // Window {…}
console.log( self );   // Window {…}
console.log( this );   // Window {…}

console.log( window.window ); // Window {…}
console.log( window.self );   // Window {…}
console.log( window.this );   // undefined  

console.log( self.self );     // Window {…}
console.log( self.window );   // Window {…}
console.log( self.this );     // undefined

console.log( this.this );     // undefined
console.log( this.window );   // Window {…}
console.log( this.self );     // Window {…}

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