Untuk memahami hal ini berdasarkan di mana ini berada, situasinya dapat dibagi secara kasar menjadi tiga jenis:
1. Dalam fungsi: Ini biasanya merupakan parameter implisit.
2. Di luar fungsi (dalam lingkup teratas): di browser, ini mengacu pada objek global; di Node.js mengacu pada ekspor modul.
3. String diteruskan ke eval (): jika eval () dipanggil secara langsung, ini mengacu pada objek saat ini; Jika eval () disebut secara tidak langsung, ini mengacu pada objek global.
Kami telah melakukan tes yang sesuai untuk kategori ini:
1. Ini dalam fungsi
Fungsi pada dasarnya dapat mewakili semua struktur yang dipanggil dalam JS, jadi ini juga merupakan skenario yang paling umum di mana ini digunakan, dan fungsi dapat dibagi lagi menjadi tiga peran berikut:
Fungsi nyata
Konstruktor
metode
1.1 Ini dalam fungsi nyata
Dalam fungsi nyata, nilai ini adalah pola yang tergantung pada konteks di mana ia berada.
Mode ceroboh: Ini mengacu pada objek global (jendela di browser).
Salinan kode adalah sebagai berikut:
fungsi sloppyfunc () {
console.log (ini === Jendela); // BENAR
}
sloppyfunc ();
Mode ketat: Nilai ini tidak ditentukan.
Salinan kode adalah sebagai berikut:
fungsi strictfunc () {
'Gunakan ketat';
console.log (ini === tidak ditentukan); // BENAR
}
strictfunc ();
Ini adalah parameter implisit suatu fungsi, sehingga nilainya selalu sama. Namun, Anda dapat mendefinisikan nilai ini dengan menggunakan metode panggilan () atau terapkan () untuk menampilkan nilai.
Salinan kode adalah sebagai berikut:
fungsi fungsi (arg1, arg2) {
Console.log (ini); // 1
console.log (arg1); // 2
console.log (arg2); // 3
}
func.Call (1, 2, 3); // (ini, arg1, arg2)
func.Apply (1, [2, 3]); // (ini, arraywithargs)
1.2 Ini di konstruktor
Anda dapat menggunakan fungsi sebagai konstruktor melalui yang baru. Operasi baru menciptakan objek baru dan meneruskan objek ini ke dalam konstruktor melalui ini.
Salinan kode adalah sebagai berikut:
var menyimpan ini;
function constr () {
savedThis = ini;
}
var inst = new constr ();
console.log (savedThis === inst); // BENAR
Prinsip implementasi operasi baru di JS secara kasar ditampilkan dalam kode berikut (lihat di sini untuk implementasi yang lebih akurat, implementasi ini juga lebih rumit):
Salinan kode adalah sebagai berikut:
function newoperator (constr, arraywithargs) {
var thisvalue = object.create (constr.prototype);
Constr.Apply (nilai ini, arraywithargs);
mengembalikan nilai ini;
}
1.3 Ini dalam metode ini
Dalam metode, penggunaan ini cenderung lebih dalam bahasa tradisional yang berorientasi objek: penerima ditunjuk oleh ini, yaitu, objek yang berisi metode ini.
Salinan kode adalah sebagai berikut:
var obj = {
Metode: function () {
Console.log (ini === OBJ); // BENAR
}
}
obj.method ();
2. Ini dalam ruang lingkup
Di browser, ruang lingkup adalah ruang lingkup global, dan ini mengacu pada objek global ini (seperti jendela):
Salinan kode adalah sebagai berikut:
<script>
console.log (ini === Jendela); // BENAR
</script>
Di Node.js, Anda biasanya menjalankan fungsi dalam modul. Oleh karena itu, ruang lingkup tingkat atas adalah ruang lingkup modul yang sangat istimewa:
Salinan kode adalah sebagai berikut:
// `global` (bukan` window`) Lihat objek global:
console.log (matematika === global.math); // BENAR
// `this` tidak merujuk ke objek global:
console.log (this! == global); // BENAR
// `ini` mengacu pada ekspor modul:
console.log (this === Module.Exports); // BENAR
3. Ini di eval ()
eval () dapat dipanggil secara langsung (dengan memanggil nama fungsi 'eval') atau secara tidak langsung (dengan cara lain, seperti panggilan ()). Untuk detail lebih lanjut, silakan lihat di sini.
Salinan kode adalah sebagai berikut:
// Fungsi Nyata
fungsi sloppyfunc () {
console.log (eval ('this') === jendela); // BENAR
}
sloppyfunc ();
fungsi strictfunc () {
'Gunakan ketat';
console.log (eval ('this') === tidak terdefinisi); // BENAR
}
strictfunc ();
// Konstruktor
var menyimpan ini;
function constr () {
savedThis = eval ('this');
}
var inst = new constr ();
console.log (savedThis === inst); // BENAR
// metode
var obj = {
Metode: function () {
console.log (eval ('this') === obj); // BENAR
}
}
obj.method ();
4. Perangkap yang terkait dengan ini
Anda harus berhati -hati tentang 3 perangkap yang terkait dengan ini yang akan diperkenalkan di bawah ini. Perhatikan bahwa dalam contoh berikut, menggunakan mode ketat dapat meningkatkan keamanan kode. Karena dalam fungsi nyata, nilai ini tidak ditentukan, Anda akan mendapatkan peringatan ketika ada yang salah.
4.1 Lupa menggunakan yang baru
Jika Anda tidak menggunakan yang baru untuk memanggil konstruktor, Anda sebenarnya menggunakan fungsi nyata. Jadi ini tidak akan menjadi nilai yang Anda harapkan. Dalam mode ceroboh, ini menunjuk ke jendela dan Anda akan membuat variabel global:
Salinan kode adalah sebagai berikut:
titik fungsi (x, y) {
this.x = x;
this.y = y;
}
var p = titik (7, 5); // kita lupa baru!
console.log (p === tidak ditentukan); // BENAR
// Variabel global telah dibuat:
console.log (x); // 7
console.log (y); // 5
Namun, jika Anda menggunakan mode yang ketat, Anda masih akan mendapatkan peringatan (ini === tidak ditentukan):
Salinan kode adalah sebagai berikut:
titik fungsi (x, y) {
'Gunakan ketat';
this.x = x;
this.y = y;
}
var p = titik (7, 5);
// typeError: tidak dapat mengatur properti 'x' dari tidak terdefinisi
4.2 Metode penggunaan yang tidak pantas
Jika Anda secara langsung mendapatkan nilai suatu metode (tidak menyebutnya), Anda menggunakan metode ini sebagai fungsi. Ketika Anda ingin meneruskan metode sebagai parameter ke dalam fungsi atau metode panggilan, Anda kemungkinan besar akan melakukan ini. Ini adalah kasus dengan penangan setimeout () dan pendaftaran. Saya akan menggunakan metode callit () untuk mensimulasikan skenario ini:
Salinan kode adalah sebagai berikut:
/** mirip dengan setTimeout () dan setimmediate ()*/
function callit (func) {
func ();
}
Jika Anda menyebut metode sebagai fungsi dalam mode ceroboh, * ini * menunjuk ke objek global, sehingga yang dibuat selanjutnya akan menjadi variabel global.
Salinan kode adalah sebagai berikut:
var counter = {
Hitung: 0,
// metode mode ceroboh
Inc: function () {
this.count ++;
}
}
callit (counter.inc);
// tidak berhasil:
console.log (counter.count); // 0
// sebagai gantinya, variabel global telah dibuat
// (NAN adalah hasil dari penerapan ++ ke tidak ditentukan):
Console.log (Count); // nan
Jika Anda melakukan ini dalam mode yang ketat, ini tidak ditentukan dan Anda masih tidak akan mendapatkan hasil yang diinginkan, tetapi setidaknya Anda akan mendapatkan peringatan:
Salinan kode adalah sebagai berikut:
var counter = {
Hitung: 0,
// Metode mode ketat
Inc: function () {
'Gunakan ketat';
this.count ++;
}
}
callit (counter.inc);
// typeError: tidak dapat membaca 'jumlah' properti yang tidak ditentukan
console.log (counter.count);
Untuk mendapatkan hasil yang diharapkan, Anda dapat menggunakan bind ():
Salinan kode adalah sebagai berikut:
var counter = {
Hitung: 0,
Inc: function () {
this.count ++;
}
}
callit (counter.inc.bind (counter));
// itu berhasil!
console.log (counter.count); // 1
Bind () menciptakan fungsi lain yang selalu dapat mengatur nilai ini untuk dikuatkan.
4.3 Sembunyikan ini
Ketika Anda menggunakan fungsi dalam metode, Anda sering mengabaikan fungsi memiliki ini sendiri. Ini berbeda dari metode ini, jadi Anda tidak dapat mencampur keduanya bersama -sama. Untuk detailnya, silakan lihat kode berikut:
Salinan kode adalah sebagai berikut:
var obj = {
Nama: 'Jane',
Teman: ['Tarzan', 'Cheeta'],
loop: function () {
'Gunakan ketat';
this.friends.foreach (
function (teman) {
console.log (this.name+'tahu'+teman);
}
);
}
};
obj.loop ();
// typeError: tidak dapat membaca 'nama' properti yang tidak ditentukan
This.name Dalam fungsi dalam contoh di atas tidak dapat digunakan karena nilai fungsi ini tidak terdefinisi, yang berbeda dari ini dalam loop metode (). Berikut ini adalah tiga ide untuk menyelesaikan masalah ini:
1. Itu = ini, tetapkan ini ke variabel, sehingga ini secara eksplisit dimanifestasikan (kecuali bahwa, diri juga merupakan nama variabel yang sangat umum digunakan untuk menyimpan ini), dan kemudian menggunakan variabel itu:
Salinan kode adalah sebagai berikut:
loop: function () {
'Gunakan ketat';
var itu = ini;
this.friends.foreach (function (friend) {
console.log (that.name+'pengetahuan'+teman);
});
}
2. Bind (). Gunakan bind () untuk membuat fungsi. Fungsi ini selalu berisi nilai yang ingin Anda lewati (dalam contoh berikut, metode ini):
Salinan kode adalah sebagai berikut:
loop: function () {
'Gunakan ketat';
this.friends.foreach (function (friend) {
console.log (this.name+'tahu'+teman);
} .bind (ini));
}
3. Gunakan parameter kedua Foreach. Parameter kedua Foreach akan diteruskan ke fungsi callback dan digunakan sebagai fungsi callback ini.
Salinan kode adalah sebagai berikut:
loop: function () {
'Gunakan ketat';
this.friends.foreach (function (friend) {
console.log (this.name+'tahu'+teman);
}, ini);
}
5. Praktik terbaik
Secara teori, saya pikir fungsi sebenarnya bukan miliknya sendiri, dan solusi di atas juga didasarkan pada ide ini. Ecmascript 6 menggunakan fungsi panah untuk mencapai efek ini, yang merupakan fungsi yang tidak memiliki ini sendiri. Dalam fungsi seperti itu Anda dapat menggunakan ini sesuka hati, tanpa khawatir apakah ada keberadaan implisit.
Salinan kode adalah sebagai berikut:
loop: function () {
'Gunakan ketat';
// Parameter foreach () adalah fungsi panah
this.friends.foreach (friend => {
// `ini adalah loop's` this`
console.log (this.name+'tahu'+teman);
});
}
Saya tidak suka beberapa API menganggap ini sebagai parameter tambahan untuk fungsi nyata:
Salinan kode adalah sebagai berikut:
sebelum kembali (function () {
this.addmatchers ({
TobeinRange: function (start, end) {
...
}
});
});
Menulis parameter implisit seperti yang diteruskan secara eksplisit, kode akan tampak lebih baik untuk dipahami, dan ini konsisten dengan persyaratan fungsi panah:
Salinan kode adalah sebagai berikut:
sebelum kembali (API => {
api.addmatchers ({
TobeinRange (Mulai, Akhiri) {
...
}
});
});