2012-11-16 15 views
27

W C++, język mi się najbardziej komfortowo, zwykle jeden deklaruje obiekt tak:JavaScript: Czy muszę wstawić this.var dla każdej zmiennej w obiekcie?

class foo 
{ 
public: 
    int bar; 
    int getBar() { return bar; } 
} 

Wywołanie getBar() działa dobrze (pomijając fakt, że bar może być niezainicjowanymi). Zmienna bar w ramach getBar() jest w zakresie klasy foo, więc nie muszę mówić this->bar, chyba że naprawdę muszę wyjaśnić, że mam na myśli klasę "bar zamiast, powiedzmy, parametru.

Teraz próbuję zacząć od OOP w JavaScript. Więc patrzę w górę, jak definiować klasy i spróbować tego samego rodzaju rzeczy:

function foo() 
{ 
    this.bar = 0; 
    this.getBar = function() { return bar; } 
} 

I to daje mi bar is undefined. Zmiana bar na this.bar rozwiązuje ten problem, ale zrobienie tego dla każdej zmiennej powoduje dość częste obcinanie kodu. Czy jest to konieczne dla każdej zmiennej? Ponieważ nie mogę znaleźć żadnych pytań na ten temat, mam wrażenie, że robię coś fundamentalnie złego.


EDIT: Tak, tak, z uwagami o co mi chodzi, że this.bar, właściwość obiektu odniesienia coś innego niż bar zmienna lokalna. Czy ktoś może powiedzieć, dlaczego tak jest, pod względem zakresu i obiektów, i czy istnieje inny sposób zdefiniowania obiektu, w którym nie jest to konieczne?

+2

dostęp '.bar' jest własności przedmiotów nie zmienna. Więc tak, aby uzyskać właściwość z obiektu, musisz to zrobić poprzez odwołanie do tego obiektu. Jeśli 'this' odwołuje się do twojego obiektu, to' this.bar' da ci tę właściwość. –

+3

'this.bar' nie jest tym samym, co' bar'. Możesz jednak użyć 'var bar = this.bar', który spowoduje, że' bar' odwoła się do 'this.bar'a – Loktar

+0

@Loktar, ale tylko wtedy, gdy' this.bar' jest obiektem lub tablicą ... – Alnitak

Odpowiedz

31

JavaScript nie ma klasycznego modelu obiektowego klasy . Używa potężniejszego prototypowego dziedziczenia, które może naśladować klasy, ale nie nadaje się do tego dobrze. Wszystko jest przedmiotem, a przedmioty mogą dziedziczyć z innych obiektów.

Konstruktor jest po prostu funkcją, która przypisuje właściwości do nowo utworzonych obiektów. Obiekt (utworzony przez wywołanie z new keyword) można odwoływać się przez this keyword (który jest lokalny dla funkcji).

Metoda jest również tylko funkcją o nazwie na obiekt - ponownie z this wskazując na obiekt. Przynajmniej wtedy, gdy ta funkcja jest wywoływana jako właściwość obiektu, przy użyciu kropki, nawiasów. Powoduje to wiele zamieszania u początkujących, ponieważ jeśli przekażesz tę funkcję (np. Do detektora zdarzeń), zostanie ona "odłączona" od obiektu, na którym był dostępny.

Teraz, gdzie jest dziedziczenie? Instancje "klasy" dziedziczą z tego samego obiektu prototypowego. Metody są zdefiniowane jako właściwości funkcji dla tego obiektu (zamiast jednej funkcji dla każdej instancji), instancja, w której się je nazywa, dziedziczy tylko tę właściwość.

Przykład:

function Foo() { 
    this.bar = "foo"; // creating a property on the instance 
} 
Foo.prototype.foo = 0; // of course you also can define other values to inherit 
Foo.prototype.getBar = function() { 
    // quite useless 
    return this.bar; 
} 

var foo = new Foo; // creates an object which inherits from Foo.prototype, 
        // applies the Foo constructor on it and assigns it to the var 
foo.getBar(); // "foo" - the inherited function is applied on the object and 
       // returns its "bar" property 
foo.bar; // "foo" - we could have done this easier. 
foo[foo.bar]; // 0 - access the "foo" property, which is inherited 
foo.foo = 1; // and now overwrite it by creating an own property of foo 
foo[foo.getBar()]; // 1 - gets the overwritten property value. Notice that 
(new Foo).foo;  // is still 0 

Tak, my używaliśmy tylko właściwości tego obiektu i są z niego zadowoleni. Ale wszystkie z nich są "publiczne" i można je zastąpić/zmienić/usunąć! Jeśli to nie ma znaczenia, masz szczęście. Możesz wskazać "prywatyzację" właściwości, dodając przedrostki ich nazw podkreśleniami, ale to tylko wskazówka dla innych programistów i może nie być przestrzegana (szczególnie w błędzie).

Więc, sprytni umysły znaleźli rozwiązanie, które wykorzystuje funkcję konstruktora jako zamknięcie, pozwalając na tworzenie prywatnych "atrybutów". Każde wykonanie funkcji javascript tworzy nowe środowisko zmienne dla zmiennych lokalnych, które mogą zbierać śmieci po zakończeniu wykonywania. Każda funkcja zadeklarowana w tym zakresie ma również dostęp do tych zmiennych i dopóki te funkcje mogą być wywoływane (np. Przez detektor zdarzeń), środowisko musi się utrzymywać. Tak więc, przez eksportowanie funkcji zdefiniowanych lokalnie ze swojego konstruktora, zachowujesz to zmienne środowisko za pomocą zmiennych lokalnych, do których dostęp mają tylko te funkcje.

Zobaczmy, jak to działa:

function Foo() { 
    var bar = "foo"; // a local variable 
    this.getBar = function getter() { 
     return bar; // accesses the local variable 
    }; // the assignment to a property makes it available to outside 
} 

var foo = new Foo; // an object with one method, inheriting from a [currently] empty prototype 
foo.getBar(); // "foo" - receives us the value of the "bar" variable in the constructor 

Ta funkcja getter, która jest zdefiniowana wewnątrz konstruktora, jest teraz nazywany „uprzywilejowany sposób”, jak to ma dostęp do „private” (lokalny) "atrybuty" (zmienne). Wartość bar nigdy się nie zmieni. Można również zadeklarować funkcję ustawiającą dla niego oczywiście, i że można dodać pewne sprawdzanie poprawności itp.

Należy zauważyć, że metody obiektu prototypowego nie mają dostępu do zmiennych lokalnych konstruktora, ale mogą one użyj metod uprzywilejowanych. Dodajmy:

Foo.prototype.getFooBar = function() { 
    return this.getBar() + "bar"; // access the "getBar" function on "this" instance 
} 
// the inheritance is dynamic, so we can use it on our existing foo object 
foo.getFooBar(); // "foobar" - concatenated the "bar" value with a custom suffix 

Można więc połączyć oba podejścia. Zauważ, że uprzywilejowane metody wymagają więcej pamięci, ponieważ tworzysz różne obiekty funkcji z różnymi łańcuchami zasięgu (ale ten sam kod). Jeśli zamierzasz tworzyć niezwykle duże ilości instancji, powinieneś zdefiniować metody tylko na prototypie.

To staje się jeszcze bardziej skomplikowane, gdy konfigurujesz dziedziczenie z jednej "klasy" na drugą - w zasadzie musisz uczynić obiekt prototypowy dziecka dziedziczącym po obiekcie macierzystym i zastosować konstruktor macierzysty w instancjach potomnych w celu utworzenia "prywatne atrybuty". Przyjrzeć się Correct javascript inheritance, Private variables in inherited prototypes, Define Private field Members and Inheritance in JAVASCRIPT module pattern i How to implement inheritance in JS Revealing prototype pattern?

+0

To bardzo pomaga przy moim zrozumieniu, czym dokładnie jest dzieje się tutaj. Naprawdę, pierwotny motywator był po prostu irytacją, że muszę umieścić "to" przed wszystkim, ale w rzeczywistości zrozumienie, dlaczego muszę, jest dobre. – Chaosed0

+0

Brakuje "operatora członkowskiego" w ECMAScript. Właściwym terminem jest "[właściwość accessor] (http: // ecma-international.org/ecma-262/5.1/# sec-11.2.1) notacja ". – PointedEars

+0

@PointedEars: Jasne, to oficjalny termin. Ale czy "członek operatora" (na pozór ukuty przez MDN) jest naprawdę zły lub nieopisujący? Operator w sensie matematycznym, oczywiście nie w kategoriach binarnego operatora ES (§ 11.5-11.11) – Bergi

2

Aby zbliżyć się do OOP w JavaScript, warto spojrzeć na wzór projektu modułu (na przykład opisane here).

W oparciu o efekt zamknięcia wzór ten umożliwia emulację prywatnych właściwości obiektów.

Dzięki właściwościom "prywatnym" można odwoływać się do nich bezpośrednio za pomocą swojego identyfikatora (tj. Nie ma to słowo kluczowe, jak w konstruktorze).

Ale w każdym razie, zamknięcia i wzorce projektowe w JS - zaawansowany temat. Zapoznaj się więc z podstawami (wyjaśnione również w wyżej wspomnianej książce).

+0

Wracając i przeglądając ten artykuł (z pewnością ma on długość być książką, ale nie wydaje się być opublikowany) był bardzo, bardzo pomocny. Dzięki, że przyniosłeś to pod moim okiem. – Chaosed0

+0

@ Chaosed0 Zawsze mile widziane! Nie jest tak wiele książek/artykułów dotyczących wzorców projektowych w JS, które warto przeczytać, ale ten jest dobry. –

2

W javascript this zawsze odnosi się do właściciela obiektu funkcji. Na przykład, jeśli zdefiniujesz na stronie funkcję foo(), właścicielem będzie obiekt javascript windows; lub jeśli zdefiniujesz foo() na html element <body>, to właściciel jest ciałem elementu html; i podobnie, jeśli zdefiniujesz funkcję onclick elementu <a>, wówczas właścicielem jest kotwica.

W twoim przypadku przypisujesz właściwość bar do obiektu "właściciel" na początku i próbujesz zwrócić lokalną zmienną bar.

Ponieważ nigdy nie zdefiniowałeś żadnego lokalnego varialbe bar, oznacza to, że pasek jest niezdefiniowany.

Idealnie kod powinien zdefiniować zmienną jako var bar;, jeśli chcesz zwrócić wartość zero.

6

Wyraźnie mówiąc: this.foo oznacza (jak dobrze rozumiałeś), że interesuje Cię obiekt foo bieżącego obiektu, do którego odnosi się this. Więc jeśli użyjesz: this.foo = 'bar';, ustawisz właściwość foo bieżącego obiektu, do którego odnosi się this, co odpowiada bar.

Słowo kluczowe this w języku JavaScript nie zawsze oznacza to samo, co w C++. Tutaj mogę dać wam przykład:

function Person(name) { 
    this.name = name; 
    console.log(this); //Developer {language: "js", name: "foo"} if called by Developer 
} 

function Developer(name, language) { 
    this.language = language; 
    Person.call(this, name); 
} 

var dev = new Developer('foo', 'js'); 

W powyższym przykładzie mamy wywołanie funkcji Person z kontekstu funkcji Developer tak this odwołuje się do obiektu, który zostanie utworzony przez Developer. Jak można zauważyć na podstawie wyniku console.log, this pochodzi z Developer. Za pomocą pierwszego argumentu metody określamy kontekst, z którym funkcja zostanie wywołana.

Jeśli nie użyjesz this, własność, którą utworzysz, będzie zmienną lokalną. Jak pewnie wiesz, JavaScript ma zasięg funkcjonalny, dlatego zmienna będzie lokalna, widoczna tylko dla funkcji, w której została zadeklarowana (i oczywiście wszystkie jej funkcje podrzędne, które są zadeklarowane w rodzicu). Oto przykład:

function foo() { 
    var bar = 'foobar'; 
    this.getBar = function() { 
     return bar; 
    } 
} 

var f = new foo(); 
console.log(f.getBar()); //'foobar' 

Jest to prawdziwe, gdy używasz słowa kluczowego var. Oznacza to, że definiujesz bar jako zmienną lokalną, jeśli zapomnisz var, niestety bar stanie się globalnym.

function foo() { 
    bar = 'foobar'; 
    this.getBar = function() { 
     return bar; 
    } 
} 

var f = new foo(); 
console.log(window.bar); //'foobar' 

Dokładnie lokalny zasięg może pomóc w osiągnięciu prywatności i enkapsulacji, które są jedną z największych zalet OOP.

rzeczywistym przykładem świat:

function ShoppingCart() { 
    var items = []; 

    this.getPrice = function() { 
     var total = 0; 
     for (var i = 0; i < items.length; i += 1) { 
      total += items[i].price; 
     } 
     return total; 
    } 

    this.addItem = function (item) { 
     items.push(item); 
    } 

    this.checkOut = function() { 
     var serializedItems = JSON.strigify(items); 
     //send request to the server... 
    } 
} 

var cart = new ShoppingCart(); 
cart.addItem({ price: 10, type: 'T-shirt' }); 
cart.addItem({ price: 20, type: 'Pants' }); 
console.log(cart.getPrice()); //30 

Jeszcze jeden przykład z korzyści płynących z zakresu JavaScript jest Module Pattern. W Wzorze modułu można symulować prywatność za pomocą lokalnego zakresu funkcjonalnego JavaScript. Dzięki takiemu podejściu możesz mieć zarówno prywatne właściwości, jak i metody. Oto przykład:

var module = (function { 

    var privateProperty = 42; 

    function privateMethod() { 
     console.log('I\'m private'); 
    } 
    return { 

     publicMethod: function() { 
      console.log('I\'m public!'); 
      console.log('I\'ll call a private method!'); 
      privateMethod(); 
     }, 

     publicProperty: 1.68, 

     getPrivateProperty: function() { 
      return privateProperty; 
     }, 

     usePublicProperty: function() { 
      console.log('I\'ll get a public property...' + this.publicProperty); 
     } 

    } 
}()); 

module.privateMethod(); //TypeError 
module.publicProperty(); //1.68 
module.usePublicProperty(); //I'll get a public property...1.68 
module.getPrivateProperty(); //42 
module.publicMethod(); 
/* 
* I'm public! 
* I'll call a private method! 
* I'm private 
*/ 

Jest trochę dziwny składnia z parentless owijania anonimowych funkcje, ale zapomnieć na chwilę (to tylko wykonywania funkcji po to jest inicjowany). Funkcjonalność można zobaczyć na przykładzie użycia, ale korzyści są związane głównie z zapewnieniem prostego interfejsu publicznego, który nie angażuje wszystkich szczegółów implementacji. Aby uzyskać bardziej szczegółowe wyjaśnienie wzoru, możesz zobaczyć link, który umieściłem powyżej.


Mam nadzieję, że z this :-) informacji Pomogłem Ci zrozumieć kilka podstawowych tematów JavaScript.

3
function Foo() { 
    this.bar = 0; 
    this.getBar = function() { return this.bar }; 
} 

Po wywołaniu funkcji powyżej z new słów kluczowych - jak to ...

var foo = new Foo(); 

... - kilka rzeczy się zdarzają:

1) obiekt jest tworzony
2) funkcja jest wykonywana za pomocą słowa kluczowego this odwołującego się do tego obiektu.
3) ten obiekt jest zwracany.

foo, potem staje obiektu:

{ 
    bar: 0, 
    getBar: function() { return this.bar; } 
}; 

Dlaczego nie, a następnie, po prostu to zrobić:

var foo = { 
    bar: 0, 
    getBar: function() { return this.bar; } 
}; 

byś, jeśli to tylko, że jeden prosty przedmiot.

Ale tworzenie obiektu za pomocą konstruktora (tak to się nazywa) daje nam dużą przewagę w tworzeniu wielu "samych" obiektów.

Zobacz, javascript, wszystkie funkcje są tworzone z prototypową właściwością [obiekt], a wszystkie obiekty utworzone za pomocą tej funkcji (przez wywoływanie jej za pomocą nowego słowa kluczowego) są powiązane z tym obiektem prototypowym. Dlatego jest tak fajny - możesz przechowywać wszystkie typowe metody (i właściwości, jeśli chcesz) w obiekcie prototypowym i zaoszczędzić dużo pamięci. Oto, jak to działa:

function Foo(bar, bob) { 
    this.bar = bar; 
    this.bob = bob; 
} 

Foo.prototype.calculate = function() { 
    // 'this' points not to the 'prototype' object 
    // as you could've expect, but to the objects 
    // created by calling Foo with the new keyword. 
    // This is what makes it work. 
    return this.bar - this.bob; 
}; 

var foo1 = new Foo(9, 5); 
var foo2 = new Foo(13, 3); 
var result1 = foo1.calculate(); 
var result2 = foo2.calculate(); 

console.log(result1); //logs 4 
console.log(result2); //logs 10 

To wszystko!

1

to jest jak modyfikator publiczny dostęp do obiektów (zmienne lub funkcje), a var jest prywatny modyfikator

Przykład

var x = {}; 
x.hello = function(){ 
    var k = 'Hello World'; 
    this.m = 'Hello JavaScript'; 
} 

var t = new x.hello(); 
console.log(t.k); //undefined 
console.log(t.m); //Hello JavaScript 
Powiązane problemy