Tworzenie obiektu JS za pomocą Object.create (null)?

150

Znam wiele sposobów tworzenia obiektów JS, ale nie znałem tego Object.create(null).

Pytanie:

czy to dokładnie to samo, co:

var p = {}

vs

var p2 = Object.create(null);

?

Royi Namir
źródło

Odpowiedzi:

199

Nie są równoważne. {}.constructor.prototype == Object.prototypewhile Object.create(null)nie dziedziczy niczego, a zatem nie ma żadnych właściwości.

Innymi słowy: obiekt JavaScript dziedziczy Object domyślnie, chyba że wyraźnie go utworzyć z zerową jako prototyp, jak: Object.create(null).

{}zamiast tego byłby równoważny Object.create(Object.prototype).


W Chrome Devtool widać, że Object.create(null)nie ma on żadnej __proto__właściwości, podczas gdy {}tak.

wprowadź opis obrazu tutaj

Peter Herdenborg
źródło
99

Zdecydowanie nie są równoważne. Piszę tę odpowiedź, aby pełniej wyjaśnić, dlaczego ma to znaczenie.

  1. var p = {};

    Tworzy obiekt, który dziedziczy właściwości i metody z Object.

  2. var p2 = Object.create(null);

    Tworzy obiekt, który niczego nie dziedziczy.

Jeśli używasz obiektu jako mapy i tworzysz obiekt za pomocą powyższej metody 1, musisz zachować szczególną ostrożność podczas wyszukiwania na mapie. Ponieważ właściwości i metody z Objectsą dziedziczone, kod może zostać uruchomiony w przypadku, gdy w mapie znajdują się klucze, których nigdy nie wstawiono. Na przykład, gdybyś przeszukał toString, znalazłbyś funkcję, nawet jeśli nigdy nie umieściłeś tam tej wartości. Możesz to obejść w ten sposób:

if (Object.prototype.hasOwnProperty.call(p, 'toString')) {
    // we actually inserted a 'toString' key into p
}

Zauważ, że można coś przypisać p.toString, po prostu nadpisuje odziedziczoną toStringfunkcję p.

Zauważ, że nie możesz tego zrobić tylko p.hasOwnProperty('toString')dlatego, że mogłeś wstawić klucz „hasOwnProperty” do p, więc zmuszamy go do użycia implementacji w Object.

Z drugiej strony, jeśli użyjesz metody 2 powyżej, nie będziesz musiał martwić się o Objectpojawienie się rzeczy na mapie.

Nie możesz sprawdzić istnienia właściwości za pomocą prostego ifprzykładu:

// Unreliable:
if (p[someKey]) {
    // ...
}

Wartością może być pusty ciąg, może to być false, lub null, lub undefined, lub 0, lub NaN, itd. Aby sprawdzić, czy właściwość w ogóle istnieje, nadal musiałbyś jej użyć Object.prototype.hasOwnProperty.call(p, someKey).

doug65536
źródło
6
Prostszą alternatywą sprawdzania istnienia nieruchomości jest:if (someKey in p) {
mrcrowl
2
@mrcrowl Tylko jeśli używali Object.create(null). Wolę nie przyjmować takich założeń, nawet jeśli miałeś absolutną rację, że użył Object.create(null), kod może się zmienić, obiekt można zastąpić takim, który Objectw pewnym momencie dziedziczy . hasOwnPropertyzawsze działa.
doug65536
1
Uważam, że uważanie na coś takiego powinno być niepotrzebne. Doceniam twoją odpowiedź, ale dokumentacja powinna zapewnić interfejs API niezbędny do pracy z dowolnym kodem, z którym pracujesz. Jeśli pobierasz losowy kod z github, możesz go rozwidlić i być bezpiecznym przed mniej udokumentowanymi aktualizacjami. Nie wspominając o tym, że {}jest o wiele bardziej rozpowszechniony niż to Object.create(null), że jeśli twój kod przypadkowo przejmie w tym momencie odziedziczoną właściwość, prawdopodobnie będziesz się martwić o znacznie większe błędy. Widzę tylko osoby używające Object.create (null) jako niewielkiej optymalizacji.
aaaaaa
Podwójna negacja !!p[key]działa dobrze z Object.create(null). Ale też hasKey = (key, input) => Object.prototype.hasOwnProperty.call(input, key)nie jest zły
andreid
> Zauważ, że nie możesz po prostu zrobić p.hasOwnProperty ('toString'), ponieważ mogłeś wstawić klucz "hasOwnProperty" do p, więc zmuszamy go do użycia implementacji w Object. To jest niepotrzebne. W takim przypadku nie możesz użyć żadnych metod w, pponieważ każda metoda może zostać wstawiona i dlatego nie jest bezpieczna.
xianshenglu
1

Tworzenie obiektów za pomocą {}use spowoduje utworzenie obiektu, którego prototyp Object.prototypedziedziczy podstawowe funkcje z Objectprototypu, podczas gdy tworzenie obiektów za pomocą użycia Object.create(null)spowoduje utworzenie pustego obiektu, którego prototyp jest pusty.

Praveen Kishor
źródło
0

Jeśli ktoś szuka implementacji Object.create(null), wystarczy wiedzieć, jak to działa. Jest napisany w __proto__sposób niestandardowy i dlatego nie polecam tego .

function objectCreateMimic()
{
  /*optional parameters: prototype_object, own_properties*/
  var P = arguments.length>0?arguments[0]:-1;
  var Q = arguments.length>1?arguments[1]:null;
  var o = {};
  if(P!==null && typeof P === "object")
  {
    o.__proto__ = P;
  }
  else if(P===null)
  {
    o.__proto__ = null;
  }
  if(Q!==null && typeof Q === "object")
  {
   for(var key in Q)
   {
     o[key] = Q[key];
   }
  }
  return o;
}

Uwaga : napisałem to z ciekawości i jest napisane tylko prostymi słowami, na przykład nie przenoszę deskryptorów właściwości z drugiego obiektu do obiektu zwracanego.

Aravind
źródło
1
Zauważ, że od wydania ECMAScript latem, __proto__będzie teraz oficjalnie częścią języka.
Chiru
1
Dlaczego -1w arguments.length>0?arguments[0]:-1;?
happy_marmoset
@happy_marmoset późna odpowiedź, ale wygląda na to, że jest to po prostu niezerowy symbol zastępczy, więc Objectprototyp jest zachowywany, jeśli nie zostanie podany pierwszy argument. Nazwy zmiennych mogłyby być tutaj znacznie lepsze.
Mike Hill
Ponadto drugi parametr powinien opisywać deskryptory właściwości, a nie same właściwości. Zobacz odniesienie tutaj: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Mike Hill
0

Kiedy tworzysz obiekt za pomocą Object.create (null), oznacza to, że tworzysz obiekt bez prototypu. null oznacza tutaj koniec łańcucha prototypów. Niemniej jednak podczas tworzenia obiektu takiego jak {} zostanie dodany prototyp obiektu. Dlatego są to dwa różne obiekty, jeden z prototypem, drugi bez prototypu. Mam nadzieję, że to pomoże

user13624607
źródło