İç içe geçmiş işlev içinde Javascript "bu" işaretçisi


96

İç içe geçmiş bir işlev senaryosunda "bu" göstericinin nasıl ele alındığı ile ilgili bir sorum var.

Aşağıdaki örnek kodu bir web sayfasına eklediğimi varsayalım. İç içe geçmiş işlevi "doSomeEffects ()" çağırdığımda bir hata alıyorum. Firebug'da kontrol ettim ve bu iç içe geçmiş işlevde olduğumda, "bu" işaretçinin aslında küresel "pencere" nesnesine işaret ettiğini gösteriyor - ki bunu beklemiyordum. Bir şeyi doğru anlamamalıyım çünkü iç içe geçmiş işlevi nesnenin bir işlevi içinde tanımladığım için, işlevle ilişkili olarak "yerel" kapsama sahip olması gerektiğini düşündüm (yani "bu" gösterici nesnenin kendisine şu şekilde atıfta bulunacaktır: ilk "eğer" ifademde nasıl olduğu).

Herhangi bir işaretçi (kelime anlamı olmayan) takdir edilecektir.

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

Sorunuz tam olarak nedir?
Sarfraz

2
Bir işlevin içinde kullanıldığında this, işlevin çağrıldığı nesneyi ifade eder.
yaklasik mavi

9
Dış kapsamda yapabileceğiniz şey, buna benzer bir şeydir var self = this;ve daha sonra selfiç işlevde kapatma yoluyla başvurabilirsiniz .
Kai

1
doSomeEffectsözellikle herhangi bir obj ile ilişkili değildir, bu nedenle thistüm elemanların anası olan pencere olduğu varsayılır.
yaklaşık:

3
@JoJoeDad Bunu diplomatik olarak nasıl söyleyebilirim? Ancak aşağıda chuckj tarafından verilen cevap, sorunuzun cevabıdır. Neler olup bittiğini gerçekten anlamak için yürütme bağlamını , kapsam zincirini ve bu anahtar kelimeyi okumalısınız . Ve buradaki bazı cevapların görünüşüne göre, diğer insanlar da onları okumalı. İyi bir javascript'i yaymaya çalışıyorum. Bu yüzden bu bağlantıları vermeye zaman ayırıyorum.
onefoot2

Yanıtlar:


121

JavaScript'te thisnesne, işlev çağrılarınızı nasıl yaptığınıza bağlıdır.

Genel olarak thisnesneyi kurmanın üç yolu vardır :

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

Yukarıdaki örneklerin hepsinde thisnesne olacaktır someThing. Önde gelen bir ana nesne olmadan bir işlevi çağırmak, genellikle size çoğu tarayıcıda nesne anlamına gelen global nesneyi getirir window.


this.doSomeEffects();Cevabınıza göre kodu değiştirdim ama yine de çalışmıyor. Neden?
Arashsoft

1
@Arashsoft thisiçinde this.doSomeEffects()için nokta std_obj. Yukarıdaki yanıtta açıklandığı gibi, bir işlevin nesne başvurusu yoksa, o thiszaman bir pencere nesnesi olarak kabul edilir.
Shivam

43

Bu, türünün en beğenilen sorularından biri gibi göründüğünden, bunca yıldan sonra ok işlevlerini kullanan ES6 çözümünü eklememe izin verin:

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

6
İşte bu ilerleme!
Joshua Pinter

Mükemmel, basit, çözüm.
Joshua Schlichting

4 yıldır bunun bir şey olduğunu nasıl bilemedim.
xorinzor

32

thiskapanış kapsamının bir parçası olmadığından, çağrı sitesinde bağlanan işleve ek bir parametre olarak düşünülebilir. Yöntem bir yöntem olarak çağrılmazsa, global nesne olarak iletilir this. Tarayıcıda global nesne ile aynıdır window. Örneğin, aşağıdaki işlevi düşünün,

function someFunction() {
}

ve aşağıdaki nesne,

var obj = { someFunction: someFunction };

İşlevi yöntem sözdizimini kullanarak çağırırsanız, örneğin,

obj.someFunciton();

sonra thisbağlanır obj.

BazıFunction () işlevini doğrudan çağırırsanız, örneğin,

someFunction();

o zaman thisglobal nesneye bağlanır, yani window.

En yaygın çözüm, bunu kapanışa kadar yakalamaktır.

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

that = this is perfect
Nather Webber

9

Kapsama değişkenleri ile "bu" arasında bir fark vardır. "bu" aslında işlevin çağırıcısı tarafından tanımlanırken, açık değişkenler, kapsam olarak bilinen işlev bildirim bloğu içinde bozulmadan kalır. Aşağıdaki örneğe bakın:

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

burada deneyebilirsiniz: http://jsfiddle.net/kSTBy/

İşlevinizde olan şey "doSomeEffects ()" dir, açıkça çağrılmaktadır, bu, işlevin bağlamı veya işlevin "bu "unun pencere olduğu anlamına gelir. eğer "doSomeEffects" bir prototip metoduysa, örneğin this.doSomeEffects "myObject" derken, myObject.doSomeEffects () "this" in "myObject" olmasına neden olur.


4
tembel ve sabırsız olanlar için bu demo _this.name = myFirstObject this.name = mySecondObject
şunları

9

Bu soruyu anlamak için aşağıdaki kod parçacığının çıktısını almaya çalışın

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

Yukarıdaki kod, konsola aşağıdakileri verecektir:

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

Dış işlevde, hem bu hem de self myObject'e başvurur ve bu nedenle her ikisi de foo'ya uygun şekilde başvurabilir ve erişebilir.

İç işlevde, bu artık myObject ile ilgili değildir. Sonuç olarak, this.foo iç işlevde tanımsızdır, oysa yerel değişken self, kapsam içinde kalır ve buradan erişilebilir. (ECMA 5'ten önce, iç işlevdeki bu, genel pencere nesnesine atıfta bulunurken, ECMA 5 itibariyle, bu iç işlevde tanımsız olacaktır.)


1
İç işlevde, thispencere nesnesini (bir tarayıcı ortamında) veya GLOBAL nesnesini (bir node.js ortamında) ifade eder
raneshu

2
Bu neden oluyor?
setu

@raneshu "sıkı kullanın" ve thisartık pencereyi ifade
etmiyor

3

Kyle tarafından açıklandığı gibi, işlev içinde belirtmek için callveya kullanabilirsiniz :applythis

İşte kodunuza uygulanan bu kavram:

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


0

Ben de "bir uyarı var bu aracılığıyla bir sınıf alanına Potansiyel geçersiz referans erişim "

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

0

Bahsedilmediğinden, kullanmanın .bind()bir çözüm olduğundan bahsedeceğim -


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

    std_obj.displayMe();

İşte daha basit bir örnek -

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'

Ok işlevini kullanmanın =>daha düzgün göründüğü ve programcı için kolay olduğu doğrudur . Ancak, bir işlevin basitçe thisbir işaretçi aracılığıyla ilişkilendirilmesine kıyasla, sözlü bir kapsamın bu sözcük kapsamını kurmak ve sürdürmek için işleme ve bellek açısından daha fazla çalışma gerektireceği unutulmamalıdır .bind().

JS'de sınıf geliştirmenin faydasının bir kısmı, thisdaha güvenilir bir şekilde mevcut ve kullanılabilir hale getirmek , fonksiyonel programlama ve sözcük kapsamlarından uzaklaşmak ve böylece ek yükü azaltmak için bir yöntem sağlamaktı .

MDN'den

Performansla ilgili hususlar Belirli bir görev için kapatma gerekmiyorsa, diğer işlevler içinde gereksiz yere işlevler oluşturmak akıllıca değildir, çünkü hem işlem hızı hem de bellek tüketimi açısından komut dosyası performansını olumsuz etkileyecektir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.