2010-09-26 67 views
8

hôm nay I've read we have a way of declaring the function by Function constructor. Nhưng tôi chưa bao giờ thấy triển khai thực tế sử dụng hàm tạo Function trong thực tế. Vì vậy, tôi muốn hỏi, có bất kỳ trường hợp nào mà chúng ta có thể nhận được lợi ích bằng cách sử dụng phương thức khởi tạo Function thay vì sử dụng khai báo function() không? Và sự khác biệt ẩn từ là gì (nếu có)Hàm tạo hàm so với câu lệnh hàm

Chức năng Constructor

var func = new Function("x", "y", "return x*y;"); // pass the context by String 

function():?

var func = function(x, y){ return x*y; } 

Cảm ơn

+2

Xem: [Sử dụng hợp pháp của hàm tạo hàm] (http://stackoverflow.com/questions/3026089/legitimate-uses-of-the-function-constructor) – CMS

+1

Video tốt về hàm javascript http: // developer. yahoo.com/yui/theater/video.php?v=crockonjs-3 – Thomas

+0

@CMS @Thomas, Cảm ơn bạn đã liên kết. – c4il

Trả lời

10

Hàm tạo hàm là dạng eval, thường nên tránh (nó chậm và được coi là không an toàn). Thực sự không có lợi ích khi sử dụng hàm dựng Hàm trên câu lệnh hàm dựng sẵn trừ khi bạn muốn xây dựng một hàm từ các thành phần động, điều này khá hiếm. Có sử dụng hợp pháp cho hình thức này, tuy nhiên hầu hết thời gian nó được sử dụng không cần thiết đó là lý do tại sao nó nhìn xuống và nói chung là tránh.

Ngoài ra, các hàm được tạo bằng hàm tạo hàm sẽ không giữ tham chiếu đến môi trường mà chúng đã được định nghĩa trong (đóng). Khi được thực hiện, chúng sẽ kéo các biến đó trực tiếp từ phạm vi toàn cục.

var f, a; 
(function() { 
    var a = 123; 
    f = new Function("return a"); 
})(); 

f(); //undefined 

a = "global" 
f(); // "global" 

Trong khi chức năng thường xuyên làm giữ một tham chiếu đến môi trường mà họ đã định nghĩa:

var f; 
(function() { 
    var a = 123; 
    f = function() { return a; } 
})(); 
f(); //123 
+0

Tôi tự hỏi họ đang nghĩ gì khi xây dựng đối tượng 'Chức năng' này. – Tarik

+0

Vâng nó khá hữu ích cho một cái gì đó giống như js1k cạnh tranh :-) – Pointy

+3

@ Daniel, Lưu ý rằng phần "chậm" sẽ chỉ là chức năng * biên dịch *, sau khi một đối tượng chức năng được xây dựng, nó sẽ hành xử chính xác giống như nếu nó đã được tạo bằng 'FunctionDeclaration' hoặc' FunctionExpression'. – CMS

2

Vâng, sự khác biệt rõ ràng khi làm việc với các chuỗi là bạn có tùy chọn lập trình meta, b y xây dựng chuỗi tại thời gian chạy (giống như bạn có thể với eval). Tuy nhiên, điều này là gấp đôi lưỡi, và cho là dẫn đến một loạt các vấn đề khác với nối chữ (tiêm), và có lẽ chỉ đơn giản là phức tạp. Nếu bạn không cần phiên bản chuỗi, tôi sẽ không sử dụng nó thành thật.

Một lợi ích phụ của phiên bản thông thường (không phải chuỗi) là hầu hết các người khai thác javascript (hoặc kẻ làm xáo trộn) sẽ biết phải làm gì với nó. Điều này có vẻ không chắc đối với chuỗi, tức là họ sẽ để nó "như là" (không được rút gọn hoặc làm xáo trộn).

+0

Bạn có thể đưa ra một ví dụ cho các vấn đề lập trình meta không? – Tarik

+1

@Braveyard - done * right * meta lập trình là tốt; nhưng nó cũng là một cách tuyệt vời để mở lỗ xss nếu làm sai. Ví dụ: nếu người dùng có thể lạm dụng chuỗi để người dùng khác đăng cookie của họ cho bạn. –

2

Nếu bạn đang viết trình phân tích cú pháp Javascript và đang diễn giải chuỗi dưới dạng hàm thì bạn có thể sử dụng hàm tạo hàm. EG nếu bạn đang đưa ra:

"function(x){return x+2}" 

Và bạn có một số loại phân tích cú pháp từ vựng và nó thấy rằng chuỗi con thực sự là một chức năng, để dịch nó thành một chức năng thực bạn sẽ sử dụng new Function và thêm nó vào cây của bạn.

Nếu không, thực sự không có nhiều công dụng mà tôi có thể nghĩ đến.

0

Ghi chú thêm để gửi bài Cristian Sanchez'.

Bạn không bao giờ có thể truy cập vars phạm vi địa phương trong 'Đánh giá chức năng'.

Cho phép xem sự khác biệt giữa Hàm và eval.

Chức năng-Ví dụ:

var f, a = 1; 
(function() { 
    var a = 123; 
    f = new Function("return a"); 
})(); 
console.log(f()) // 1 

Chức năng-Constructor không biết gì về phạm vi địa phương, bởi vì nó tạo ra một Phạm vi cô lập mới bên dưới cửa sổ/toàn cầu-Object - không phải ở vị trí mà nó là - đó là sự khác biệt chính đối với eval.

Eval-Ví dụ:

var f, a = 1; 
(function() { 
    var a = 123; 
    eval("f = function() { return a }"); 
})(); 
console.log(f()) // 123 

'eval' có quyền truy cập vào các vars địa phương. Ngay cả khi bạn phạm vi toàn bộ điều.

var f, a = 1; 
(function() { 
    var a = 123; 
    eval("f = (function() { function f() { return a }; return f; })();"); 
})(); 
console.log(f()) // still 123 

Trong ví dụ sau f() throws một lỗi - "a" được định nghĩa trong phạm vi riêng (và thế giới) của mình. Function-Constructor có phạm vi đặc biệt riêng của nó mà không biết bất cứ điều gì bên ngoài - ngoại trừ phạm vi cha mẹ của bạn - cửa sổ/đối tượng toàn cầu.

delete a; 
var f; 
(function() 
{ 
    var a = 1; 
    f = new Function("return a"); 
})(); 

console.log(f()); // Throws error (a is not defined) 

Nếu bạn muốn sử dụng các vars cục bộ, hãy chuyển nó làm tham số cho hàm-Constructor bên trong phạm vi bên trong!

var f, a = 1; 
(function() { 
    var a = 123; 
    f = new Function("a", "return a"); 

    console.log(f(a)); // passing inner a: result = 123 
})(); 

console.log(f(a)); // passing outer a: result = 1 

hoặc

var result, a = 1; 
(function() { 
    var a = 123; 
    var f = new Function("a", "return a"); 
    result = f(a); // store result in global var 
})(); 

console.log(result); // 123 

Bạn có thể thực hiện chức năng trực tiếp với cuộc gọi/áp dụng, quá (không "mới" cần thiết).

Function('a','b','c', 'console.log(c,b,a)').call(this, 1, 2, 3); // output: 3 2 1 
Function('console.log(arguments)').apply(this, [1, 2, 3]); // access through arguments[0-3] 

Bằng cách này, nó luôn luôn khuyên, thiết lập chế độ nghiêm ngặt, để cho phép hành vi ES5 chính xác.

Với chế độ nghiêm ngặt 'này' là (chính xác) không được xác định theo mặc định - cho đến khi bạn ràng buộc một đối tượng.

Function('console.log(this)')(); // this is 'window' in sloppy mode 
Function('"use strict"; console.log(this)')(); // this is undefined - correct behavior 

Nếu bạn muốn, bạn có thể liên kết 'này' một cách rõ ràng

// bind 'this' to 'window' again 
Function('"use strict";console.log(this)').bind(window)(); 
// bind 'this' to an Object without __proto__ 
Function('"use strict";console.log(this)').bind(Object.create(null))(); 

Tất nhiên bạn có thể gắn bất kỳ chức năng/lớp đối tượng để mở rộng đối tượng.

function SomeClass() 
{ 
    this.a = 1; 
} 

var cls = new SomeClass(); 

new Function('"use strict"; this.a = 5; this.b = 6;').bind(cls)(); 

console.log(cls.a); // a = 5 - override property 
console.log(cls.b); // b = 6 - appended property 

Cùng một kết quả với cuộc gọi trực tiếp, mà không cần khóa 'mới'.

function SomeClass() 
{ 
    this.a = 1; 
} 

var cls = new SomeClass(); 

Function('"use strict"; this.a = 5; this.b = 6; this.foo = function(){console.log("bar")}').call(cls); 

console.log(cls.a); // a = 5 - override property 
console.log(cls.b); // b = 6 - appended property 
console.log(cls.foo()); // bar - appended function 

Mọi chức năng/lớp được tạo thông qua hàm tạo hàm 'Hàm'.

Vì vậy, bạn không phải viết "Chức năng" trong mã của mình. Bạn cũng có thể sử dụng đối tượng hàm tạo.

function SomeClass() 
{ 
    this.a = 1; 
} 

var cls = new SomeClass(); 

SomeClass.constructor("this.a = 2;").call(cls); 

cls; // SomeClass {a: 2}, because "SomeClass.constructor" === Function 

Việc sử dụng 'Chức năng' là tốt ví dụ cho các trò chơi, nếu bạn có một XMLHttpRequest-Preloader, mà tải một rất lớn Javascript Tệp (5-10 MB kèm script), trong khi bạn muốn hiển thị một tải-bar hoặc một cái gì đó khác cho người dùng, thay vì tải toàn bộ điều với một script-tag, trong khi người dùng đang chờ tải trang mà không có bất kỳ phản ứng trực quan nào.

Việc bạn tải tập lệnh lớn thông qua thẻ tập lệnh không quan trọng hay thông qua chức năng một lần khi khởi động. Động cơ phải nạp mã đơn giản và đánh giá nó theo một trong hai cách. Không có khía cạnh bảo mật, nếu mã (!) Của bạn đến từ một nguồn đáng tin cậy (tên miền của bạn) và bạn biết những gì bên trong.

'Chức năng' và 'eval' chỉ xấu với mã không đáng tin cậy (khi người dùng khác có ảnh hưởng đến nó) hoặc trong vòng lặp (biên dịch chậm hiệu suất), nhưng hoàn toàn không thể tải & đánh giá tập lệnh của riêng bạn khi khởi động , nếu mã giống như trong các tệp Javascript bên ngoài.

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