2012-11-05 29 views
11

Mam następujące funkcję w klasie:Słowo kluczowe "this" zwraca obiekt okna w prototypie obiektu w JavaScript?

MyClass.prototype.myFunction = function(item, args) 
{  
    console.log(this); 
} 

Ta funkcja jest wywoływana z zewnętrznej biblioteki, które nie mają dostępu do zmiany. Po wywołaniu konsola rejestruje "to" jako obiekt okna zamiast rzeczywistego obiektu instancji. Po przeszukaniu stackoverflow znalazłem ten cytat:

ustawiono to w zależności od sposobu wywołania metody, a nie w zależności od metody zapisu. Tak więc dla obj.method(), zostanie to ustawione na obj wewnątrz metody(). Dla obj.method.call (x), to wewnątrz metody() zostanie ustawione na x. Określa się to w jaki sposób się nazywa. Oznacza to także, że jeśli przekażesz to jako wywołanie zwrotne np. onclick zostanie ustawiony na obiekt okna globalnego, a nie na to, czego oczekujesz.

Zakładam, że to właśnie się dzieje i nie mogę zmienić tego, jak się nazywa. Moje pytanie brzmi, czy jest tak czy inaczej, aby uzyskać instancję obiektu, niezależnie od tego, jak to się nazywa?

+0

Jeśli możesz, użyj słowa kluczowego "new", aby utworzyć instancję klasy. –

Odpowiedz

4

Przypuszczalnie odniesienie funkcja jest przekazywana do innej funkcji do wywołania, a druga funkcja jest coś takiego:

function otherFunction(args, fn) { 
    ... 
    fn(); 
    ... 
} 

Aby zapewnić metodę dostaje this potrzebuje, można zrobić:

// Create a local variable referencing the `this` you want 
var instance = this; 

// Pass a function that has a closure to the variable 
// and sets the appropriate this in the call 
otherFunction(args, function(){myMethod.call(instance)}) 

Teraz this w myMethod będzie niezależnie od instance odniesień. Zauważ, że jeśli zmienisz wartość instance po wywołaniu na otherFunction i przed wywołaniem tej metody, myMethod otrzyma tę nową wartość.

Możesz sobie z tym poradzić, jeśli jest to problem.

Oh, można też sobie z tym poradzić w konstruktorze, dając każdej instancji swój własny sposób, który ma zamknięcie na przykład:

function MyObj(name) { 
    var instance = this; 
    instance.name = name; 
    instance.getName = function() { 
    return instance.name; 
    } 
} 

var anObj = new MyObj('fred'); 

// Call as a method of anObj 
alert(anObj.getName()); // fred 

// Pass method as a reference 
var x = anObj.getName; 

// Unqualified call 
alert(x()); // fred  
1

Jedna poprawka byłoby mieć kopię myFunction() dla każdej instancji , czyli utworzyć go w konstruktorze zamiast go na prototypie, bo wtedy można mieć odniesienie do this przechowywane jako zmiennej lokalnej w konstruktorze:

function MyClass() { 
    var self = this; 
    this.myFunction = function(item, args) { 
     // use self instead of this 
     console.log(self); 
    }; 
    // other constructor code here 
} 

Ale oczywiście tworzenie funkcji dla każdego ins Wykorzystuje więcej pamięci - co może, ale nie musi być problemem w zależności od wielkości twojej funkcji. Kompromis może być umieścić otoki dla funkcji wewnątrz konstruktora i określić rzeczywistą funkcję na prototypie:

function MyClass() { 
    var self = this; 
    this.myFunction = function(item, args) { 
     return self.wrappedMyFunction(item, args); 
    }; 

    // your other constructor code here 
}  
MyClass.prototype.wrappedMyFunction = function(item, args) 
{  
    console.log(this); 
} 
2

Jest to powszechne zamieszanie z JavaScript. Łatwo jest myśleć o nich, że zachowują się tak, jak metody rozszerzeń w innych językach, ale w JavaScript jest tak łatwo zmienić kontekst, że często robi się to przez przypadek.

Więc:

MyClass.prototype.myFunction = function(args) 
{ 
    // You expect [this] to refer to an instance of MyClass 
    this.somePropertyOfMyClass; 
}; 

Następnie można nazwać z:

var x = new MyClass(); 
x.myFunction(args) 

Jednak sposób, że funkcja jest wywoływana w JavaScript może zmienić to, co this odnosi się do:

var y = somethingElse(); 
x.myFunction.call(y, args); // now in myFunction [this] refers to y 

Bardziej prawdopodobne jest to, że wiele bibliotek korzysta z this kontekst dla łańcuchów i wydarzeń - łatwe wprowadzanie błędów. Na przykład w jQuery:

var $thing = $('.selector'); 
$thing.click(x.myFunction); // now in myFunction [this] refers to $thing 

To chyba nie jest oczywiste dla osoby pisania jQuery że nazywając x.myFunction w ten sposób go złamać. Mogą obejść, że (przy założeniu, że wiedzą o realizacji) z:

$thing.click(function() { x.myFunction(); }); 

Jeśli chcesz swój MyClass być odporne na miano tak potem nie używać prototype - zamiast użyć właściwości obiektu:

function MyClass() { 
    var self = this; 
    // ... 
    this.myFunction = function(args) 
    { 
     // [self] will always refer to the current instance of MyClass 
     self.somePropertyOfMyClass; 
    }; 
} 

Należy pamiętać, że im bardziej nowoczesne silniki Javascript są dość dobre, jak zoptymalizować te rodzaje połączeń, więc nie będę używać prototype tylko jako optymalizacji jeżeli już zidentyfikowane, że trzeba dodatkowej wydajności.