2015-10-18 16 views
5

Tôi đã sử dụng các chức năng sau đây để tạo ra các trường hợp của các lớp học chưa được biết đối với một số thời gian:constr.apply (this, args) trong các lớp ES6

Kernel.prototype._construct = function (constr, args) { 
    function F() { 
    constr.apply(this, args); // EXCEPTION! 
    } 
    F.prototype = constr.prototype; 
    return new F(); 
}; 

Nếu tôi sử dụng nguyên mẫu mọi thứ hoạt động:

function Person(name, surname) { 
    this.name = name; 
    this.surname = surname; 
} 

var person = Kernel._construct(Person, ["name", "surname"]); // WORKS! 

Tuy nhiên, một số người đang sử dụng thư viện của tôi bằng cách sử dụng các lớp gốc ES6 trong nút v4 +:

class Person { 
    constructor(name, surname) { 
    this.name = name; 
    this.surname = surname; 
    } 
} 

var person = Kernel._construct(Person, ["name", surname]); // EXCEPTION! 

Họ đang gặp lỗi:

TypeError: Class constructors cannot be invoked without 'new' 

Tôi cần có khả năng gọi hàm tạo với số lượng đối số không xác định. Bất kỳ ý tưởng về cách để có được xung quanh vấn đề này?

+0

Mục đích của '_bind' trong mã của bạn là gì? – Amit

+0

Tôi đã gỡ bỏ, nó đã có bởi vì tôi đã thử nghiệm bằng cách sử dụng _bind ... –

Trả lời

9

Có nhiều cách khác nhau mà bạn có thể làm điều đó:

  1. Sử dụng phương pháp Function đối tượng:

    Kernel.prototype._construct = function (constr, args) { 
        return new (Function.prototype.bind.apply(constr, [null].concat(args))); 
    }; 
    

    Dưới đây chúng tôi applyingargs như các đối số cho bind. Mục đích là để có một chức năng có thể được gọi là không có các đoạn để chúng ta có thể gọi new x(). bind thực hiện việc này cho chúng tôi, nhưng chúng tôi cần thiết lập chính xác. Cú pháp là:

    func.bind(thisArg[, arg1][, args2...]) 
    // calling the results from the above is like 
    // thisArg.func(arg1, arg2...); 
    

    Chúng tôi muốn sử dụng constr như chức năng để ràng buộc, và các mục trong args như các đối số. Chúng tôi không quan tâm đến số thisArg. Để làm điều đó, chúng ta cần "chuyển đổi" mảng args thành đối số. Cuộc gọi apply nào đó:

    func.apply(thisArg[, args]); 
    // calling the results from the above is like 
    // thisArg.func(args[0], args[1]...); 
    

    apply là thực sự gọi bind.Mục đầu tiên, [null], rất quan trọng vì chúng tôi muốn gọi bind nơi thisArgnull - như thế này: constr.bind(null, args[0], args[1]...).

  2. Sử dụng ES2015 Spread operator:

    Kernel.prototype._construct = function (constr, args) { 
        return new constr(...args); 
    }; 
    

    Đây là đơn giản hơn nhiều, nhưng có 2 vấn đề:

    1. Nó đòi hỏi hỗ trợ ES2015, và thậm chí cả Node mới nhất (v4.2.1) đòi hỏi một gắn cờ cho điều này (--harmony_spreadcalls).
    2. Điều này sẽ tạo ra lỗi cú pháp nếu không được hỗ trợ và bạn thậm chí không thể thực hiện điều đó, sau đó sử dụng kịch bản động (eval()/new Function(...)) - điều này không được thông báo.
  3. Sử dụng đối tượng được tích hợp sẵn trong đối tượng Reflect.

    Kernel.prototype._construct = function (constr, args) { 
        return Reflect.construct(constr, args); 
    }; 
    

    Đây cũng là đơn giản, nhưng thậm chí còn hơn nữa đằng sau về hỗ trợ (về cơ bản bạn phải sử dụng babel).

+0

Cảm ơn câu trả lời xuất sắc của bạn, tùy chọn giải quyết vấn đề của tôi :) –

+0

Cần lưu ý rằng 1 sẽ chỉ hoạt động cho các lớp gốc với bản địa Triển khai 'Function.prototype.bind'. Nếu vô tình thay thế bằng polyfill, TypeError vẫn sẽ được ném (các lớp transpiled là ok với bất kỳ việc thực thi nào). – estus

+0

Câu trả lời hay. Giải pháp 1 thực sự là kẻ giết người và chỉ là lựa chọn cho tôi – Karamell

2

Bạn có thể làm được việc này bằng cách liên kết các đối số cho hàm và sau đó gọi mới vào nó:

Kernel.prototype._construct = function (constr, args) { 
    var F = constr.bind(null, args); 
    return new F(); 
}; 
+0

Đó là một ý tưởng tốt, nhưng giải pháp của bạn là không chính xác. Xem câu trả lời của tôi. – Amit

2

Tốt câu hỏi, nhưng không, bạn không thể gọi constructor mà không có từ khóa new trong ES6 theo ECMAScript §9.22.

Tôi hỏi một câu hỏi giống hệt nhau ở đây: ES6: call class constructor without new keyword

I need to be able to invoke the constructor with an unknown number of arguments. Any ideas about how to get around this issue?

Vâng điều này không nói gì về (not) bằng cách sử dụng từ khóa new, nhưng bạn có thể dễ dàng sử dụng đối số variadic trong một constructor ES6

class Person { 
    constructor(name, surname, ...others) { 
    this.name = name; 
    this.surname = surname; 
    this.others = others || []; 
    } 
} 

let p = new Person('OweR', 'ReLoaDeD', 'a', 'b', 'c'); 
p; 
//=> {"name":"OweR","surname":"ReLoaDeD","others":["a","b","c"]} 

Tôi nghi ngờ đây không phải là những gì bạn đang sau. Nhưng, nếu bạn đang cố gắng tránh sử dụng new để gọi một lớp trong ES6, nó không thể được thực hiện.


Vì vậy, với điều đó đã nói, bạn đang thực sự cố gắng hoàn thành điều gì?

+0

Tôi đang háo hức hỗ trợ '--harmony' cho http://inversify.io/ –

Các vấn đề liên quan