2010-06-11 38 views
36

Như nhiều lần nói, nó được coi là xấu thực hành để sử dụng Function constructor (cũng thấy ECMAScript Language Specification, 5 thứ edition, § 15.3.2.1):sử dụng hợp pháp của các nhà xây dựng Chức năng

new Function ([arg1[, arg2[, … argN]],] functionBody) 

(nơi mà tất cả đối số là các chuỗi chứa các tên đối số và chuỗi cuối cùng (hoặc chỉ) chứa nội dung hàm).

Để tóm lại, nó được cho là chậm, như được giải thích bởi the Opera team:

Mỗi lần [...] các nhà xây dựng Function được gọi vào một chuỗi đại diện cho mã nguồn, động cơ kịch bản phải bắt đầu máy móc chuyển đổi mã nguồn thành mã có thể thực thi. Điều này thường là tốn kém cho hiệu suất - dễ dàng một trăm lần đắt hơn một hàm đơn giản gọi, ví dụ. (Mác ‘Tarquin’ Wilton-Jones)

Mặc dù nó không phải rằng xấu, theo this post trên MDC (Tôi đã không kiểm tra này bản thân mình bằng cách sử dụng phiên bản hiện tại của Firefox, mặc dù).

Crockford adds rằng

[t] ông trích dẫn ước của ngôn ngữ làm cho nó rất khó để diễn tả một cách chính xác một cơ quan chức năng như là một chuỗi . Ở dạng chuỗi, đầu tiên không thể thực hiện kiểm tra lỗi . […] Và nó lãng phí bộ nhớ vì mỗi hàm yêu cầu thực hiện độc lập của riêng mình.

khác biệt nữa là

một chức năng được xác định bởi một hàm constructor không thừa hưởng bất kỳ phạm vi khác ngoài phạm vi toàn cầu (mà tất cả chức năng kế thừa). (MDC)

Ngoài ra, bạn phải chú ý tránh việc tiêm mã độc khi bạn tạo new Function sử dụng nội dung động.

Điều đó nói rằng, T.J. Crowder nói trong an answer rằng

[t] ở đây hầu như không bao giờ bất cứ nhu cầu sự tương tự [...] Chức năng mới (...), một trong hai, một lần nữa, ngoại trừ đối với một số trường hợp cạnh tiên tiến.

Vì vậy, bây giờ tôi tự hỏi: "những trường hợp cạnh tiên tiến" là gì? Có sử dụng hợp pháp nào của hàm tạo hàm không?

+0

ES5 ở chế độ nghiêm ngặt không ném khi 'chức năng mới' được sử dụng ... –

+0

@ Šime: Theo thông số (Phụ lục C), cần:“ Cố gắng tự động xác định chức năng chế độ nghiêm ngặt như vậy bằng cách sử dụng Hàm constructor (15.3.2) sẽ ném một ngoại lệ 'SyntaxError', nhưng một thử nghiệm đã tiết lộ nó không. Tôi có hiểu nhầm điều gì đó không? –

+0

Vâng, hãy đọc câu trên. "Như một chức năng chế độ nghiêm ngặt" đề cập đến một chức năng mà một trong hai tên hoặc một trong các tham số là "eval" hoặc "đối số". –

Trả lời

8

jQuery sử dụng nó để phân tích chuỗi JSON khi đối tượng phân tích cú pháp JSON không khả dụng. Có vẻ hợp pháp đối với tôi :)

 // Try to use the native JSON parser first 
     return window.JSON && window.JSON.parse ? 
      window.JSON.parse(data) : 
      (new Function("return " + data))(); 
+1

Sử dụng thông dịch viên đầy đủ để phân tích cú pháp định dạng dữ liệu không phải là lý tưởng; nó mở ra cánh cửa để tiêm script. (* Nhiều người * làm điều đó, mặc dù, nó không chỉ là jQuery. :)) –

+1

Dữ liệu đã được khử trùng tại thời điểm này bởi jQuery, điều này được thực hiện hoàn toàn cho tốc độ. –

6

tôi sử dụng constructor new Function() như một thông dịch viên JS in-line tại một trong những ứng dụng web Tôi đang phát triển:

function interpret(s) { 
    //eval(s); <-- even worse practice 
    try { 
     var f = new Function(s); 
     f(); 
    } 
    catch (err) { 
     //graceful error handling in the case of malformed code 
    } 
} 

Như tôi đã có được công cụ trực tuyến trên AJAX (không iframe), tôi liên tục interpret() trên readyStateChange == 3. Điều này hoạt động khá tốt.

Chỉnh sửa: đây là một nghiên cứu điển hình rõ ràng cho thấy rằng new Function() nhanh hơn so với eval(). I E. bạn không nên (hiếm khi?) sử dụng eval thay cho new Function().

http://polyfx.com/stuff/bsort.html < - phiên bản 1000 lần lặp, có thể sụp đổ trình duyệt của bạn

http://polyfx.com/stuff/bsort10.html < - phiên bản ngắn hơn

Eval là trên trung bình, gần như 8 lần chậm hơn so với new Function().

+5

Chính xác là "thực hành tồi tệ hơn" về việc đánh giá chuỗi mã thông qua 'eval' so với việc thực hiện nó thông qua hàm tạo' Hàm'? – kangax

+2

Nó chạy nhanh hơn và dễ hiểu hơn: http://www.go4expert.com/forums/showthread.php?t=13979 Ngoài ra, nó dễ dàng hơn để thả 'f()' trong một phạm vi cụ thể hoặc đóng cửa nếu cần (vì lý do bảo mật). –

+2

Đã chỉnh sửa câu trả lời của tôi với một nghiên cứu điển hình tôi vừa viết. Đánh giá là trong một số trình duyệt chậm hơn gần 10 lần. –

2

Đây là trường hợp riêng biệt với câu trả lời khác của tôi.

Tôi đã sử dụng hàm tạo Hàm trong khi quay lại để tạo các trình định dạng chuỗi tùy chỉnh được gọi nhiều lần. Chi phí của việc tạo ra hàm (mà tôi lấy nó là vấn đề hiệu suất mà bạn đang nói đến) đã vượt xa hiệu suất của các hàm được xây dựng tùy chỉnh, được tạo ra trong thời gian chạy đặc biệt để xử lý một chuỗi định dạng cụ thể. không cần đánh giá tấn trường hợp không liên quan — hoặc phân tích chuỗi định dạng, cho vấn đề đó. Đó là một chút như biên dịch một biểu thức chính quy, tôi giả sử.

6

John Resig đã sử dụng hàm tạo Hàm để tạo phiên bản "được biên dịch" của các mẫu phía máy khách được viết bằng cú pháp asp. http://ejohn.org/blog/javascript-micro-templating/

+0

Thực hiện của John Resig lấy cảm hứng từ ngôn ngữ lập mô hình vi mô của gạch dưới, sử dụng cùng một phương pháp (tức là hàm tạo 'Hàm'): http://underscorejs.org/docs/underscore.html#section-143 – turtlemonvh

18

NWMatcher - Javascript CSS selector và khớp, bởi Diego Perini - sử dụng Function constructor (1, 2, 3, 4, vv) để tạo ra ("biên dịch") phiên bản hiệu suất cao của quẹt chọn.

Các benchmark (mà tôi chỉ chạy trên Chrome 5) nói cho chính nó:

alt text

Lưu ý sự khác biệt giữa NWMatcher và sizzle, mà là một công cụ chọn rất giống nhau, chỉ không có chức năng soạn thảo :)

Lưu ý phụ, ECMAScript 5 không phát hiện bất kỳ lỗi nào khi gọi Function. Không ở chế độ nghiêm ngặt, cũng như ở chế độ "chuẩn".chế độ nghiêm ngặt, tuy nhiên, giới thiệu vài hạn chế đối với sự hiện diện của định danh như "eval" và "lý luận":

  • Bạn không thể có khai báo các biến/chức năng/tranh luận với những cái tên như:

    function eval() { } 
    var eval = { }; 
    function f(eval) { } 
    var o = { set f(eval){ } }; 
    
  • Bạn không thể gán cho ID đó:

    eval = { }; 
    

Cũng lưu ý rằng trong chế độ nghiêm ngặt, eval ngữ nghĩa hơi khác so với ES3. Nghiêm ngặt chế độ đang không thể nhanh chóng biến hoặc các chức năng trong môi trường mà từ đó nó được gọi là:

eval(' "use strict"; var x = 1; '); 
typeof x; // "undefined" 
+0

Công cụ chọn của Ext.js (http://code.google.com/p/extjs-public/source/browse/trunk/release/source/core/DomQuery.js) đã sử dụng chức năng biên dịch cho các độ tuổi cũng (xem 'biên dịch') –

+2

Tôi đã đã cập nhật các tham chiếu mã nguồn. Nhưng điểm chuẩn đã trở thành một liên kết chết. – galambalazs

+0

[Trừ khi tôi bỏ lỡ điều gì đó] (http: // mathiasbynens.//notes/javascript-properties), chế độ nghiêm ngặt của ES5 cho phép cả 'var o = {eval: 1}' và 'myObject.eval = 1;'. Ví dụ khác của bạn kiểm tra, mặc dù. Sự thay đổi đặc điểm kể từ khi bạn viết câu trả lời này? –

2

Việc sử dụng hợp pháp duy nhất tôi đã đi cho nó là khi tôi viết này:

Function.prototype.New = (function() { 
    var fs = []; 
    return function() { 
    var f = fs [arguments.length]; 
    if (f) { 
     return f.apply (this, arguments); 
    } 
    var argStrs = []; 
    for (var i = 0; i < arguments.length; ++i) { 
     argStrs.push ("a[" + i + "]"); 
    } 
    f = new Function ("var a=arguments;return new this(" + argStrs.join() + ");"); 
    if (arguments.length < 100) { 
     fs [arguments.length] = f; 
    } 
    return f.apply (this, arguments); 
    }; 
})(); 

Các mã cho phép bạn sử dụng Function.prototype.apply trong khi 'sử dụng' từ khóa new.

Ví dụ:

function Foo (x, y, z) { 
    this.x = x; 
    this.y = y; 
    this.z = z; 
    this.otherArgs = Array.prototype.slice.call (arguments, 3); 
} 

var foo = Function.prototype.New.apply (Foo, [1, 2, 3, 4, 5, 6, 7]); 
// /*equiv*/ var foo = Foo.New.apply (Foo, [1, 2, 3, 4, 5, 6, 7]); 
// /*equiv*/ var foo = Foo.New (1, 2, 3, 4, 5, 6, 7); 
var bool = true 
    && foo.x == 1 
    && foo.y == 2 
    && foo.z == 3 
    && foo.otherArgs.length == 4 
    && foo.otherArgs [0] == 4 
    && foo.otherArgs [1] == 5 
    && foo.otherArgs [2] == 6 
    && foo.otherArgs [3] == 7 
    ; 

alert (bool); 
+0

Trường hợp sử dụng này có thể được thực hiện bằng cách sử dụng 'Object.create' trong các trình duyệt tuân thủ ES5:' var newObj = Object.create (MyClass.prototype); var ret = MyClass.apply (newObj, constructorArgs); if (ret && typeof ret === "object") {newObj = ret; } ' – kpozin

1

Bạn có thể muốn thực hiện một chuỗi mã nhiều hơn một lần. Sử dụng hàm tạo hàm có nghĩa là bạn chỉ phải biên dịch nó một lần.

Bạn có thể muốn chuyển đối số cho mã, ví dụ: nếu bạn đang điền vào một sự kiện, bạn có thể truy xuất thuộc tính sự kiện và xây dựng một hàm mong đợi đối số sự kiện.

Bạn có thể kết hợp hai và biên dịch nó ở một vị trí và thực hiện nó ở vị trí khác và vẫn quản lý để chuyển đối số trong các biến mà chuỗi mã mong đợi.

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