2009-04-21 32 views
36

Trong JavaScript, nếu tôi có một chuỗi trong một biến, có cách nào để lấy một tham chiếu đến đối tượng hàm có tên trùng khớp đó không? Lưu ý rằng jQuery có sẵn cho tôi để tôi có thể sử dụng bất kỳ phương thức trợ giúp nào của nó.Nhận đối tượng hàm JavaScript từ tên của nó dưới dạng chuỗi?

Ví dụ:

myFunction = function(){}; 

var func_name = "myFunction"; 
var myFunctionPtr = ??? //how to get the function from the name 

Cảm ơn

Trả lời

58

nếu bạn biết rằng một chức năng toàn cầu của mình, bạn có thể sử dụng:

var functPtr = window[func_name]; 
//functPtr() 

Nếu không thay cửa sổ với đối tượng phụ huynh có chứa các chức năng .

+5

Để vượt qua đối số: cửa sổ [FUNC_NAME] (myArg1, myArg2); – mpemburn

9

[func_name] này sẽ cung cấp cho bạn chức năng.

var myfunc = this[func_name]; 
myfunc(); 
+0

Tôi chỉ thử rằng trong Firebug và nó không hoạt động. Có lẽ nó hoạt động khác nhau trong một trang web? – rmeador

+0

Tùy thuộc vào vị trí của hàm được xác định. Nếu hàm không được định nghĩa trên 'this' thì nó sẽ không tìm thấy nó. Bạn có thể cần phải thử cửa sổ [func_name] hoặc Some.Other.Object [func_name] –

4

Sử dụng eval:

myFunction = function(){}; 

var func_name = "myFunction"; 
var myFunctionPtr = eval(func_name); 
+2

eval là điều ác. không sử dụng eval – mkoryak

+3

tại sao? bạn có thể trích dẫn một số lý do tốt. –

+0

http://stackoverflow.com/questions/86513/why-is-using-javascript-eval-function-a-bad-idea – Miles

21

tôi chỉ làm một thử nghiệm nhanh trong Firebug và tôi đã có thể để có được những chức năng từ tên bằng cách đơn giản eval() ing tên ... Tôi cảm thấy bẩn sử dụng eval() , nhưng có vẻ như để hoàn thành công việc ở đây khá độc đáo.

var myFunctionPtr = eval(func_name); 
+0

Bạn có thể chuyển các tham số theo cách đó không? –

+0

Chắc chắn - một khi bạn có myFunctionPtr, bạn sử dụng nó như một hàm bình thường. –

+0

hoặc bạn có thể sử dụng cửa sổ [func_name] để thực hiện công việc. – mkoryak

3

Một cách an toàn để làm là sandbox chức năng bị cáo buộc trong khi thử nghiệm loại của nó:

function isFunction(expr) { 
    function sandboxTemplate() { 
     var window, document, alert; // etc. 

     try { 
      return typeof $expr$ == "function"; 
     } catch (e) { 
      return false; 
     } 
    } 

    try { 
     var sandbox = new Function(
      sandboxTemplate.toString().replace("$expr$", expr) 
      + "return sandboxTemplate()"); 
     return sandbox(); 
    } catch (e) { 
     return false; 
    } 
} 

function test(expr) { 
    document.write("<div>\"" + expr + "\" <b>is " 
     + (isFunction(expr) ? "" : "not ") 
     + "</b>a function</div>"); 
} 

/* Let's do some testing */ 

function realFunction() { 
} 

test("realFunction");  // exists! 
test("notHere");   // non-existent 
test("alert('Malicious')"); // attempt to execute malicious code! 
test("syntax error {");  // attempt to blow us up! 

Sản lượng:

  • "realFunction" một hàm
  • "notHere" không phải là một hàm
  • "alert ('độc hại')" không phải là một hàm
  • "lỗi cú pháp {" không phải là một hàm

Mã sandboxing có thể được viết một cách ngắn gọn hơn nhưng tôi thích sử dụng các hàm "mẫu" thay vì nhúng mã JS dưới dạng các chuỗi ký tự chuỗi.

Và ồ, điều này thật tuyệt vời khi không sử dụng eval - mặc dù người ta có thể lập luận rằng việc sử dụng hàm tạo hàm không khác với eval.

14

Đây không phải là cách tiếp cận ưa thích. Thay vì giữ tên hàm trong func_name, bạn có thể giữ tham chiếu đến một hàm giống như thành công trong một cái gì đó như func_to_call.

Nếu bạn hoàn toàn yêu cầu để giữ cho các tài liệu tham khảo chức năng như là một chuỗi, bạn sẽ thường sử dụng một bảng băm để ánh xạ tên tùy ý cho một biến (JS có hạng nhất các chức năng mà làm cho nó có thể)

myFunction = function(){}; 
var obj = {func_name: myFunction}; 

obj['func_name']();//executes the function 

Fiddled (tôi không biết tại sao, đó là một kịch bản rất nhỏ như vậy :)

nó được gợi ý rằng bạn sử dụng eval(func_name) - tuy nhiên điều này có thể nhanh chóng thoát ra khỏi tầm kiểm soát vì JS Phạm vi.

Bạn cũng đã khai báo myFunction = function(){}; làm biến toàn cục. Một mặt, nó cho phép bạn tham khảo nó như là window[func_name] nhưng mặt khác nó gây ô nhiễm phạm vi toàn cầu.

+1

+1 Giải pháp này hoạt động và đáng ngạc nhiên chưa bao giờ được bình chọn. Tôi tìm thấy nó một trong những thanh lịch nhất cho trường hợp sử dụng của tôi. – bernardn

+0

thậm chí tốt hơn cho những thứ đơn giản bạn chỉ có thể vượt qua một đại biểu xung quanh –

+0

Lập tức một điều khiển từ MVC Razor, vì vậy tên hàm gọi lại phải đến như là một chuỗi C#. Do đó, eval là lựa chọn đơn giản nhất. – Triynko

0

tìm thấy chức năng và sau đó gọi cho họ

autoCallBack : function(_action){ 
      $(".module").each(function(){ 
       var modulName = $(this).attr("id"); 
       if(isFunction(modulName)){ 
        eval(modulName)(); 
       } 
      }); 
     } 

isFunction : function(_functionName){ 
     try { 
      eval(_functionName); 
     } catch (error) { 
      return false; 
     } 
    return true; 
} 
5

Nó phụ thuộc vào nơicách chức năng là (hoặc không) tuyên bố.

Nếu đó là một toàn cầu và không khai báo qua let name = ... hoặc const name = ... cú pháp (và nó không phải là một constructor lớp khai báo với class), bạn có thể kiểm tra bằng cách tìm kiếm nó như là một tài sản trên các đối tượng toàn cầu. (Những cảnh báo đó là tất cả những thứ ES2015; dưới đây.) Bạn có thể lấy một tham chiếu đến đối tượng toàn cầu qua this ở chế độ lỏng lẻo ở phạm vi toàn cầu; các trình duyệt cũng cung cấp cho bạn toàn cầu được gọi là window. Vì vậy, giả sử một trình duyệt:

if (typeof window[func_name] === "function") { 
    // .... 
} 

Nếu nó có thể không là một toàn cầu, nhưng thay vì chỉ là trong phạm vi bởi vì mã của bạn đóng trên nó, hoặc nếu nó được tạo bằng một trong những cơ chế ES2015 tôi đã đề cập, có thực sự không có cách nào tốt để kiểm tra khác hơn eval:

if (eval("typeof " + func_name) === "function") { 
    // .... 
} 

sử dụng eval là một phương sách cuối cùng, và bạn phải chỉ sử dụng nó với đầu vào nghiêm ngặt kiểm soát. Nhưng khi bạn phải làm thế, và bạn có đầu vào được kiểm soát chặt chẽ, điều đó là tốt.


Về hãy cẩn thận ES2015:

mới let, const, và class là beasties rất thú vị: Khi sử dụng ở phạm vi toàn cầu, họ tạo globals, nhưng họ không tạo thuộc tính trên đối tượng toàn cầu. Kể từ ES2015, mặc dù tất cả các thuộc tính của đối tượng chung là các hình cầu, nhưng không phải tất cả các thuộc tính đều là các thuộc tính của đối tượng chung. Đó là một phần của việc cố gắng kiềm chế trong không gian tên toàn cầu bị ô nhiễm và cũng mang lại sự bảo mật cao hơn cho mô hình ràng buộc JavaScript. (Bây giờ chúng ta có các mô-đun thực sự.)

Vì vậy, (lưu ý rằng điều này sẽ chỉ chạy trong các trình duyệt tiên tiến):

// Global scope, in a browser (because I used `window` and `document.body`) that 
 
// implements this aspect of ES2015 (as I write this, Firefox's SpiderMonkey 
 
// doesn't, Chrome's V8 does on the latest Chrome; expect SpiderMonkey and IE 
 
// to catch up pretty quick (didn't test IE Edge, maybe it's already there) 
 

 
// Strict mode isn't required for this behavior, but for the moment V8 only 
 
// supports the block-scoped constructs in strict mode. 
 
"use strict"; 
 
let tbody = setup(); 
 

 
// Old-fashioned var: Creates a property on the global object, so 
 
// we get "function, function" 
 
var f1 = function() { /*...*/ }; 
 
result("var declaration", typeof f1, typeof window["f1"]); 
 

 
// Function declaration: Creates a property on the global object, so 
 
// "function, function" 
 
function f2() {} 
 
result("function declaration", typeof f2, typeof window["f2"]); 
 

 
// `let` declaration: Doesn't create property on global object, so 
 
// "function, undefined" 
 
let f3 = function() { /*...*/ }; 
 
result("let declaration", typeof f3, typeof window["f3"]); 
 

 
// `const` declaration: Doesn't create property on global object, so 
 
// "function, undefined" 
 
const f4 = function() { /*...*/ }; 
 
result("const declaration", typeof f4, typeof window["f4"]); 
 

 
// `class` declaration: Doesn't create property on global object, so 
 
// "function, undefined" 
 
class C1 {} 
 
result("class declaration", typeof C1, typeof window["C1"]); 
 

 
function setup() { 
 
    document.body.insertAdjacentHTML(
 
    "beforeend", 
 
    "<table>" + 
 
    "<thead>" + 
 
    "<tr><th>test</th><th>global</th><th>prop</th></tr>" + 
 
    "</thead>" + 
 
    "<tbody></tbody>" + 
 
    "</table>" 
 
); 
 
    return document.body.querySelector("tbody"); 
 
} 
 

 
function result(label, direct, win) { 
 
    tbody.insertAdjacentHTML(
 
    "beforeend", 
 
    "<tr><td>" + [label, direct, win].join("</td><td>") + "</td></tr>" 
 
); 
 
}
body { 
 
    font-family: sans-serif; 
 
} 
 
table { 
 
    border-collapse: collapse; 
 
} 
 
th, td { 
 
    border: 1px solid #ddd; 
 
    padding: 4px 8px; 
 
}

Output trên các trình duyệt tiên tiến:

 
+----------------------+------------+-----------+ 
|   test   | global | prop | 
+----------------------+------------+-----------+ 
| var declaration  | function | function | 
| function declaration | function | function | 
| let declaration  | function | undefined | 
| const declaration | function | undefined | 
| class declaration | function | undefined | 
+----------------------+------------+-----------+ 

Lưu ý: Một số transpilers không thực thi điều này một cách nghiêm ngặt, vì vậy nếu bạn thấy các kết quả khác nhau trong mã transpiled, đừng ngạc nhiên.

0

Đối NodeJs

Viết chức năng của bạn trong một file riêng biệt và xuất khẩu chúng và sử dụng với tên tài liệu tham khảo về điều đó để gọi cho họ, giống như

// functions.js 
var funcOne = function(){ 
        console.log('function ONE called') 
       } 
module.exports={ 
    // name_exported : internal_name 
    funcOne : funcOne 
} 

Sử dụng chức năng quy định tại functions.js trong index.js:

// index.js 
var methods = require('./functions.js') // path to functions.js 
methods['funcOne']() 

OUTPUT:

> node index.js 
> function ONE called 
Các vấn đề liên quan