ferrante / javascriptpl

http://javascript.pl
2 stars 0 forks source link

Domyślne wartości zmiennych przekazywane do funkcji. #12

Closed afterdesign closed 6 years ago

afterdesign commented 11 years ago

Przejrzyj czy tak może być czy jeszcze to modyfikować bo nie mam wprawy w zostawianiu krótkich tipów ;)

porada commented 11 years ago

W przypadku undefined, tylko globalne zmienne sprawdza się używając typeof — do lokalnych wystarczy samo porównanie:

typeof globalVariable === "undefined"

localVariable === undefined
afterdesign commented 11 years ago

Trzymam się wytycznych Crockforda w tych sprawach i zawsze stosuję typeof.

nandy-andy commented 11 years ago

"Przezorny zawsze ubezpieczony", czy coś w tym stylu ;)

porada commented 11 years ago

Ale po co? typeof zmienna === "undefined" sprawdza się w przypadku, kiedy zmienna może okazać się niezdefiniowana. W przypadku funkcji interesuje nas tylko wartość zmiennej, która jest już ona zdefiniowana w local scope.

Just sayin’ — naturalnie, obie wersje zadziałają. ◕◡◕


Przezorny zawsze ubezpieczony

@nandy-andy, po co używać wideł, kiedy potrzebna jest tylko igła.

nandy-andy commented 11 years ago

@porada Czasami mam zdolność do "przekombinowania" i sprawdzanie typu if( typeof(globalVar) !== 'undefined' && globalVar === true ) { ... } koledzy często sugerują, abym zamienił na if( window.globalVar ) { ... }, gdyż piszemy głównie kod dla przeglądarek, a tam zmienne globalne są polami obiektu window. Krótko: masz rację -- dorzuciłem swoje .5 grosza i nie będę dalej trollował :p

afterdesign commented 11 years ago

@porada standardowo odsyłam do jedynej słusznej książki "JavaScript: The Good Parts" ;)

A co do zmiennych globalnych to chyba nie tylko ja robię tak, że jest maksymalnie 1 zmienna globalna, która jest obiektem (czyli w sumie namespacem) i wszystkie inne rzeczy lądują automatycznie w tym. Nawet jeżeli mam napisać kilka linijek strukturalnego kodu to wykorzystuję namespace. No ewentualnie można użyć domknięcia żeby był lokalny scope.

porada commented 11 years ago

@afterdesign To, że Douglas Crockford says so nie oznacza, że to, co napisałem jest nieprawidłowe.

Douglas Crockford also says “four spaces”, and yet most popular JavaScript libraries are set in either tabs or two spaces. #

afterdesign commented 11 years ago

@porada no i to jest ich problem. Ja wykorzystuję 4 spacje w każdym języku w którym programuję bo kod jest dla mnie wtedy najczytelniejszy. Totalnie nie ogarniam 2 spacji a taby mi przeszkadzają jak muszę coś wyedytować na produkcji na szybko. Także mówiąc szczerze to jest już opcja wyglądu samego kodu a nie jego użyteczności.

Zresztą o czym dyskutujemy. W JSie mamy undefined, które jest typem, wartością albo zmienną. W ECMA 3 był jeszcze taki "ficzer" (w ECMA5 nie da się tego zrobić szczęśliwie):


undefined = "test";
typeof undefined; //string

var inna_zmienna = undefined;
typeof inna_zmienna; //string !?

Także jak dla mnie używanie typeof jest uzasadnione. Szczególnie jak się wykorzystuje IE. A ostatnimi czasy mam wrażenie, że wszyscy są super i porzucili IE i tylko ja coś tworzę dla tych 40-50% "zwykłych" użytkowników ;)

porada commented 11 years ago

@afterdesign Ale kto pisze takie kwiatki, jak var zmienna = undefined;, zamiast var zmienna;?

Dobra… Trochę zjechaliśmy z głównego tematu. Nie ma co tego kontynuować, bo i Ty masz rację, i ja mam rację.

(function(u) {
  console.log(u === undefined); //→ true
  console.log(typeof u === "undefined"); //→ true
})();
kukawski commented 11 years ago

Chciałem tylko zaznaczyć, że jeśli w nowej poradzie napiszemy o możliwości bezpośredniego porównania z globalnym undefined, którego da się/dało się nadpisać inną wartością

`function foo (bar) { console.log(bar === undefined); }``

to porada automatycznie będzie kłócić się z poradą nr 1, gdzie radzimy, żeby nie korzystać z konstruktorów Array i Object, bo te można nadpisać.

Dlatego uważam, że typeof jest odpowiednim narzędziem. Ewentualnie w grę wchodzi porównanie z lokalną zmienną z wartością niezdefiniowaną, co jednak niepotrzebnie skomplikuje poradę. Skoro typeof działa, to nie ma sensu pisać o 5 innych sposobach uzyskania tego efektu, ale z jakimiś ograniczeniami (np. nie używać tego w kontekście globalnym).

afterdesign commented 11 years ago

@porada ale zasada jest prosta - jeżeli można coś spieprzyć to znajdzie się ktoś kto to spieprzy. I widziałem nawet kwiatki, które robiły: true = false;. Trzymam się prostej logiki. Jeżeli kiedyś była możliwość skopania czegoś to nie będę tego używał i to właśnie Crockford promouje w swojej książce - a idealnym przykładem jest switch w którym "zabrania" wykorzystywania case'ów które wpadają w ten sam warunek.

Można się z tym nie zgadzać i twierdzić "po co" ale jeżeli się raz człowiek nadzieje na coś takiego (a ja się nadziałem na problemy z undefined) to się tego nie używa. I w przykładzie wyżej wystarczy, że ktoś gdzieś (w ECMA3 oczywiście) zrobił: undefined = "A popsujemy."; i warunek u === undefined nie byłby prawdziwy.

porada commented 11 years ago

@kukawski @afterdesign Okay, I stand convinced. ◕◡◕

Od razu pomyślałem, że można by napisać artykuł nt. zalet rozpoczynania pisania własnego JS od IIFE.

(function(window, document, undefined) {

  // DON’T TRUST OTHER PROGRAMMERS’ CODE

  // `window` i `document` (jeśli potrzeba) pięknie się zminifikują,
  // a `undefined` będzie niezdefiniowane.

})(window, document);
afterdesign commented 11 years ago

@porada mówisz o tym: http://javascript.pl/#tip-9 ? ;)

porada commented 11 years ago

Nie do końca. Prócz prywatnych zmiennych, ten artukuł podaje nie żadnej z zalet lokalnego scope. IMHO, rozpoczynanie od izolacji w IIFE to bardziej podejście do kodu.

afterdesign commented 11 years ago

@porada to przeedytuj albo napisz co to jest closure i do czego się wykorzystuje. Ale moim zdaniem to powinno iść razem z informacją "używaj tylko jednej zmiennej globalnej która robi za twój namespace".

porada commented 11 years ago

@afterdesign Nie chodzi mi o namespace ani o closure.


Nie chcę brzmieć, jak obrażalski smarkacz, ale jako pierwszy zrobiłem pull request z nowym tipem (pull request #6, wersja do przeczytania). Leży on w Issues już trzeci dzień bez żadnego feedbacku, co dla mnie jest brakiem szacunku do mojego czasu poświęconego na rozwój javascript.pl ze strony @ferrante. Pomijam już merge: samo naświetlenie sytuacji, dlaczego artykuł się nie nadaje, byłoby w porządku. Przez taką sytuację, nie mam zamiaru pisać kolejnego tipa.

ferrante commented 11 years ago

Dominik, chillout. Sam napisales, ze Twoj wpis moze byc za dlugi. Wyszedlem z tego samego zalozenia, ale nie mialem czasu by zrobic korekte przy merdzu. Dodatkowo wprowadzal kolejne elementy hx, ktorych nie przewidzialem w designie. Dlatego wisi - brak czasu na cos wiecej niz klikniecie w auto merge. Sprobuje sie do tego odnoesc i zaproponowac poprawki asap.

porada commented 11 years ago

@ferrante I tyle wystarczyło tyle napisać pod requestem.

ferrante commented 11 years ago

Biore to na klate, ucze sie prowadzic projekt open sourcowy. Powinienem zareagowac szybciej, wybacz. Mam nadzieje, ze to sie nie powtorzy...

afterdesign commented 11 years ago

@mmalecki Racja. Poprawię wieczorem i puszczę jeszcze raz pull requesta.

porada commented 11 years ago

@afterdesign Wystarczy rebase albo po prostu dodanie commita z poprawką do brancha, z którego submitowany jest request — zostanie on automatycznie dodany tutaj.

ferrante commented 11 years ago

Zgadzam sie z Rafalem Kukawskim. Dobra praktyka jest uzywanie typeof zawsze. Mozna tez stworzyc zmienna undefined w lokalnym scope, tak jak robia niektore frameworki.

Poza tym undefined nie da sie nadpisac tylko w strict mode btw.

medikoo commented 11 years ago

Ten tip jest trochę kontrowersyjny. Prawda jest taka, że sam JavaScript (wewnętrznie) zupełnie inaczej obchodzi się z normalizacją argumentów, czy też przypisywaniem ewentualnych wartości domyślnych. Oczywiście deweloperzy robią to na setki sposobów, między innymi jak powyżej, ale mam wrażenie najlepszą praktyką jest jednak powielanie mechanizmów jakie się znajdują w funkcjach natywnych. Sam to od jakiegoś czasu tak działam i działa to naprawdę logicznie, bez niespodzianek

Może na obrazowym przykładzie:

function (str, strOpt, num, numOpt, bool, boolOpt, arr, arrOpt, obj, objOpt) {
  // String
  str = String(str);

  // String z wartością domyślną (tego scenariusza się raczej nie stosuje)
  strOpt = (strOpt == null) ? 'default' : String(strOpt);

  // Number
  num = Number(num);

  // Number z wartością domyślną
  numOpt = isNaN(numOpt) ? 0 : Number(numOpt);

  // Boolean
  bool = Boolean(bool);

  // Boolean z wartością domyślną (podobnie, raczej się nie stosuje)
  boolOpt = (boolOpt == null) ? true : Boolean(boolOpt);

  // Array (wymagany)
  if (arr == null) {
    throw new TypeError("Cannot use null or undefined");
  }
  arr = Object(arr);
  // Dalej dopóki nie mamy pewności, że działamy na faktycznej instancji
  // konstruktora Array, działamy jak poniżej
  arr = Array.prototype.map.call(arr, cb);

  // Array opcjonalny
  arrOpt = (arrOpt == null) ? [] : Object(arrOpt);

  // Obiekt (wymagany)
  if (obj == null) {
    throw new TypeError("Cannot use null or undefined");
  }
  obj = Object(obj);

  // Obiekt opcjonalny
  objOpt = Object(objOpt);

  // Jeśli jest to w założeniu generyczna metoda (jak większość natywnych)
  // to też walidujemy kontekst:
  if (this == null) {
    throw new TypeError("Cannot use null or undefined");
  }
  // Dalej kontekst obsługujemy tak jak powyżej (patrz oczekiwany typ)
};

To oczywiście nie wszystkie kombinacje, do kompletu by trzeba było jaszcze parę linijek dopisać, chciałem tylko pokazać jak to działa w samym silniku i myślę, że wszystkich publicznych metodach naszego API warto tę samą logikę stosować, unikamy wtedy dziwnych niewiele mówiących błędów.