(Załóżmy, że istnieje dobry powód, aby tego chcieć.) Zobacz koniec pytania, jeśli chcesz przeczytać dobry powód .)Uzyskaj taki sam wynik, jak dla pętli for..in, bez pętli for..in
Chciałbym uzyskać ten sam wynik co pętla for in
, ale bez przy użyciu tej konstrukcji językowej. Przez wynik rozumiem tylko tablicę nazw właściwości (nie muszę odtwarzać zachowania, które by się stało, gdybym zmodyfikował obiekt podczas iteracji nad nim).
Aby umieścić pytanie do kodu, chciałbym wdrożyć tę funkcję bez for in
:
function getPropertiesOf(obj) {
var props = [];
for (var prop in obj)
props.push(prop);
return props;
}
z mojego zrozumienia specyfikacji około the for in statement i the Object.keys
method ECMAScript 5.1, wydaje się następujące realizacja powinny być poprawne :
function getPropertiesOf(obj) {
var props = [];
var alreadySeen = {};
// Handle primitive types
if (obj === null || obj === undefined)
return props;
obj = Object(obj);
// For each object in the prototype chain:
while (obj !== null) {
// Add own enumerable properties that have not been seen yet
var enumProps = Object.keys(obj);
for (var i = 0; i < enumProps.length; i++) {
var prop = enumProps[i];
if (!alreadySeen[prop])
props.push(prop);
}
// Add all own properties (including non-enumerable ones)
// in the alreadySeen set.
var allProps = Object.getOwnPropertyNames(obj);
for (var i = 0; i < allProps.length; i++)
alreadySeen[allProps[i]] = true;
// Continue with the object's prototype
obj = Object.getPrototypeOf(obj);
}
return props;
}
Chodzi o to, aby chodzić wyraźnie łańcuch prototypów i używać Object.keys
uzyskać własne właściwości w każdym obiekcie łańcucha. Wykluczamy nazwy obiektów już widziane w poprzednich obiektach w łańcuchu, w tym także wtedy, gdy były postrzegane jako nieprzeliczalne. Sposób ten powinien również przestrzegać the additional guarantee mentioned on MDN:
Sposób
Object.keys()
zwraca tablicę własnych właściwości przeliczalnych danego obiektu, w takiej samej kolejności jak przewidziany przezfor...in
pętli [...].
(podkreślenie moje)
grałem trochę z tej realizacji, a ja nie byłem w stanie go złamać.
Więc pytanie:
Czy moja analiza jest prawidłowy? Czy mogę przeoczyć szczegół specyfikacji, która spowodowałaby, że ta implementacja byłaby niepoprawna?
Czy znasz inny sposób, aby to zrobić, który byłby zgodny z konkretną kolejnością implementacji for in
we wszystkich przypadkach?
Uwagi:
- Nie dbam o ECMAScript < 5.1.
- Nie dbam o wydajność (może to być katastrofalne).
Edycja: aby zaspokoić ciekawość @ lexicore'S (ale naprawdę nie część pytania), to dobry powód jest następujący. Opracowuję kompilator do JavaScript (od Scala), a konstrukcja językowa for in
nie jest częścią rzeczy, które chcę wspierać bezpośrednio w pośredniej reprezentacji mojego kompilatora. Zamiast tego mam "wbudowaną" funkcję getPropertiesOf
, która jest zasadniczo tym, co pokazuję jako pierwszy przykład. Próbuję pozbyć się jak największej liczby wbudowanych, zastępując je implementacjami "przestrzeni użytkownika" (napisanymi w Scali).Aby uzyskać wydajność, nadal mam optymalizator, który czasami "intrinsifies" niektórych metod, i w tym przypadku byłoby to intrinsify getPropertiesOf
z wydajnym pierwszym wdrożeniem. Jednak aby pośrednia reprezentacja była dobra i działała, gdy optymalizator jest wyłączony, potrzebuję prawdziwej implementacji tej funkcji, bez względu na koszt wydajności, o ile jest on poprawny. I w tym przypadku nie mogę użyć for in
, ponieważ mój IR nie może reprezentować tego konstruktu (ale mogę wywoływać dowolne funkcje JavaScript na dowolnych obiektach, np. Object.keys
).
'(obj === null || obj === undefined)' jest takie samo jak 'obj == null'. Czy z ciekawości jest jakiś powód, dla którego używasz bardziej gadatliwej postaci? – gilly3
@ gilly3 Ponieważ '==' jest złe. Dla mniej upartej odpowiedzi: z tego samego powodu, dla którego nie mogę użyć 'for in': IR intencjonalnie nie obsługuje' == '(ponieważ jest złe ...). – sjrd
Interesujące. Czuję, że '==' jest faktycznie * siłą * języka, który powinien być używany rozważnie. – gilly3