起因是朋友自己实现了一个bind,并产生了疑惑:自己实现的bind和原生的bind有什么不同,或者可能会有什么bug?
于是萌生了去查看一下v8中的实现,千辛万苦找到了原生实现,迎面就是一堆c++代码代码地址:
Object DoFunctionBind(Isolate* isolate, BuiltinArguments args) {
HandleScope scope(isolate);
DCHECK_LE(1, args.length());
if (!args.receiver()->IsCallable()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kFunctionBind));
}
// Allocate the bound function with the given {this_arg} and {args}.
Handle<JSReceiver> target = args.at<JSReceiver>(0);
Handle<Object> this_arg = isolate->factory()->undefined_value();
ScopedVector<Handle<Object>> argv(std::max(0, args.length() - 2));
if (args.length() > 1) {
this_arg = args.at(1);
for (int i = 2; i < args.length(); ++i) {
argv[i - 2] = args.at(i);
}
}
Handle<JSBoundFunction> function;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, function,
isolate->factory()->NewJSBoundFunction(target, this_arg, argv));
LookupIterator length_lookup(isolate, target,
isolate->factory()->length_string(), target,
LookupIterator::OWN);
// Setup the "length" property based on the "length" of the {target}.
// If the targets length is the default JSFunction accessor, we can keep the
// accessor that's installed by default on the JSBoundFunction. It lazily
// computes the value from the underlying internal length.
if (!target->IsJSFunction() ||
length_lookup.state() != LookupIterator::ACCESSOR ||
!length_lookup.GetAccessors()->IsAccessorInfo()) {
Handle<Object> length(Smi::zero(), isolate);
Maybe<PropertyAttributes> attributes =
JSReceiver::GetPropertyAttributes(&length_lookup);
if (attributes.IsNothing()) return ReadOnlyRoots(isolate).exception();
if (attributes.FromJust() != ABSENT) {
Handle<Object> target_length;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, target_length,
Object::GetProperty(&length_lookup));
if (target_length->IsNumber()) {
length = isolate->factory()->NewNumber(std::max(
0.0, DoubleToInteger(target_length->Number()) - argv.length()));
}
}
LookupIterator it(isolate, function, isolate->factory()->length_string(),
function);
DCHECK_EQ(LookupIterator::ACCESSOR, it.state());
RETURN_FAILURE_ON_EXCEPTION(isolate,
JSObject::DefineOwnPropertyIgnoreAttributes(
&it, length, it.property_attributes()));
}
// Setup the "name" property based on the "name" of the {target}.
// If the target's name is the default JSFunction accessor, we can keep the
// accessor that's installed by default on the JSBoundFunction. It lazily
// computes the value from the underlying internal name.
LookupIterator name_lookup(isolate, target, isolate->factory()->name_string(),
target);
if (!target->IsJSFunction() ||
name_lookup.state() != LookupIterator::ACCESSOR ||
!name_lookup.GetAccessors()->IsAccessorInfo() ||
(name_lookup.IsFound() && !name_lookup.HolderIsReceiver())) {
Handle<Object> target_name;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, target_name,
Object::GetProperty(&name_lookup));
Handle<String> name;
if (target_name->IsString()) {
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, name,
Name::ToFunctionName(isolate, Handle<String>::cast(target_name)));
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, name, isolate->factory()->NewConsString(
isolate->factory()->bound__string(), name));
} else {
name = isolate->factory()->bound__string();
}
LookupIterator it(isolate, function, isolate->factory()->name_string());
DCHECK_EQ(LookupIterator::ACCESSOR, it.state());
RETURN_FAILURE_ON_EXCEPTION(isolate,
JSObject::DefineOwnPropertyIgnoreAttributes(
&it, name, it.property_attributes()));
}
return *function;
}
} // namespace
// ES6 section 19.2.3.2 Function.prototype.bind ( thisArg, ...args )
BUILTIN(FunctionPrototypeBind) { return DoFunctionBind(isolate, args); }
注释中提到了ES6规范,我们的思路可能出现了一些偏差,既然V8的实现也是遵循规范的,那我们直接去看规范,规范会告诉我们这个bind函数需要做什么,并且会阐述函数一步步做了什么,用这个步骤来比较自己实现的bind函数即可(想到了网上很多手写promise并测试是否符合A+规范)ES6规范中说明的bind函数。