Javascript penunjuk "ini" dalam fungsi bertingkat

94

Saya punya pertanyaan tentang bagaimana pointer "ini" diperlakukan dalam skenario fungsi bersarang.

Katakanlah saya memasukkan kode contoh berikut ini ke dalam halaman web. Saya mendapatkan pesan kesalahan saat memanggil fungsi bersarang "doSomeEffects ()". Saya memeriksa di Firebug dan ini menunjukkan bahwa ketika saya berada dalam fungsi bersarang itu, penunjuk "ini" sebenarnya menunjuk ke objek "jendela" global - yang tidak saya duga. Saya tidak harus memahami sesuatu dengan benar karena saya pikir karena saya mendeklarasikan fungsi bersarang dalam fungsi objek, itu harus memiliki ruang lingkup "lokal" dalam hubungannya dengan fungsi (yaitu, penunjuk "ini" akan merujuk ke objek itu sendiri seperti bagaimana di pernyataan "jika" pertama saya).

Setiap petunjuk (tidak ada permainan kata-kata) akan dihargai.

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();
JoJoeDad
sumber
Apa sebenarnya pertanyaan Anda?
Sarfraz
2
Saat digunakan di dalam suatu fungsi, thismengacu pada objek tempat fungsi tersebut dipanggil.
kira
8
Apa yang bisa Anda lakukan di lingkup luar adalah sesuatu seperti var self = this;dan kemudian merujuk ke selffungsi dalam melalui closure.
Kai
1
doSomeEffectstidak terkait dengan objek apa pun secara khusus, sehingga thisdiasumsikan sebagai jendela, induk dari semua elemen.
kira
3
@JoJoeDad Bagaimana saya secara diplomatis mengatakan ini? Tetapi jawaban yang diberikan oleh chuckj di bawah ini sejauh ini merupakan jawaban atas pertanyaan Anda. Untuk benar-benar memahami apa yang sedang terjadi, Anda harus membaca konteks eksekusi , rantai cakupan , dan kata kunci ini . Dan dari tampilan beberapa jawaban di sini, orang lain juga harus membacanya. Saya mencoba menginjili javascript yang baik. Itulah mengapa saya meluangkan waktu untuk memberikan tautan ini.
kaki akan

Jawaban:

120

Dalam JavaScript, thisobjek benar-benar didasarkan pada cara Anda membuat panggilan fungsi.

Secara umum ada tiga cara untuk mengatur thisobjek:

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

Dalam semua contoh di atas, thisobjek akan menjadi someThing. Memanggil fungsi tanpa objek induk terkemuka biasanya akan memberi Anda objek global yang di sebagian besar browser berarti windowobjek tersebut.

KylePDavis
sumber
Saya mengubah kode menjadi this.doSomeEffects();berdasarkan jawaban Anda tetapi tetap saja tidak berhasil. Mengapa?
Arashsoft
1
@Arashsoft thisdalam this.doSomeEffects()menunjuk ke std_obj. Seperti yang dijelaskan pada jawaban di atas jika suatu fungsi tidak memiliki referensi objek maka itu diperlukan thisuntuk menjadi objek jendela.
Shivam
40

Karena ini tampaknya menjadi salah satu pertanyaan yang paling disukai dari jenisnya, izinkan saya menambahkan, setelah bertahun-tahun ini, solusi ES6 menggunakan fungsi panah:

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") { }
    };
    ...    
  }
};

sumber
6
Nah, itu kemajuan!
Joshua Pinter
Solusi yang sangat baik, sederhana.
Joshua Schlichting
Bagaimana saya tidak tahu selama 4 tahun terakhir bahwa ini adalah sesuatu.
xorinzor
32

thisbukan bagian dari lingkup closure, itu bisa dianggap sebagai parameter tambahan untuk fungsi yang terikat di situs panggilan. Jika metode tidak dipanggil sebagai metode maka objek global akan diteruskan sebagai this. Di browser, objek global identik dengan window. Misalnya, perhatikan fungsi berikut,

function someFunction() {
}

dan objek berikut,

var obj = { someFunction: someFunction };

Jika Anda memanggil fungsi menggunakan sintaks metode seperti,

obj.someFunciton();

kemudian thisterikatobj .

Jika Anda memanggil someFunction () secara langsung, seperti,

someFunction();

kemudian thisterikat ke objek global, yaitu window.

Penyelesaian yang paling umum adalah menangkap ini ke dalam closure seperti,

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();         
 }      
chuckj
sumber
itu = ini sempurna
Nather Webber
10

Ada perbedaan antara variabel enklosur dan "ini". "ini" sebenarnya ditentukan oleh pemanggil fungsi, sementara variabel eksplisit tetap utuh di dalam blok deklarasi fungsi yang dikenal sebagai enklosur. Lihat contoh di bawah ini:

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

Anda dapat mencobanya di sini: http://jsfiddle.net/kSTBy/

Apa yang terjadi dalam fungsi Anda adalah "doSomeEffects ()", dipanggil secara eksplisit, ini berarti konteks atau fungsi "ini" adalah jendela. jika "doSomeEffects" adalah metode prototipe misalnya this.doSomeEffects pada kata "myObject", maka myObject.doSomeEffects () akan menyebabkan "ini" menjadi "myObject".

Shane
sumber
4
bagi yang malas dan tidak sabar, demo ini mencatat:_this.name = myFirstObject this.name = mySecondObject
ptim
9

Untuk memahami pertanyaan ini, coba dapatkan keluaran untuk cuplikan berikut

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

Kode di atas akan menampilkan yang berikut ke konsol:

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

Di fungsi luar, baik this maupun self merujuk ke myObject dan oleh karena itu keduanya dapat mereferensikan dan mengakses foo dengan benar.

Namun, dalam fungsi bagian dalam, ini tidak lagi mengacu pada myObject. Akibatnya, this.foo tidak terdefinisi di fungsi dalam, sedangkan referensi ke variabel lokal itu sendiri tetap dalam cakupan dan dapat diakses di sana. (Sebelum ECMA 5, ini di fungsi bagian dalam akan merujuk ke objek jendela global; sedangkan, pada ECMA 5, ini di fungsi bagian dalam tidak akan ditentukan.)

ronakshah725
sumber
1
Dalam fungsi bagian dalam, thismengacu pada objek jendela (dalam lingkungan browser) atau objek GLOBAL (dalam lingkungan node.js)
raneshu
2
Mengapa itu terjadi?
setu
@raneshu "gunakan ketat" dan thistidak lagi mengacu pada jendela
rofrol
3

Seperti yang dijelaskan oleh Kyle, Anda dapat menggunakan callatau applymenentukan thisdi dalam fungsi:

Inilah konsep yang diterapkan pada kode Anda:

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

Hari Pachuveetil
sumber
0

Saya juga mendapat peringatan " Akses referensi yang berpotensi tidak valid ke bidang kelas melalui ini "

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
CodeToLife
sumber
0

Karena tidak disebutkan, saya akan menyebutkan bahwa menggunakan .bind()adalah solusi -


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

    std_obj.displayMe();

Berikut adalah contoh yang lebih sederhana -

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'

Memang benar bahwa menggunakan fungsi panah =>terlihat lebih licin dan mudah bagi programmer. Namun, harus diingat bahwa ruang lingkup leksikal mungkin memerlukan lebih banyak pekerjaan dalam hal pemrosesan dan memori untuk menyiapkan dan mempertahankan ruang lingkup leksikal tersebut, dibandingkan dengan sekadar mengaitkan fungsi thisdengan penunjuk melalui .bind().

Bagian dari manfaat pengembangan kelas di JS adalah menyediakan metode untuk membuat thislebih andal hadir dan tersedia, untuk menjauh dari pemrograman fungsional dan cakupan leksikal, dan dengan demikian mengurangi overhead.

Dari MDN

Pertimbangan kinerja Tidaklah bijaksana untuk membuat fungsi dalam fungsi lain secara tidak perlu jika closure tidak diperlukan untuk tugas tertentu, karena akan berdampak negatif pada kinerja skrip baik dalam hal kecepatan pemrosesan dan konsumsi memori.

Craig Hicks
sumber