2012-12-12 9 views
10

Chciałbym zmienić zachowanie standardowego obiektu Date. Lata między 0..99 przekazane do konstruktora należy interpretować jako fullYear (bez dodatku z 1900). Ale moja następująca funkcja nie działaJavaScript: przesłonięcie Date.prototype.constructor

var oDateConst = Date.prototype.constructor; // save old contructor 

Date.prototype.constructor = function() { 
    var d = oDateConst.apply(oDateConst, arguments); // create object with it 
    if (((arguments.length == 3) || (arguments.length == 6)) 
     && ((arguments[0] < 100) && (arguments[0] >= 0))) { 
     d.setFullYear(arguments[0]); 
    } 
    return d; 
} 

Dlaczego nigdy nie jest wywoływana? Jak rozwiązać ten problem?

+0

Artykuł http://pivotallabs.com/users/pjaros/blog/articles/1368-javascript-constructors-prototypes-and-new-keyword pomógł mi zrozumieć, jak działa tworzenie obiektów w Javascript. Przesłonięcie 'Date.prototype.constructor' nie pomaga przed utworzeniem obiektu. Spróbuję ponownie napisać funkcję 'Date' i powiadomić o tym –

+0

Kod ' var oDateFnctn = oDateFnctn || Data; funkcja Date() { var d = new oDateFnctn (argumenty); var ac = arguments.length; var ay = argumenty [0]; if (((ac == 3) || (ac == 6)) && (ay <100) && (ay> = 0)) { d.setFullYear (ay); } return d; } ' wyniki w ** Uncaught RangeError: Przekroczono maksymalny rozmiar stosu połączeń ** błąd. Utknąłem teraz –

+1

Po prostu ** nie zastępuj ** rodzimych konstruktorów ** niestandardowym ** zachowaniem. Nigdy. Zbuduj dla tego swoją własną funkcję. – Bergi

Odpowiedz

30

Powodem, dla którego nigdy nie zostanie wywołany, jest zmiana właściwości constructor na Date.prototype. Prawdopodobnie nadal tworzysz datę, używając kodu new Date(). Więc nigdy nie używa twojego konstruktora. Co naprawdę chcesz zrobić, to tworzyć własne Data konstruktor:

function MyDate() { 
    var d = Date.apply(Date, arguments); 
    if ((arguments.length == 3 || arguments.length == 6) 
     && (arguments[0] < 100 && arguments[0] >= 0)) { 
     d.setFullYear(arguments[0]); 
    return d; 
} 

Następnie można utworzyć nową datę tak:

var d = MyDate(); 

Edit: Zamiast Date.apply wolałbym użyć po instantiate funkcja, która pozwala na apply arguments to a constructor function:

var bind = Function.bind; 
var unbind = bind.bind(bind); 

function instantiate(constructor, args) { 
    return new (unbind(constructor, null).apply(null, args)); 
} 

W ten sposób chciałbym wdrożyć nową datę konstruktora:

function myDate() { 
    var date = instantiate(Date, arguments); 
    var args = arguments.length; 
    var arg = arguments[0]; 

    if ((args === 3 || args == 6) && arg < 100 && arg >= 0) 
     date.setFullYear(arg); 
    return date; 
} 

Edit: Jeśli chcesz zastąpić natywną Data konstruktora musisz zrobić coś takiego:

Date = function (Date) { 
    MyDate.prototype = Date.prototype; 

    return MyDate; 

    function MyDate() { 
     var date = instantiate(Date, arguments); 
     var args = arguments.length; 
     var arg = arguments[0]; 

     if ((args === 3 || args == 6) && arg < 100 && arg >= 0) 
      date.setFullYear(arg); 
     return date; 
    } 
}(Date); 
+0

Dziękuję Co zrobić, jeśli chcę utworzyć datę przy użyciu 'var d = Date()' zamiast własnej klasy? –

+1

Dodałem przykład, jak to zrobić. Zobacz to skrzypce: http://jsfiddle.net/ubmzs/ –

+3

Rok później OP powinien naprawdę zaakceptować odpowiedź. – ach

2

piggybacking na Aadit Zastępca natywnego konstruktora daty Shah - to powinna być odpowiedź, ale nie mam dostatecznego przedstawiciela SO - jak wspomniał @sakthi, utracisz swoje natywne metody Date, robiąc to w ten sposób. Trochę to przynosi, ponieważ metody dat są nieprzeliczalne, więc musisz zaimplementować trochę hack, aby je sklonować.

Po pierwsze, polecam nie robienie tego w ten sposób, chyba że musisz. W moim przypadku pracowałem w aplikacji z pakietem starszego kodu, który konstruował daty za pomocą formatu "m-d-yyyy", który działa w chrome, ale nie safari. Nie mogłem po prostu znaleźć/zastąpić w aplikacji, ponieważ było wiele przypadków, w których ciągi dat były pobierane z warstwy usługi lub bazy danych. Zdecydowałem się więc przesłonić konstruktor Date w przypadku, gdy istnieje argument datownika w formacie "m-d-rrrr". Chciałem, aby było tak małoinwazyjne, jak to tylko możliwe, aby działało jak normalnie.

Oto moje zmiany - powinny pozwolić na zastąpienie daty pewnymi zmianami w konstruktorze, ale wszystko inne to samo. Będziesz chciał zmienić konstruktor MyDate, zanim instancja zostanie wywołana, aby zrobić to, co chcesz, aby konstruktor obsługiwał. Nastąpi to ZANIM zostanie zastosowany konstruktor daty systemowej.

var bind = Function.bind; 
var unbind = bind.bind(bind); 

function instantiate(constructor, args) { 
    return new (unbind(constructor, null).apply(null, args)); 
} 

Date = function (Date) { 

    // copy date methods - this is a pain in the butt because they're mostly nonenumerable 
    // Get all own props, even nonenumerable ones 
    var names = Object.getOwnPropertyNames(Date); 
    // Loop through them 
    for (var i = 0; i < names.length; i++) { 
     // Skip props already in the MyDate object 
     if (names[i] in MyDate) continue; 
     // Get property description from o 
     var desc = Object.getOwnPropertyDescriptor(Date, names[i]); 
     // Use it to create property on MyDate 
     Object.defineProperty(MyDate, names[i], desc); 
    } 

    return MyDate; 

    function MyDate() { 
     // we only care about modifying the constructor if a datestring is passed in 
     if (arguments.length === 1 && typeof (arguments[0]) === 'string') { 
      // if you're adding other date transformations, add them here 

      // match dates of format m-d-yyyy and convert them to cross-browser-friendly m/d/yyyy 
      var mdyyyyDashRegex = /(\d{1,2})-(\d{1,2})-(\d{4})/g; 
      arguments[0] = arguments[0].replace(mdyyyyDashRegex, function (match, p1, p2, p3) { 
       return p1 + "/" + p2 + "/" + p3; 
      }); 
     } 

     // call the original Date constructor with whatever arguments are passed in here 
     var date = instantiate(Date, arguments); 

     return date; 
    } 
}(Date); 

referencje:

+0

Jest to świetne rozwiązanie do całkowitego zastąpienia 'Date' bez wpływu na inny kod. Zauważyłem tylko, że to nie powiedzie się "nowa data wystąpienia daty", co może powodować problemy w niektórych przypadkach. Możesz to naprawić, dodając 'MyDate.prototype = Date.prototype;' tuż przed 'return MyDate;' co według moich krótkich testów nie łamie żadnej innej funkcjonalności. –

0

Nawiązując do wspomnianej techniki w Matthew Albert” s post, poza tym, co napisał Dan Hlavenka, jest jeszcze jeden scenariusz testowy, który zawodzi. Zobacz poniższy kod:

typeof Date() == typeof new Date()  //Should be false, but it returns true 

W przypadku wcześniejszego projektu istnieje szansa, że ​​powyższy scenariusz może spowodować kilka scenariuszy. Oprócz powyższego i tego, co wskazał Dan Hlavenka, zgadzam się, że jest to najbardziej kompletne rozwiązanie do tej pory.

Powiązane problemy