Cập nhật Chrome 59: Như tôi đã dự đoán trong câu trả lời bên dưới, ràng buộc không còn chậm hơn với trình biên dịch tối ưu hóa mới. Đây là mã với chi tiết: https://codereview.chromium.org/2916063002/
Hầu hết thời gian nó không quan trọng.
Trừ khi bạn đang tạo một ứng dụng, đâu .bind
là nút thắt cổ chai, tôi sẽ không bận tâm. Khả năng đọc quan trọng hơn nhiều so với hiệu suất tuyệt đối trong hầu hết các trường hợp. Tôi nghĩ rằng sử dụng bản địa .bind
thường cung cấp mã dễ đọc và dễ bảo trì hơn - đó là một điểm cộng lớn.
Tuy nhiên, có, khi nó quan trọng - .bind
chậm hơn
Có, .bind
chậm hơn đáng kể so với quá trình đóng - ít nhất là trong Chrome, ít nhất là theo cách hiện tại mà nó được triển khai v8
. Cá nhân tôi đã phải chuyển đổi trong Node.JS vì các vấn đề về hiệu suất một số lần (nói chung, việc đóng cửa hơi chậm trong các tình huống chuyên sâu về hiệu suất).
Tại sao? Bởi vì .bind
thuật toán phức tạp hơn rất nhiều so với việc bao hàm một hàm với một hàm khác và sử dụng .call
hoặc .apply
. (Thật thú vị, nó cũng trả về một hàm với chuỗi to được đặt thành [hàm gốc]).
Có hai cách để xem xét vấn đề này, từ quan điểm đặc tả và từ quan điểm triển khai. Hãy quan sát cả hai.
- Đặt Target là giá trị này.
- Nếu IsCallable (Target) là sai, hãy ném một ngoại lệ TypeError.
- Cho A là một danh sách nội bộ mới (có thể trống) của tất cả các giá trị đối số được cung cấp sau thisArg (arg1, arg2, v.v.), theo thứ tự.
...
(21. Gọi phương thức bên trong [[DefineOwnProperty]] của F với các đối số là "đối số", PropertyDescriptor {[[Get]]: thrower, [[Set]]: thrower, [[Enumerable]]: false, [[Có thể cấu hình] ]: false} và false.
(22. F trở lại.
Có vẻ khá phức tạp, nhiều hơn chỉ là một cái bọc.
Hãy kiểm tra FunctionBind
mã nguồn v8 (chrome JavaScript engine):
function FunctionBind(this_arg) {
if (!IS_SPEC_FUNCTION(this)) {
throw new $TypeError('Bind must be called on a function');
}
var boundFunction = function () {
"use strict";
if (%_IsConstructCall()) {
return %NewObjectFromBound(boundFunction);
}
var bindings = %BoundFunctionGetBindings(boundFunction);
var argc = %_ArgumentsLength();
if (argc == 0) {
return %Apply(bindings[0], bindings[1], bindings, 2, bindings.length - 2);
}
if (bindings.length === 2) {
return %Apply(bindings[0], bindings[1], arguments, 0, argc);
}
var bound_argc = bindings.length - 2;
var argv = new InternalArray(bound_argc + argc);
for (var i = 0; i < bound_argc; i++) {
argv[i] = bindings[i + 2];
}
for (var j = 0; j < argc; j++) {
argv[i++] = %_Arguments(j);
}
return %Apply(bindings[0], bindings[1], argv, 0, bound_argc + argc);
};
%FunctionRemovePrototype(boundFunction);
var new_length = 0;
if (%_ClassOf(this) == "Function") {
var old_length = this.length;
if ((typeof old_length === "number") &&
((old_length >>> 0) === old_length)) {
var argc = %_ArgumentsLength();
if (argc > 0) argc--;
new_length = old_length - argc;
if (new_length < 0) new_length = 0;
}
}
var result = %FunctionBindArguments(boundFunction, this,
this_arg, new_length);
return result;
Chúng ta có thể thấy rất nhiều thứ đắt tiền ở đây trong quá trình thực hiện. Cụ thể là %_IsConstructCall()
. Điều này tất nhiên là cần thiết để tuân theo đặc điểm kỹ thuật - nhưng nó cũng làm cho nó chậm hơn so với một gói đơn giản trong nhiều trường hợp.
Một lưu ý khác, cách gọi .bind
cũng hơi khác một chút, thông số kỹ thuật ghi chú "Các đối tượng hàm được tạo bằng Function.prototype.bind không có thuộc tính nguyên mẫu hoặc nội bộ [[Code]], [[FormalParameters]] và [[Phạm vi]] tính chất"