Apa yang mendasari idiom JavaScript ini: var self = this?

357

Saya melihat yang berikut ini di sumber untuk WebKit HTML 5 SQL Storage Notes Demo :

function Note() {
  var self = this;

  var note = document.createElement('div');
  note.className = 'note';
  note.addEventListener('mousedown', function(e) { return self.onMouseDown(e) }, false);
  note.addEventListener('click', function() { return self.onNoteClick() }, false);
  this.note = note;
  // ...
}

Penulis menggunakan diri di beberapa tempat (badan fungsi) dan ini di tempat lain (badan fungsi yang didefinisikan dalam daftar argumen metode). Apa yang sedang terjadi? Sekarang setelah saya perhatikan sekali, akankah saya mulai melihatnya di mana-mana?

Thomas L Holaday
sumber
4
Ini adalah fitur bahasa JS yang disebut "lexical closure."
subZero
2
Kemungkinan rangkap: var self = ini? .
DavidRR
konsep INI dijelaskan secara eksplisit di sini scotch.io/@alZami/understanding-this-in-javascript
AL-zami
Contoh yang relevan dalam jawaban ini stackoverflow.com/a/20279485/5610569 (untuk pertanyaan "Bagaimana cara mengakses yang benar thisdi dalam panggilan balik?")
FluxLemur

Jawaban:

431

Lihat artikel ini di alistapart.com . (Ed: Artikel ini telah diperbarui sejak awalnya ditautkan)

selfsedang digunakan untuk mempertahankan referensi ke aslinya thisbahkan ketika konteksnya sedang berubah. Ini adalah teknik yang sering digunakan dalam event handler (terutama pada penutupan).

Sunting: Perhatikan bahwa menggunakan selfsekarang tidak disarankan karena window.selfada dan berpotensi menyebabkan kesalahan jika Anda tidak berhati-hati.

Apa yang Anda sebut variabel tidak terlalu penting. var that = this;baik-baik saja, tetapi tidak ada keajaiban tentang nama itu.

Fungsi yang dideklarasikan di dalam konteks (mis. Panggilan balik, penutupan) akan memiliki akses ke variabel / fungsi yang dideklarasikan dalam cakupan yang sama atau di atas.

Misalnya, panggilan balik acara sederhana:

function MyConstructor(options) {
  let that = this;

  this.someprop = options.someprop || 'defaultprop';

  document.addEventListener('click', (event) => {
    alert(that.someprop);
  });
}

new MyConstructor({
  someprop: "Hello World"
});

Jonathan Fingland
sumber
Muncul bahwa artikel berubah menjadi menggunakanvar that = this;
Bob Stein
@ BobStein Terima kasih. Saya akan memperbarui jawabannya.
Jonathan Fingland
96

Saya pikir nama variabel 'diri' tidak boleh digunakan dengan cara ini lagi, karena browser modern memberikan variabel global yangself menunjuk ke objek global baik dari jendela normal atau WebWorker.

Untuk menghindari kebingungan dan kemungkinan konflik, Anda dapat menulis var thiz = thisatau var that = thissebagai gantinya.

Duan Yao
sumber
43
Saya biasanya menggunakan_this
djheru
6
@djheru +1. jauh lebih bagus daripada " that" (yang tidak akan pernah terbiasa dengan otak saya).
o_o_o--
9
Saya mulai menggunakan "saya" :)
Mopparthy Ravindranath
3
Sampai browser modern mulai menyediakan variabel global _ini, itu, atau saya.
Beejor
10
Sama sekali tidak ada masalah dengan menggunakan nama selfselama Anda menyatakannya sebagai variable, itu akan membayangi global. Tentu saja jika Anda lupa varitu tidak akan bekerja dengan nama lain juga.
Bergi
34

Ya, Anda akan melihatnya di mana-mana. Itu sering that = this;.

Lihat bagaimana selfdigunakan di dalam fungsi yang dipanggil oleh acara? Mereka akan memiliki konteks mereka sendiri, sehingga selfdigunakan untuk menampung thisyang masuk Note().

Alasannya selfmasih tersedia untuk fungsi, meskipun mereka hanya dapat mengeksekusi setelah Note()fungsi selesai dieksekusi, adalah bahwa fungsi dalam mendapatkan konteks fungsi luar karena penutupan .

Nosredna
sumber
12
Bagi saya intinya adalah selftidak memiliki makna khusus. Saya pribadi lebih suka menggunakan var bernama sesuatu selain selfkarena sering membingungkan saya, karena saya berharap 'diri' menjadi kata yang dipesan. Jadi saya suka jawaban Anda. Dan dalam contoh OP, saya lebih suka var thisNote = thisatau mirip.
steve
@Eve setuju, meskipun saya mencoba untuk menghindari menggunakan referensi / diri ini secara umum karena mereka sangat rapuh dalam hal rawatan.
mattLummus
28

Perlu juga dicatat ada pola Proxy alternatif untuk mempertahankan referensi ke aslinya thisdalam panggilan balik jika Anda tidak menyukai var self = thisidiom tersebut.

Karena suatu fungsi dapat dipanggil dengan konteks yang diberikan dengan menggunakan function.applyatau function.call, Anda bisa menulis pembungkus yang mengembalikan fungsi yang memanggil fungsi Anda dengan applyatau callmenggunakan konteks yang diberikan. Lihat proxyfungsi jQuery untuk penerapan pola ini. Berikut ini adalah contoh menggunakannya:

var wrappedFunc = $.proxy(this.myFunc, this);

wrappedFunckemudian dapat dipanggil dan akan memiliki versi Anda thissebagai konteksnya.

Maks
sumber
10

Seperti yang telah dijelaskan orang lain, var self = this;izinkan kode dalam penutupan untuk merujuk kembali ke lingkup induk.

Namun, sekarang 2018 dan ES6 didukung secara luas oleh semua browser web utama. The var self = this;idiom tidak cukup penting seperti dulu.

Sekarang mungkin untuk menghindari var self = this;melalui penggunaan fungsi panah .

Dalam kasus di mana kita akan menggunakan var self = this:

function test() {
    var self = this;
    this.hello = "world";
    document.getElementById("test_btn").addEventListener("click", function() {
        console.log(self.hello); // logs "world"
    });
};

Kami sekarang dapat menggunakan fungsi panah tanpa var self = this:

function test() {
    this.hello = "world";
    document.getElementById("test_btn").addEventListener("click", () => {
        console.log(this.hello); // logs "world"
    });
};

Fungsi panah tidak memiliki fungsi mereka sendiri thisdan hanya mengasumsikan lingkup melampirkan.

Elliot B.
sumber
Atau - kaget, ngeri! - mengapa tidak melewatkan hal yang relevan sebagai argumen untuk fungsi Anda (penutupan)? Kenapa kamu merujuk keluar dari ruang lingkup keadaan, kenapa sih ada orang yang memprogram seperti ini? Tidak pernah ada alasan nyata untuk melakukan ini. Alih-alih, .addEventListender("click", (x) => { console.log(x); });Anda telah menjelaskan caranya dan mengapa dengan sangat jelas, dan saya setuju menggunakan fungsi panah lebih masuk akal, tapi tetap saja ... ini hanya pemrograman yang mengerikan, malas, berantakan.
Benjamin R
2
Dalam JavaScript, merujuk kembali ke ruang lingkup induk adalah sangat umum dan perlu. Ini adalah bagian mendasar dari bahasa. Saran Anda untuk meneruskan dalam lingkup induk sebagai argumen pada pengendali acara sebenarnya tidak mungkin. Juga, di ES6, fungsi panah menggunakan pelingkupan leksikal - 'ini' merujuk pada lingkup sekitarnya saat ini dan tidak lebih jauh - ini bukan "referensi keluar dari lingkup keadaan" atau semacamnya.
Elliot B.
9

Variabel ditangkap oleh fungsi sebaris yang ditentukan dalam metode. thisdalam fungsi akan merujuk ke objek lain. Dengan cara ini, Anda dapat membuat fungsi menyimpan referensi ke thisdalam lingkup luar.

Mehrdad Afshari
sumber
9

Ini adalah kekhasan JavaScript. Ketika suatu fungsi adalah properti dari suatu objek, lebih tepat disebut metode, ini mengacu pada objek. Dalam contoh event handler, objek yang berisi adalah elemen yang memicu event. Ketika fungsi standar dipanggil, ini akan merujuk ke objek global. Ketika Anda memiliki fungsi bersarang seperti dalam contoh Anda, ini tidak berhubungan dengan konteks fungsi luar sama sekali. Fungsi dalam memang berbagi ruang lingkup dengan fungsi yang berisi, sehingga pengembang akan menggunakan variasi var that = thisuntuk mempertahankan ini yang mereka butuhkan di fungsi dalam.

kombat
sumber
5

Sebenarnya diri adalah referensi ke jendela ( window.self) karena itu ketika Anda mengatakan var self = 'something'Anda menimpa referensi jendela ke dirinya sendiri - karena diri ada di objek jendela.

Inilah sebabnya mengapa sebagian besar pengembang lebih memilih var that = thislebihvar self = this;

Bagaimanapun; var that = this;tidak sejalan dengan praktik yang baik ... dengan anggapan bahwa kode Anda akan direvisi / dimodifikasi nanti oleh pengembang lain, Anda harus menggunakan standar pemrograman yang paling umum sehubungan dengan komunitas pengembang

Oleh karena itu Anda harus menggunakan sesuatu seperti var oldThis/ var oThis/ etc - untuk memperjelas ruang lingkup Anda // .. tidak terlalu banyak tetapi akan menghemat beberapa detik dan beberapa siklus otak

SorinN
sumber
2
@prior Saya pikir masuk akal sampai paragraf terakhir.
miphe
0

Seperti yang disebutkan beberapa kali di atas, 'diri' hanya digunakan untuk menyimpan referensi ke 'ini' sebelum memasuki fungsi. Sekali dalam fungsi 'ini' mengacu pada sesuatu yang lain.

Cyprien
sumber
@ JohnPaul ... ini memang memberikan jawaban. Itu mungkin bukan jawaban yang tepat, tetapi bagaimana Anda bisa mengatakan bahwa "ini digunakan untuk ..." bukan jawaban untuk "mengapa dia melakukan ini"?
GreenAsJade
-1
function Person(firstname, lastname) {
  this.firstname = firstname;

  this.lastname = lastname;
  this.getfullname = function () {
    return `${this.firstname}   ${this.lastname}`;
  };

  let that = this;
  this.sayHi = function() {
    console.log(`i am this , ${this.firstname}`);
    console.log(`i am that , ${that.firstname}`);
  };
}

let thisss = new Person('thatbetty', 'thatzhao');

let thatt = {firstname: 'thisbetty', lastname: 'thiszhao'};

thisss.sayHi.call (thatt);

ht zhao
sumber
1
Anda harus menambahkan beberapa penjelasan dengan kode yang Anda lakukan istimewa.
Farhana