2012-12-12 42 views
10

Tôi muốn thay đổi hành vi của đối tượng Ngày chuẩn. Các năm giữa 0..99 được truyền cho hàm tạo nên được hiểu là fullYear (không thêm 1900). Nhưng chức năng sau của tôi không hoạt độngJavaScript: ghi đè 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; 
} 

Tại sao nó không bao giờ được gọi? Làm thế nào bạn sẽ giải quyết vấn đề này?

+0

Bài viết http://pivotallabs.com/users/pjaros/blog/articles/1368-javascript-constructors-prototypes-and-the-new-keyword đã giúp tôi hiểu nhiều về cách tạo đối tượng trong Javascript hoạt động. Để ghi đè 'Date.prototype.constructor' không giúp trước khi tạo đối tượng. Sẽ cố gắng viết lại chức năng 'Ngày' và cho bạn biết –

+0

Mã số ' var oDateFnctn = oDateFnctn || Ngày; chức năng Date() { var d = new oDateFnctn (đối số); var ac = arguments.length; var ay = arguments [0]; nếu (((ac == 3) || (ac == 6)) && (ay <100) && (ay> = 0)) { d.setFullYear (ay); } trả lại d; } ' kết quả trong lỗi ** Phạm vi không kích hoạt: Lỗi cuộc gọi tối đa đã vượt quá **. Tôi hiện đang bị kẹt –

+1

Chỉ ** không ghi đè ** các nhà xây dựng gốc với hành vi ** tùy chỉnh **. Không bao giờ. Xây dựng chức năng của riêng bạn cho điều đó. – Bergi

Trả lời

30

Lý do không bao giờ được gọi là vì bạn đang thay đổi thuộc tính constructor trên Date.prototype. Tuy nhiên, bạn có thể vẫn đang tạo ngày sử dụng mã new Date(). Vì vậy, nó không bao giờ sử dụng constructor của bạn. Những gì bạn thực sự muốn làm là tạo ngày của bạn constructor riêng:

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; 
} 

Sau đó, bạn có thể tạo ngày mới của bạn như thế này:

var d = MyDate(); 

Edit: Thay vì sử dụng Date.apply tôi thà sử dụng theo chức năng instantiate cho phép bạn 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)); 
} 

Đây là cách tôi sẽ thực hiện các nhà xây dựng ngày mới:

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: Nếu bạn muốn ghi đè Ngày constructor mẹ đẻ thì bạn phải làm một cái gì đó như thế này:

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

Cảm ơn bạn Nếu tôi muốn tạo Ngày bằng 'var d = Date()' thay vì một lớp riêng? –

+1

Tôi đã thêm một ví dụ về cách thực hiện điều đó. Xem fiddle này: http://jsfiddle.net/ubmzs/ –

+3

Một năm sau, OP thực sự nên chấp nhận câu trả lời. – ach

2

cõng trên Aadit Nhà xây dựng ngày sinh bản địa của M Shah đã ghi đè - đây phải là câu trả lời nhưng tôi không có đủ đại diện SO cho điều đó - như @sakthi đã đề cập, bạn sẽ mất phương thức Ngày gốc bằng cách thực hiện theo cách đó. Điều này hút một chút bởi vì phương thức Date không thể đếm được, vì vậy bạn phải thực hiện một chút hack để sao chép chúng.

Trước hết, tôi khuyên bạn nên không làm theo cách này trừ khi bạn phải làm như vậy. Trong trường hợp của tôi, tôi đã làm việc trong một ứng dụng với một loạt các mã kế thừa đã được xây dựng ngày sử dụng định dạng "m-d-yyyy", hoạt động trong chrome nhưng không safari. Tôi không thể thực hiện tìm/thay thế trong ứng dụng, bởi vì có rất nhiều trường hợp các chuỗi ngày tháng bị kéo ra khỏi lớp dịch vụ hoặc cơ sở dữ liệu. Vì vậy, tôi quyết định ghi đè lên hàm tạo ngày trong trường hợp có một đối số datestring trong định dạng "m-d-yyyy". Tôi muốn nó được tối thiểu xâm lấn nhất có thể, để nó hoạt động như một ngày bình thường khác.

Dưới đây là những thay đổi của tôi - nó sẽ cho phép bạn ghi đè ngày tháng với một số thay đổi đối với hàm tạo, nhưng mọi thứ khác giống nhau. Bạn sẽ muốn thay đổi constructor MyDate trước khi instantiate được gọi để làm bất cứ điều gì bạn muốn constructor xử lý. Điều này sẽ xảy ra TRƯỚC KHI hệ thống Hàm tạo ngày được áp dụng.

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); 

tham khảo:

+0

Đây là giải pháp tuyệt vời để thay thế hoàn toàn 'Ngày' mà không ảnh hưởng đến mã khác. Một điều tôi nhận thấy là điều này sẽ không thành công 'Date Date instanceof Date', điều này có thể gây rắc rối trong một số trường hợp. Bạn có thể sửa lỗi này bằng cách thêm 'MyDate.prototype = Date.prototype;' ngay trước 'trả về MyDate;' theo thử nghiệm ngắn gọn của tôi không phá vỡ bất kỳ chức năng nào khác. –

0

Với tham chiếu đến các kỹ thuật nêu tại Matthew Albert' s, ngoài điểm mà Dan Hlavenka đăng, có thêm một kịch bản thử nghiệm thất bại. Xem mã sau:

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

Trong một dự án kế thừa, có khả năng trường hợp trên có thể phá vỡ một vài trường hợp. Ngoài những điều trên và những gì Dan Hlavenka đã chỉ ra, tôi đồng ý rằng đây là giải pháp hoàn chỉnh nhất cho đến nay.