Użycie zmiennej "this" w języku Javascript

in javascript •  6 years ago  (edited)

Definiując funkcję w języku Javascript możemy też tworzyć klasy obiektów, a dokładniej konstruktory tych klas. Tutaj należy zwrócić szczególną uwagę na zmienną "this" wskazującą na aktualny obiekt klasy, bo zasięg tej zmiennej może być ograniczony lokalną zmienną "this" zwykłej funkcji której definicja znajduje się wewnątrz konstruktora. Problemu tego nie ma z funkcjami strzałkowymi które nie tworzą swoje lokalnego kontekstu (closure) i dlatego zawsze odwołują się do "this" widzianego na zewnątrz, jest to szczególnie użyteczne w momencie kiedy użyjemy całego łańcucha funkcji ze strzałką gdzie wynik jednej funkcji => jest parametrem wejściowym drugiej funkcji strzałkowej.

function BookList (prefix) {  
   
   this.prefix = prefix; 
   this.prefix2 = 'd'  

   this.makeGetBooksWithPrefix = function() { 

       return () => {
           /* zmienna "this" objektu klasy BookList lub*/
           /* zmienna "this" metody getBooksWithPrefix */  
           return ["book1", "book2", "book3"].map(book => `${this.prefix}${book}` );
       } 
   }

   this.getBooksWithPrefix1 = (() => { 

       return () => {   
           /* atrybut "this.prefix" objektu klasy BookList, nieokreślony jeszcze w definicji klasy */
           return ["book1", "book2", "book3"].map(book => `${this.prefix}${book}` );
       } 
   })();

   this.getBooksWithPrefix2 = (() => { 

       return () => { 
           /* atrybut "this.prefix2" klasy BookList, określony w definicji klasy */     
           return ["book1", "book2", "book3"].map(book => `${this.prefix2}${book}` ); 
       } 
   })();

   this.getBooksWithPrefix3 = (function() {

       /* funkcja tworzy nowy lokalny "this", war. this.prefix2 będzie undefined */
       return () => {
           /* zmienna "this" metody getBooksWithPrefix3 */
           return ["book1", "book2", "book3"].map(book => `${this.prefix2}${book}` ); 
       } 
   })();

   this.getBooksWithPrefix4 = (function() {

       /* lokalne "this" zostaje zastąpione kontekstem objektu BookList dzięki bind(this)*/
       return () => {
           /* zmienna "this" metody getBooksWithPrefix3 */
           return ["book1", "book2", "book3"].map(book => `${this.prefix2}${book}` ); 
       } 
   }).bind(this)();
}

let c = new BookList('*');
let d = new BookList('x');

/* użycie funkcji wygenerowanej lokalnie w miejscu wywołania: */
console.log( c.makeGetBooksWithPrefix()() );
console.log( d.makeGetBooksWithPrefix()() );

/* użycie funkcji wygenerowanych wcześniej w konstruktorze: */
console.log( d.getBooksWithPrefix1() );
console.log( d.getBooksWithPrefix2() );
console.log( d.getBooksWithPrefix3() );
console.log( d.getBooksWithPrefix4() );

// wynik funkcji stworzonej lokalnie:
//  [ '*book1', '*book2', '*book3' ]
//  [ 'xbook1', 'xbook2', 'xbook3' ]

// wynik funkcji stworzonych w konstruktorze:
//  [ 'xbook1', 'xbook2', 'xbook3' ]
//  [ 'dbook1', 'dbook2', 'dbook3' ]
//  [ 'undefinedbook1', 'undefinedbook2', 'undefinedbook3' ]
//  [ 'dbook1', 'dbook2', 'dbook3' ]

Przykład powyżej pokazuje jak zastosowanie notacji strzałkowej (arrow functions) zmienia znaczenie zmiennej "this". Każde odwołanie do "this" wewnątrz funkcji strzałkowej odnosi się do zasięgu w którym taka funkcja jest definiowana, jeżeli brakuje szukanej zmiennej to sprawdzany jest zasięg bardziej zewnęczny - w przykładzie powyżej this.prefix nie jest zdefiniowany w funkcji makeGetBooksWithPrefix więc pobrany jest prefix z funkcji BookList. Ponieważ funkcja makeGetBooksWithPrefix tworzy i zwraca właściwą funkcję anonimową w miejscu jej wywołania to właśnie w tym momencie określana jest wartość this.prefix. (jest to aktualna wartość atrybutu prefix w obiekcie klasy BookList).
Podobnie zachowuje się anonimowa funkcja przypisana do zmiennej getBooksWithPrefix1 - wartość this.prefix jest nieokreślona w momencie generowania tej funkcji więc użycie tej funkcji dla konkretnego obiektu pobiera aktualną wartość this.prefix tego obiektu.
Funkcja przypisana do atrybutu getBooksWithPrefix2 jest dokładnie taką samą anonimową funkcją strzałkową wygenerowaną przez inną anonimową funkcję strzałkową jak getBooksWithPrefix1 ale różnica polega na tym że funkcja ta odwołuje się do atrybutu this.prefix2 który jest dostępny w definicji konstruktora.
Następny przypadek to zwykła funkcja która zwraca funkcję strzałkową i przypisuje ją do atrybutu getBooksWithPrefix3. Tutaj zwracana funkcja strzałkowa nie ma dostępu ani do this.prefixani też do this.prefix2 więc zwrócona będzie wartość "undefined" (zwykła funkcja zdefiniowana za pomocą słowa kluczowego function tworzy swoją lokalną zmienną this).
I ostatnia funkcja przypisana do pola getBooksWithPrefix4 to dokładna kopia funkcji z pola getBooksWithPrefix3, jedyna różnica to użycie bind(this) na funkcji anonimowej function() {...bezpośrednio przed jej wywołaniem, funkcja bind(this) przekazuje do funkcji anonimowej kontekst objektu klasy, więc aktualne wartości pól objektu klasy BookList są ponownie dostępne.

Authors get paid when people like you upvote their post.
If you enjoyed what you read here, create your account today and start earning FREE STEEM!
Sort Order:  

Congratulations @damii! You received a personal award!

Happy Birthday! - You are on the Steem blockchain for 1 year!

Click here to view your Board

Support SteemitBoard's project! Vote for its witness and get one more award!

Hello @damii! This is a friendly reminder that you have 3000 Partiko Points unclaimed in your Partiko account!

Partiko is a fast and beautiful mobile app for Steem, and it’s the most popular Steem mobile app out there! Download Partiko using the link below and login using SteemConnect to claim your 3000 Partiko points! You can easily convert them into Steem token!

https://partiko.app/referral/partiko

Congratulations @damii! You received a personal award!

Happy Birthday! - You are on the Steem blockchain for 2 years!

You can view your badges on your Steem Board and compare to others on the Steem Ranking

Vote for @Steemitboard as a witness to get one more award and increased upvotes!