2012-05-15 38 views
10

Tôi đang tìm kiếm các giải pháp để gọi các hàm tạo Javascript với số lượng đối số tùy ý và tìm thấy một số bài đăng SO tốt, khiến tôi tin rằng ba cuộc gọi này sẽ hoạt động tương tự. Tuy nhiên, ít nhất là trong tê giác và Node.js, họ không:ràng buộc/áp dụng các hàm tạo trong JavaScript

1. f = Date.bind(Date, 2000,0,1) 
2. g = Date.bind.call(Date, 2000, 0, 1) 
3. h = Date.bind.apply(Date, [2000, 0, 1]) 

Người đầu tiên có kết quả mong muốn:

print(new f()) //=> Sat Jan 01 2000 00:00:00 GMT-0500 (EST) 

Nhưng hai người kia không:

print(new g()) //=> Thu Feb 01 1900 00:00:00 GMT-0500 (EST) 
print(new h()) //=> Wed Jun 01 1904 00:00:00 GMT-0400 (EST) 

Vì vậy, một cái gì đó đã biến mất ở đâu đó. Suy nghĩ về cái gì? Chỉ là một ý tưởng tồi khi kết hợp những thứ như apply, bind và/hoặc call với new?

+0

Tất cả đều không thành công trong IE 8 vì không có 'Date.bind'. ;-) – RobG

+0

Dường như với tôi rằng đây là một cách sử dụng không phù hợp 'ràng buộc'. Nó nên được sử dụng để tạo các đối tượng funcion, nhưng ngày tháng là các đối tượng Date, không phải các hàm, và không thể được gọi. Ngoài ra, nếu không sử dụng liên kết hoặc gọi hoặc áp dụng, giá trị * this * này là * Date * anyway, vậy điểm là gì? Cuối cùng, bạn không gọi Date là một hàm tạo nhưng là một hàm. – RobG

+0

Chắc chắn, ngày tháng là các đối tượng ngày tháng, nhưng ngày chính nó là một hàm, và chính ngày tháng đó mà tôi đang cố ràng buộc. Date là một hàm, vì vậy tôi liên kết nó và nhận một hàm mới, sau đó tôi sử dụng như một hàm tạo bằng cách gọi nó bằng 'new'. Như tôi đã nói ở đầu câu hỏi, điểm thực tế là có thể gọi một hàm tạo với một danh sách các đối số được xác định trong thời gian chạy. –

Trả lời

23

previously accepted answer không chính xác. Bạn có thể sử dụng bind, call và apply với các constructors để tạo các constructor mới tốt - vấn đề duy nhất trong test của bạn là bạn đã quên rằng bind.apply và bind.call đang áp dụng và gọi bind, chứ không phải bản thân constructor , vì vậy bạn đã đưa ra các đối số sai.

f = Date.bind(null, 2000,0,1) 
g = Function.bind.call(Date, null, 2000, 0, 1) 
h = Function.bind.apply(Date, [ null, 2000, 0, 1 ]) 

new f() //=> Sat Jan 01 2000 00:00:00 GMT-0500 (EST) 
new g() //=> Sat Jan 01 2000 00:00:00 GMT-0500 (EST) 
new h() //=> Sat Jan 01 2000 00:00:00 GMT-0500 (EST) 

Tất cả ba là instanceof Ngày.

Đối số của cuộc gọi là bối cảnh thực thi theo sau là các đối số cần áp dụng. Các đối số của ứng dụng là ngữ cảnh thực thi và một mảng đối số. Đối số của Bind là ngữ cảnh thực hiện theo sau bởi các đối số để liên kết.

Vì vậy, các lập luận để áp dụng, ví dụ, là bối cảnh mà để áp dụngbind (ngày) tiếp theo là một mảng mà là các đối số cho bind (vì vậy các thành viên mảng đầu tiên là bối cảnh của ràng buộc tranh luận). Đây là lý do tại sao nó khó hiểu để gọi hoặc áp dụng ràng buộc; cảm thấy lạ khi cung cấp các đối số ngữ cảnh cho cả hai.

Lưu ý rằng, khi sử dụng liên kết với các nhà xây dựng, đối số ngữ cảnh luôn bị bỏ qua vì 'mới' tạo một ngữ cảnh mới một cách rõ ràng. Tôi sử dụng null khi đối số ngữ cảnh không liên quan để giữ cho rõ ràng, nhưng nó có thể là bất cứ điều gì.

Trong khi đó, áp dụng và gọi trong các ví dụ này cần biết rằng ngữ cảnh mà chúng áp dụng/ràng buộc cuộc gọi là hàm Date. Tôi đã chuyển 'Ngày' thành 'Chức năng', nơi có thể giúp chiếu sáng những gì đang thực sự cung cấp ngữ cảnh ở đâu. Khi chúng ta gọi áp dụng hoặc gọi Date.bind, chúng ta thực sự đang gọi áp dụng hoặc gọi phương thức bind không gắn liền với đối tượng Date. Phương pháp liên kết trong trường hợp này có thể đến từ bất kỳ hàm nào cả. Nó có thể là Number.bind.call (Date, null, 2000, 0, 1) và kết quả sẽ giống hệt nhau.

Nếu nó không rõ ràng lý do tại sao, hãy xem xét sự khác biệt giữa các ví dụ sau:

context.method(); 

var noLongerAMethod = context.method; 
noLongerAMethod(); 

Trong trường hợp thứ hai, phương pháp này đã được ly dị từ bối cảnh ban đầu của nó (.. bất kể nó đã bị ràng buộc trước đó) và sẽ hành xử khác đi nếu nó dựa vào 'điều này' trong nội bộ. Khi chúng ta kéo liên kết khỏi bất kỳ hàm đã cho nào là thuộc tính, thay vì thực thi trực tiếp, nó chỉ đơn giản là một con trỏ khác đến phương thức liên kết chung trên Function.prototype.

Cá nhân tôi không nghĩ mình đã từng cần gọi hoặc áp dụng liên kết và khó tưởng tượng một tình huống mà nó sẽ là giải pháp tốt, nhưng các nhà thầu liên kết để tạo các nhà xây dựng mới là điều tôi đã tìm thấy rất hữu ích vào dịp này. Trong mọi trường hợp, đó là một câu đố thú vị.

+0

Đây là một phản ứng rất hữu ích. Cảm ơn bạn! –

5

bindapply/call chỉ hoạt động gọi trình làm việc để chức năng nhưng không constructor, vì vậy về cơ bản với các phương pháp tự nhiên bạn không thể làm được điều này, một cách là viết một phương pháp bindConstruct nhưng nó có thể liên quan đến việc thêm phức tạp:

function bindConstruct(fn) { 
    // since constructor always accepts a static this value 
    // so bindConstruct cannot specify this 
    var extraArgs = [].slice.call(arguments, 1); 

    // create a 'subclass' of fn 
    function sub() { 
     var args = extraArgs.concat([].slice.call(arguments)); 
     fn.apply(this, args); 
    } 
    sub.prototype = fn.prototype; 
    sub.prototype.constructor = sub; 

    return sub; 
} 

Điều này, trên thực tế, hãy tạo một lớp con cho lớp con của bạn là.

Sau đó, mã của bạn:

var MyClass = function(x, y) { 
    console.log(arguments); 
    console.log(x + y); 
} 
var BindedMyClass = bindConstruct(MyClass, 1, 2, 3); 
var c = new BindedMyClass(4, 5); 
console.log(c instanceof MyClass); 
console.log(c instanceof BindedMyClass); 

Bạn cũng có thể viết hàm này để Function.prototype hoặc như là một phần mở rộng của bản địa bind chức năng.

+1

Cảm ơn, nhưng thật đáng buồn, điều này không hoạt động đúng cách. Các phương thức Date không thành công trên bất kỳ đối tượng nào mà tôi tạo ra với hàm "Date subclass" mới của tôi với 'TypeError: Method được gọi trên đối tượng không tương thích.' Như vậy chứng minh một lần nữa rằng Javascript không thực sự có các lớp con. Hoặc các lớp học ở tất cả, cho rằng vấn đề ... –

+0

"ràng buộc và áp dụng/gọi chỉ hoạt động công việc invocation để chức năng nhưng không phải nhà xây dựng" chỉ là không đúng sự thật; xem bên dưới. – Semicolon

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