Function() constructor
Baseline
Widely available
This feature is well established and works across many devices and browser versions. It’s been available across browsers since July 2015.
Warning: Các tham số được truyền vào constructor này được phân tích cú pháp và thực thi động như JavaScript. Các API như thế này được gọi là injection sinks, và là nguy cơ tiềm ẩn cho các cuộc tấn công cross-site-scripting (XSS).
Bạn có thể giảm thiểu rủi ro này bằng cách luôn truyền các đối tượng TrustedScript thay vì chuỗi và áp dụng trusted types.
Xem Security considerations để biết thêm thông tin.
Constructor Function() tạo các đối tượng Function. Gọi constructor trực tiếp có thể tạo hàm động, nhưng gặp phải các vấn đề bảo mật và hiệu năng tương tự (nhưng kém quan trọng hơn nhiều) như eval(). Tuy nhiên, không giống eval (có thể truy cập phạm vi cục bộ), constructor Function tạo ra các hàm chỉ thực thi trong phạm vi toàn cục.
Try it
const sum = new Function("a", "b", "return a + b");
console.log(sum(2, 6));
// Expected output: 8
Syntax
new Function(functionBody)
new Function(arg1, functionBody)
new Function(arg1, arg2, functionBody)
new Function(arg1, arg2, /* …, */ argN, functionBody)
Function(functionBody)
Function(arg1, functionBody)
Function(arg1, arg2, functionBody)
Function(arg1, arg2, /* …, */ argN, functionBody)
Note:
Function() có thể được gọi có hoặc không có new. Cả hai đều tạo ra một instance Function mới.
Parameters
arg1, …,argNOptional-
Các instance
TrustedScripthoặc chuỗi xác định các tên được hàm sử dụng làm tên tham số hình thức. Giá trị phải tương ứng với một tham số JavaScript hợp lệ (bất kỳ trong số identifier thông thường, rest parameter, hoặc tham số destructured, tùy chọn có default), hoặc một danh sách các chuỗi như vậy được phân tách bằng dấu phẩy.Vì các tham số được phân tích theo cùng cách như các biểu thức hàm, khoảng trắng và chú thích được chấp nhận. Ví dụ:
"x", "theValue = 42", "[a, b] /* numbers */"— hoặc"x, theValue = 42, [a, b] /* numbers */". ("x, theValue = 42", "[a, b]"cũng hợp lệ, dù rất khó đọc.) functionBody-
Một
TrustedScripthoặc chuỗi chứa các câu lệnh JavaScript tạo nên định nghĩa hàm.
Exceptions
SyntaxError-
Tham số hàm không thể được phân tích cú pháp như một danh sách tham số hợp lệ, hoặc
functionBodykhông thể được phân tích cú pháp như các câu lệnh JavaScript hợp lệ. TypeError-
Bất kỳ tham số nào là chuỗi khi Trusted Types được áp dụng bởi CSP và không có chính sách mặc định nào được định nghĩa.
Description
Các đối tượng Function được tạo bằng constructor Function được phân tích cú pháp khi hàm được tạo. Điều này kém hiệu quả hơn so với việc tạo hàm bằng function expression hoặc function declaration và gọi nó trong code của bạn, vì các hàm như vậy được phân tích cú pháp cùng với phần còn lại của code.
Tất cả các tham số được truyền vào hàm, ngoại trừ tham số cuối cùng, được xem là tên của các identifier tham số trong hàm được tạo, theo thứ tự chúng được truyền. Hàm sẽ được biên dịch động như một biểu thức hàm, với source được ghép theo cách sau:
`function anonymous(${args.join(",")}
) {
${functionBody}
}`;
Điều này có thể quan sát được bằng cách gọi phương thức toString() của hàm.
Tuy nhiên, không giống như các function expressions thông thường, tên anonymous không được thêm vào phạm vi của functionBody, vì functionBody chỉ có quyền truy cập vào phạm vi toàn cục. Nếu functionBody không ở chế độ strict (bản thân body cần có chỉ thị "use strict" vì nó không kế thừa chế độ strict từ ngữ cảnh), bạn có thể dùng arguments.callee để tham chiếu đến chính hàm đó. Ngoài ra, bạn có thể định nghĩa phần đệ quy như một hàm bên trong:
const recursiveFn = new Function(
"count",
`
(function recursiveFn(count) {
if (count < 0) {
return;
}
console.log(count);
recursiveFn(count - 1);
})(count);
`,
);
Lưu ý rằng hai phần động của source được ghép — danh sách tham số args.join(",") và functionBody — sẽ được phân tích cú pháp riêng biệt trước tiên để đảm bảo chúng đều hợp lệ về mặt cú pháp. Điều này ngăn các nỗ lực injection tương tự.
new Function("/*", "*/) {");
// SyntaxError: Unexpected end of arg string
// Doesn't become "function anonymous(/*) {*/) {}"
Security considerations
Phương thức này có thể được dùng để thực thi đầu vào tùy ý được truyền vào bất kỳ tham số nào. Nếu đầu vào là một chuỗi không an toàn tiềm ẩn do người dùng cung cấp, đây là một nguy cơ tiềm ẩn cho các cuộc tấn công Cross-site-scripting (XSS). Ví dụ, đoạn code sau giả định untrustedCode được cung cấp bởi người dùng:
const untrustedCode = "alert('Potentially evil code!');";
const adder = new Function("a", "b", untrustedCode);
Các trang web có Content Security Policy (CSP) chỉ định script-src hoặc default-src sẽ ngăn code như vậy chạy theo mặc định. Nếu bạn phải cho phép các script chạy qua Function(), bạn có thể giảm thiểu các vấn đề này bằng cách luôn gán các đối tượng TrustedScript thay vì chuỗi, và áp dụng trusted types bằng chỉ thị CSP require-trusted-types-for. Điều này đảm bảo đầu vào được chuyển qua một hàm chuyển đổi.
Để cho phép Function() chạy, bạn cần chỉ định thêm từ khóa trusted-types-eval trong chỉ thị script-src của CSP. Từ khóa unsafe-eval cũng cho phép Function(), nhưng kém an toàn hơn nhiều so với trusted-types-eval vì nó cho phép thực thi ngay cả trên các trình duyệt không hỗ trợ trusted types.
Ví dụ, CSP cần thiết cho site của bạn có thể trông như thế này:
Content-Security-Policy: require-trusted-types-for 'script'; script-src '<your_allowlist>' 'trusted-types-eval'
Hành vi của hàm chuyển đổi phụ thuộc vào trường hợp sử dụng cụ thể yêu cầu script do người dùng cung cấp. Nếu có thể, bạn nên giới hạn các script được phép chính xác là code mà bạn tin tưởng để chạy. Nếu không thể, bạn có thể cho phép hoặc chặn việc sử dụng một số hàm nhất định trong chuỗi được cung cấp.
Examples
Lưu ý rằng các ví dụ này bỏ qua việc sử dụng trusted types để ngắn gọn. Để xem code theo cách tiếp cận được khuyến nghị, hãy xem Using TrustedScript trong eval().
Chỉ định tham số với constructor Function
Đoạn code sau tạo một đối tượng Function nhận hai tham số.
// Example can be run directly in your JavaScript console
// Create a function that takes two arguments, and returns the sum of those arguments
const adder = new Function("a", "b", "return a + b");
// Call the function
adder(2, 6);
// 8
Các tham số a và b là các tên tham số hình thức được sử dụng trong thân hàm, return a + b.
Tạo đối tượng hàm từ function declaration hoặc function expression
// The function constructor can take in multiple statements separated by a semicolon. Function expressions require a return statement with the function's name
// Observe that new Function is called. This is so we can call the function we created directly afterwards
const sumOfArray = new Function(
"const sumArray = (arr) => arr.reduce((previousValue, currentValue) => previousValue + currentValue); return sumArray",
)();
// call the function
sumOfArray([1, 2, 3, 4]);
// 10
// If you don't call new Function at the point of creation, you can use the Function.call() method to call it
const findLargestNumber = new Function(
"function findLargestNumber (arr) { return Math.max(...arr) }; return findLargestNumber",
);
// call the function
findLargestNumber.call({}).call({}, [2, 4, 1, 8, 5]);
// 8
// Function declarations do not require a return statement
const sayHello = new Function(
"return function (name) { return `Hello, ${name}` }",
)();
// call the function
sayHello("world");
// Hello, world
Specifications
| Specification |
|---|
| ECMAScript® 2027 Language Specification> # sec-function-constructor> |