This w JS - czy jest konieczne?

0

Cześć,
uczę się JS i zastanawiam się kiedy konieczne jest używanie this jeśli chcemy programować obiektowo?
W C++ this używamy przykładowo wtedy, gdy nazwa zmiennej jest taka sama jak argument w konstruktorze/funkcji - w celu odróżnienia, o którą zmienną nam chodzi.
W JS jeśli dobrze rozumiem nie ma znaczenia czy nazwy zmiennych są takie same czy nie - zawsze trzeba używać this?

A co jeśli tworzymy metodę do naszego obiektu - czy tam również obligatoryjnie trzeba podawać "this"? Nawet jeśli nie ma żadnych zmiennych globalnych o takiej samej nazwie?

Weźmy taki przykład:

function konstruktor(nazwa_zmiennej)
{
this.zmienna = nazwa_zmiennej; //czy tu jest konieczne this? - zmienne inaczej się nazywają
this.funkcja = function(nowa_zmienna) {
   this.zmienna = nowa_zmienna; //czy tu muszę użyć this?
   }
}
0

I tak i nie.

W tych przykładach co dałeś musisz dać this, bo w JS to inaczej działa niż w C++.

Generalnie jak masz funkcję w JS i odwołujesz się do jakiejś zmiennej to możesz się odwoływać tylko do tych, które "widzisz" na danym poziomie zagnieżdżenia funkcji. To funkcje a nie obiekty są głównym budulcem w JS jeśli chodzi o zasięg zmiennych.

Jednak owszem, mógłbyś się pozbyć this jeśli byś to zapisał tak:

function konstruktor(nazwa_zmiennej)
{
   // to zmienna zadeklarowana w funkcji konstruktor, a nie właściwość obiektu!
   var zmienna = nazwa_zmiennej;  // tutaj! ta sama zmienna, którą używamy dwie linijki później
      
   this.funkcja = function(nowa_zmienna) {
       zmienna = nowa_zmienna; // szukamy zmiennej o nazwie `zmienna`, jest zadeklarowana funkcję wyżej
    }
}

I w ten sposób możesz się pozbyć potrzeby korzystania z this (funkcje są decydujące - wewnętrzna funkcja widzi to co jest zadeklarowane w zewnętrznej).

Jednak, jeśli chodzi o to co pytałeś, to jednak w tamtym przypadku musiałbyś użyć this. Bo w funkcji nie widzisz bezpośrednio właściwości obiektu this, musisz najpierw się dostać do obiektu this a potem dopiero do jego właściwości.

Czyli w skrócie: musisz użyć this w tym przypadku, o którym pisałeś, jednak zagadnienie jest szersze i można tak kombinować w JS, żeby tego this nie używać.

0

@LukeJL dziekuję za odpowiedź.
Twój sposób jak najbardziej mnie zadowala :-) -> w mojej ocenie jest bardziej czytelny - szczególnie, gdy robi się dużo zmiennych i często się do nich odwołujemy.
Rozumiem, że z funkcjami wewnętrznymi zamiast this również mogę używać var nazwa_funkcji = function(){...}? tzn.

function konstruktor(nazwa_zmiennej)
{
   // to zmienna zadeklarowana w funkcji konstruktor, a nie właściwość obiektu!
   var zmienna = nazwa_zmiennej;  // tutaj! ta sama zmienna, którą używamy dwie linijki później
 
   var funkcja = function(nowa_zmienna) {
       zmienna = nowa_zmienna; // szukamy zmiennej o nazwie `zmienna`, jest zadeklarowana funkcję wyżej
    }
}
0

Aha i jeszcze takie pytanie:
jeśli argument będzie się nazywał tak samo jak zmienna w konstruktorze to JS się domyśli, że próbuję przypisać argument do zmiennej a nie tą samą zmienną do siebie? tzn. czy jest dozwolone takie coś:

function konstruktor(nazwa_zmiennej)
{
   var nazwa_zmiennej = nazwa_zmiennej; 
}

Z góry dzięki za odpowiedź.

1

teoretycznie nie powinno się dać, bo zmienna nazwa_zmiennej jest już zadeklarowana (jako parametr funkcji), więc używając var po prostu deklarujesz ponownie zmienną o takiej nazwie.

I teraz tak - jeśli chodzi o var, jest to starszy konstrukt JS i ma wadę, że pozwala na wielokrotne deklarowanie tych samych zmiennych:

function konstruktor(nazwa_zmiennej)
{
   var nazwa_zmiennej = nazwa_zmiennej; 
   var nazwa_zmiennej = 2;
   return {};
}
konstruktor();

jest to wada języka. I dlatego teraz wymyślono let i const zamiast var, które mają zasięg blokowy (za pomocą let i const można np. zrobić zmienną, która będzie widoczna tylko w if, a var ma zasięg na całą funkcję od razu, co nie zawsze jest dobre), no i mają kontrolę czy dana zmienna jest już zadeklarowana (przy użyciu const na dodatek nie można przepisać raz zadeklarowanej zmiennej, co pozwala czasem na uniknięcie błędów):

function konstruktor(nazwa_zmiennej)
{
   let nazwa_zmiennej = nazwa_zmiennej; 
   return {};
}
konstruktor(); // Uncaught SyntaxError: Identifier 'nazwa_zmiennej' has already been declared


const foo = 3;

foo = 7;  // TypeError: Assignment to constant variable.

czyli "zmienna" staje się stałą, co pozwala na bardziej przewidywalne programowanie.

2

this służy do tworzenia i odwoływania się do poublicznych pól obiektu. Możesz nie uzywać this i tworzyć w konstruktorze zwykłe zmienne, jednak będą one dostępne tylko w obrębie konstruktora, będą prywatne dla konstruktora, niewidoczne nawet dla metod prototypu:

function Example() {
  var a = 'foo'
  this.b = 'bar'
}

Example.prototype.showA = function() {
  return a
}

Example.prototype.showB = function() {
  return this.b
};

const instance = new Example()

console.log('Prywatna zmienna konstruktora:', instance.a) // => undefined
console.log('Publiczne pole instancji:', instance.b) // => "bar"
console.log('showB', instance.showB()) // => "bar"
console.log('showA', instance.showA()) // => ReferenceError: a is not defined

W ES5 nie ma możliwości stworzenia za pomocą funkcji konstruktora pól prywatnych widocznych w obrębie całego obiektu (także w metodach prototypu), w ES6 można uzyć do tego symboli (choć to też nie jest pełna prywatność). Rozwiązaniem jest wrzucenie wszystkich metod obiektu do konstruktora (lub przynajmniej tych korzystających z prywatnych zmiennych konstruktora):

function Example() {
  var a = 'foo'
  this.b = 'bar'
  
  this.showA = function() {
    return a
  }
  
  this.showB = function() {
    return this.b
  };
}

const instance = new Example()

console.log('Prywatna zmienna konstruktora:', instance.a) // => undefined
console.log('Publiczne pole instancji:', instance.b) // => "bar"
console.log('showB', instance.showB()) // => "bar"
console.log('showA', instance.showA()) // => "foo"

Ma to tą wadę, że zajmuje więcej pamięci, lecz w wiekszości przypadków nie ma to żadnego znaczenia. No ale jak już tak robimy to nie jest nam potrzebny konstruktor i tworzenie obiektów za pomocą new, możemy użyć funkcji-fabryki, czyli po prostu funkcji, która zwaca literał obiektowy:

function example() {
  const a = 'foo'
  
  return {
    b: 'bar',
    
    showA() {
      return a
    },
    
    showB() {
      return this.b
    }
  }
}

const instance = example()

console.log('Prywatna zmienna konstruktora:', instance.a) // => undefined
console.log('Publiczne pole instancji:', instance.b) // => "bar"
console.log('showB', instance.showB()) // => "bar"
console.log('showA', instance.showA()) // => "foo"

Mało tego - możemy teraz całkowicie wyeliminować this, które jest wrażliwe na kontekst wywołania i przez to nie działa zawsze tak jak się intuicyjnie wydaje:

function example() {
  const a = 'foo'
  
  const pub = {
    b: 'bar',
    
    showA() {
      return a
    },
    
    showB() {
      return pub.b
    }
  }
  
  return pub
}

const instance = example()

console.log('Prywatna zmienna konstruktora:', instance.a) // => undefined
console.log('Publiczne pole instancji:', instance.b) // => "bar"
console.log('showB', instance.showB()) // => "bar"
console.log('showA', instance.showA()) // => "foo"

Polecam ostatnie dwa sposoby - dzięki domknięciom możesz łatwo sterować zasięgiem zmiennych, bardzo łatwo robi sie kompozycję takich obiektów, jest mniej magii.

W ES6 są jeszcze klasy i wspomniane symbole - to podejście też ma zalety, jednak magii tam sporo węc ja preferuję fabryki.

A tak ogólnie to programowanie funkcyjne > programowanie obiektowe ;)

PS:
Jeszcze dodam - jeśli metodę zadeklarujesz bez this w funkcji-konstruktorze to tak samo jak dla zmiennej a nie będziesz jej mógł użyć z poziomu instancji (tylko wewnątrz funkcji-konstruktora).

0

Super! Jeszcze raz dziękuję @LukeJL :-)
Rozumiem, że zamiast this.nazwa_funkcji = function(){...} mogę również bez obaw używać w konstruktorze var nazwa_funkcji = function(){...}?

0

Ojej, troszkę się pośpieszyłem :-).
@Maciej Cąderek dziekuję bardzo za wyjaśnienie (z przykładami) i skorygowanie błędów!!! :-)

1 użytkowników online, w tym zalogowanych: 0, gości: 1