2009-12-24 8 views
24

nhờ câu trả lời tuyệt vời cho this question Tôi hiểu cách gọi hàm javascript bằng vararg.javascript áp dụng trên hàm tạo, ném "thông số chính thức không đúng định dạng"

bây giờ tôi đang tìm cách để sử dụng áp dụng với một constructor

Tôi tìm thấy một số thông tin thú vị on this post.

nhưng mã của tôi là ném lỗi

nỗ lực 1:

var mid_parser = new Parser.apply(null, mid_patterns); 

lỗi:

TypeError: Function.prototype.apply called on incompatible [object Object] 

nỗ lực 2: nỗ lực 1:

var mid_parser = new Parser.prototype.apply(null, mid_patterns); 

lỗi:

TypeError: Function.prototype.apply called on incompatible [object Object] 

nỗ lực 2:

function Parser() 
{ 
    this.comparemanager = new CompareManager(arguments); 
} 

mid_patterns = [objA,objB,objC] 
var mid_parser = new Parser(); 
Parser.constructor.apply(mid_parser, mid_patterns); 

lỗi:

syntax_model.js:91: SyntaxError: malformed formal parameter 

nỗ lực 3:

var mid_parser = Parser.apply(null, mid_patterns); 

lỗi:

TypeError: this.init is undefined // init is a function of Parser.prototype 

Tôi có một workaround cho thời gian được:

function Parser() 
{ 
    if(arguments.length) this.init.call(this,arguments); // call init only if arguments 
} 
Parser.prototype = { 
    //... 
    init: function() 
    { 
     this.comparemanager = new CompareManager(arguments); 
    } 
    //... 
} 

var normal parser = new Parser(objA,objB,objC); 

mid_patterns = [objA,objB,objC] 
var dyn_parser = new Parser(); 
dyn_parser.init.apply(dyn_parser, mid_patterns); 

này hoạt động khá tốt, nhưng nó không phải là sạch và phổ thông như tôi muốn.

là nó có thể trong javascript để gọi một nhà xây dựng với varargs?

Trả lời

17

Bạn có thể sử dụng áp dụng và vượt qua một đối tượng rỗng như là đối số this:

var mid_parser = {}; 
Parser.apply(mid_parser, mid_patterns); 

Nhưng giải pháp đó sẽ không chăm sóc về chuỗi nguyên mẫu.

Bạn có thể tạo một đối tượng Parser, sử dụng toán tử new, nhưng mà không đi tranh luận, và sau đó sử dụng apply để chạy lại hàm xây dựng:

var mid_parser = new Parser(); 
Parser.apply(mid_parser, mid_patterns); 
+0

+1, cảm ơn tuyệt vời –

+0

Rất thú vị. Tôi đã chơi xung quanh với điều này một chút và nó hoạt động rất độc đáo * trong một số trường hợp *. Không phải tất cả các nhà thầu đều hỗ trợ phương pháp này. 'Date', ví dụ:' var date = new Date; Date.call (ngày, 1984, 3, 26) '. – davidchambers

+1

Function.apply (new Function(), arg) /* Là một giải pháp một dòng */ – TERMtm

7

Bạn có thể khai thác thực tế là bạn có thể khởi dựng chuỗi sử dụng apply(...) để đạt được điều này, mặc dù điều này đòi hỏi phải tạo ra một lớp proxy.Chức năng construct() bên dưới cho phép bạn làm:

var f1 = construct(Foo, [2, 3]); 
// which is more or less equivalent to 
var f2 = new Foo(2, 3); 

các construct() chức năng:

function construct(klass, args) { 

    function F() { 
    return klass.apply(this, arguments[0]); 
    }; 

    F.prototype = klass.prototype; 

    return new F(args); 

} 

Một số mẫu mã mà sử dụng nó:

function Foo(a, b) { 
    this.a = a; this.b = b; 
} 

Foo.prototype.dump = function() { 
    console.log("a = ", this.a); 
    console.log("b = ", this.b); 
}; 

var f = construct(Foo, [7, 9]); 

f.dump(); 
+1

Điều này không hoàn toàn xử lý tất cả các trường hợp. Bạn cần trả về giá trị từ klass.apply trong hàm F(): 'function F() {return klass.apply (this, arguments [0]); } '. Điều này xử lý các trường hợp hàm xây dựng trả về giá trị được sử dụng làm cá thể. –

+0

@MonroeThomas Điểm tốt, đã được cập nhật. – mjs

11

Một giải pháp tốt hơn là tạo ra một hàm constructor tạm thời , áp dụng nguyên mẫu của lớp mà bạn muốn (để đảm bảo chuỗi nguyên mẫu được giữ nguyên) và sau đó áp dụng hàm tạo theo cách thủ công. Điều này ngăn cản kêu gọi các nhà xây dựng hai lần không cần thiết ...

applySecond = function(){ 
    function tempCtor() {}; 
    return function(ctor, args){ 
     tempCtor.prototype = ctor.prototype; 
     var instance = new tempCtor(); 
     ctor.apply(instance,args); 
     return instance; 
    } 
}(); 

Tôi đã thử nghiệm hiệu suất và thấy rằng phương pháp này là, trên thực tế, chậm hơn một chút trong rất trường hợp đơn giản. Tuy nhiên, nó chỉ mất việc xây dựng một đối tượng Date() duy nhất trong hàm khởi tạo để điều này có hiệu quả hơn. Ngoài ra, đừng quên rằng một số nhà xây dựng có thể ném ngoại lệ nếu không có tham số nào được thông qua, do đó, điều này cũng chính xác hơn.

mã xác nhận của tôi:

var ExpensiveClass = function(arg0,arg1){ 
    this.arg0 = arg0; 
    this.arg1 = arg1; 
    this.dat = new Date(); 
} 

var CheapClass = function(arg0,arg1){ 
    this.arg0 = arg0; 
    this.arg1 = arg1; 
} 

applyFirst = function(ctor, args){ 
    var instance = new ctor(); 
    ctor.apply(instance, args); 
    return instance; 
} 

applySecond = function(){ 
    function tempCtor() {}; 
    return function(ctor, args){ 
     tempCtor.prototype = ctor.prototype; 
     var instance = new tempCtor(); 
     ctor.apply(instance,args); 
     return instance; 
    } 
}(); 

console.time('first Expensive'); 
for(var i = 0; i < 10000; i++){ 
    test = applyFirst(ExpensiveClass ,['arg0','arg1']); 
} 
console.timeEnd('first Expensive'); 

console.time('second Expensive'); 
for(var i = 0; i < 10000; i++){ 
    test = applySecond(ExpensiveClass ,['arg0','arg1']); 
} 
console.timeEnd('second Expensive'); 

console.time('first Cheap'); 
for(var i = 0; i < 10000; i++){ 
    test = applyFirst(CheapClass,['arg0','arg1']); 
} 
console.timeEnd('first Cheap'); 

console.time('second Cheap'); 
for(var i = 0; i < 10000; i++){ 
    test = applySecond(CheapClass,['arg0','arg1']); 
} 
console.timeEnd('second Cheap'); 

Kết quả:

first Expensive: 76ms 
second Expensive: 66ms 
first Cheap: 52ms 
second Cheap: 52ms 
+0

Tôi sẽ định lượng tốt hơn chỉ trong trường hợp hàm "ctor" đắt tiền. lý do là, rằng implimentation này liên quan đến việc gọi hai chức năng là tốt, chỉ tempCtor đó là một hàm rỗng. Cuộc gọi áp dụng cho ctor (ctor.apply) sẽ thực thi như một hàm bình thường, chỉ với từ khóa mới, nó sẽ có chi phí xây dựng một đối tượng mới. –

+0

Tôi đã cập nhật bài đăng của mình để bao gồm một số bài kiểm tra. Trong khi đúng là với các nhà xây dựng rất đơn giản, phương pháp này là chậm hơn, nó là tầm thường để làm cho các nhà xây dựng "đắt" đủ để đảm bảo phương pháp này. Ngoài ra, như đã đề cập trong bản cập nhật của tôi, phương pháp cải tiến này xử lý các nhà thầu ném các ngoại lệ mà đối số không được cung cấp hoặc loại không chính xác. – jordancpaul

+0

đã cập nhật bài đăng của tôi một lần nữa. Nó bây giờ là nhanh như nhau trong trường hợp đơn giản, và thậm chí nhanh hơn trong trường hợp đắt tiền. – jordancpaul

0

Để hoàn @CMS giải pháp bảo tồn và chuỗi nguyên mẫu, bạn có thể làm điều này:

var mid_parser = {}; 
mid_parser.__proto__ = Parser.prototype; 
Parser.apply(mid_parser, mid_patterns); 

Là một bên lưu ý, nó sẽ không hoạt động với IE 8-.