Cảnh báo: Đây là một bài viết dài.
Hãy giữ nó đơn giản. Tôi muốn tránh phải tiền tố toán tử mới mỗi lần tôi gọi hàm tạo trong JavaScript. Điều này là do tôi có xu hướng quên nó, và mã của tôi rất tệ.
Cách đơn giản xung quanh đây là ...
function Make(x) {
if ( !(this instanceof arguments.callee) )
return new arguments.callee(x);
// do your stuff...
}
Nhưng, tôi cần điều này để chấp nhận biến không. tranh luận, như thế này ...
m1 = Make();
m2 = Make(1,2,3);
m3 = Make('apple', 'banana');
Giải pháp tức thời đầu tiên dường như là phương pháp 'áp dụng' như thế này ...
function Make() {
if ( !(this instanceof arguments.callee) )
return new arguments.callee.apply(null, arguments);
// do your stuff
}
Tuy nhiên, đây là SAI - đối tượng mới được truyền cho apply
phương thức và KHÔNG cho hàm tạo của chúng ta arguments.callee
.
Bây giờ, tôi đã đưa ra ba giải pháp. Câu hỏi đơn giản của tôi là: cái nào có vẻ tốt nhất. Hoặc, nếu bạn có một phương pháp tốt hơn, hãy nói với nó.
Đầu tiên - sử dụng eval()
để tự động tạo mã JavaScript gọi hàm tạo.
function Make(/* ... */) {
if ( !(this instanceof arguments.callee) ) {
// collect all the arguments
var arr = [];
for ( var i = 0; arguments[i]; i++ )
arr.push( 'arguments[' + i + ']' );
// create code
var code = 'new arguments.callee(' + arr.join(',') + ');';
// call it
return eval( code );
}
// do your stuff with variable arguments...
}
Thứ hai - Mọi đối tượng đều có thuộc __proto__
tính là liên kết 'bí mật' với đối tượng nguyên mẫu. May mắn là tài sản này là có thể ghi.
function Make(/* ... */) {
var obj = {};
// do your stuff on 'obj' just like you'd do on 'this'
// use the variable arguments here
// now do the __proto__ magic
// by 'mutating' obj to make it a different object
obj.__proto__ = arguments.callee.prototype;
// must return obj
return obj;
}
Thứ ba - Đây là một cái gì đó tương tự như giải pháp thứ hai.
function Make(/* ... */) {
// we'll set '_construct' outside
var obj = new arguments.callee._construct();
// now do your stuff on 'obj' just like you'd do on 'this'
// use the variable arguments here
// you have to return obj
return obj;
}
// now first set the _construct property to an empty function
Make._construct = function() {};
// and then mutate the prototype of _construct
Make._construct.prototype = Make.prototype;
eval
Giải pháp có vẻ vụng về và đi kèm với tất cả các vấn đề của "tệ nạn xấu xa".__proto__
giải pháp là không chuẩn và "Trình duyệt tuyệt vời của mIsERY" không tôn trọng nó.Giải pháp thứ ba có vẻ quá phức tạp.
Nhưng với tất cả ba giải pháp trên, chúng ta có thể làm một cái gì đó như thế này, mà chúng ta không thể ...
m1 = Make();
m2 = Make(1,2,3);
m3 = Make('apple', 'banana');
m1 instanceof Make; // true
m2 instanceof Make; // true
m3 instanceof Make; // true
Make.prototype.fire = function() {
// ...
};
m1.fire();
m2.fire();
m3.fire();
Vì vậy, hiệu quả của các giải pháp trên cung cấp cho chúng tôi các hàm tạo "thực" chấp nhận biến số không. tranh luận và không yêu cầu new
. Những gì bạn có về điều này.
- CẬP NHẬT -
Một số người đã nói "chỉ cần ném một lỗi". Câu trả lời của tôi là: chúng tôi đang thực hiện một ứng dụng nặng với hơn 10 nhà xây dựng và tôi nghĩ sẽ hiệu quả hơn nhiều nếu mọi nhà xây dựng có thể "xử lý" một cách thông minh lỗi đó mà không ném thông báo lỗi trên bảng điều khiển.
Make()
mà không có new
chữ viết hoa và do đó, nó cho rằng đó là một công cụ xây dựng
new
? Bởi vì nếu đó là cái sau, có lẽ bạn đang hỏi sai trang web. Nếu đó là trước đây, bạn có thể không muốn loại bỏ các đề xuất liên quan đến việc sử dụng mới và phát hiện lỗi quá nhanh ... Nếu ứng dụng của bạn thực sự "nặng", điều cuối cùng bạn muốn là một số cơ chế xây dựng quá mức để làm chậm nó. new
, đối với tất cả các flack nó nhận được, là khá nhanh chóng.