我在哪里可以看到 JavaScript 方法的源代码,例如 Node.js 中的 hasOwnProperty?

Where can I see the source code for JavaScript methods, such as hasOwnProperty, in Node.js?

我一直在研究 JavaScript 算法和 Big O 面试。有人告诉我,了解 Object.prototype.hasOwnPropertyArray.prototype.map 等内置方法的运行时间很重要。

查看node.js中这些函数的源代码的简单方法是什么?我有一个 node.js 的本地副本,我试图在我的文本编辑器中搜索这些方法,但它并不像我想象的那么简单。

Object.prototype.hasOwnProperty()

从 Javascript 面试的角度来看,我认为您只需要完全理解 obj.hasOwnProperty() 在 Javascript 级别的作用,而不是它在 V8 内部的实现方式。

为此,您应该完全理解这个小片段:

function MyConstructor() {
   this.methodB = function() {}
}

MyConstructor.prototype = {
    methodA: function() {}
};

var o = new MyConstructor();
log(o.hasOwnProperty("methodA"));    // false
log(o.hasOwnProperty("methodB"));    // true

o.methodA = function() {};           // assign "own" property, overrides prototype
log(o.hasOwnProperty("methodA"));    // true

这是因为.hasOwnProperty()只看对象本身,不看原型链。因此仅在原型链上或根本不存在的属性将 return false 而直接在对象上的属性将 return true.


Array.prototype.map()

Javascript 中用于 Array.prototype.map() 的 polyfill 是 here on MDN,它将向您展示它的确切工作原理。当然,您也可以根据需要在 Github 存储库中进行与我上面相同类型的搜索,以查找 .map() 实现。

Array.prototype.map() 真的很简单。遍历一个数组,为数组中的每一项调用一个函数。该函数的每个 return 值都将用于构造一个新数组,该数组将从对 .map() 的调用中 return 编辑。因此,从概念上讲,它用于通过对原始数组的每个元素调用一些转换函数来 "map" 一个数组到另一个数组。

在最简单的形式中,将 1 添加到数组的每个元素:

var origArray = [1,2,3];

var newArray = origArray.map(function(item, index, array) {
   return item + 1;
});

console.log(newArray);  // [2,3,4]

实际V8源代码:

如果你真的想看看它是如何在 V8 内部实现的,这里有代码片段和相关实际代码文件的链接。如您所见,其中大部分是用 C++ 编写的,要理解它,您必须了解对象在内存中的结构以及它们在 V8 中的内部 C++ 方法。这是 V8 特有的,不是一般的 Javascript 知识。

我也包含了指向相关源文件的链接,因此如果您想查看这些文件中的其他上下文,您可以单击链接进行查看。

v8.h中:

V8_DEPRECATED("Use maybe version", bool HasOwnProperty(Local<String> key));
V8_WARN_UNUSED_RESULT Maybe<bool> HasOwnProperty(Local<Context> context, Local<Name> key);

api.cc中:

Maybe<bool> v8::Object::HasOwnProperty(Local<Context> context,
                                       Local<Name> key) {
  PREPARE_FOR_EXECUTION_PRIMITIVE(context, "v8::Object::HasOwnProperty()",
                                  bool);
  auto self = Utils::OpenHandle(this);
  auto key_val = Utils::OpenHandle(*key);
  auto result = i::JSReceiver::HasOwnProperty(self, key_val);
  has_pending_exception = result.IsNothing();
  RETURN_ON_FAILED_EXECUTION_PRIMITIVE(bool);
  return result;
}

bool v8::Object::HasOwnProperty(Local<String> key) {
  auto context = ContextFromHeapObject(Utils::OpenHandle(this));
  return HasOwnProperty(context, key).FromMaybe(false);
}

v8natives.js中:

// ES6 7.3.11
function ObjectHasOwnProperty(value) {
  var name = TO_NAME(value);
  var object = TO_OBJECT(this);
  return %HasOwnProperty(object, name);
}

objects-inl.h中:

Maybe<bool> JSReceiver::HasOwnProperty(Handle<JSReceiver> object,
                                       Handle<Name> name) {
  if (object->IsJSObject()) {  // Shortcut
    LookupIterator it = LookupIterator::PropertyOrElement(
        object->GetIsolate(), object, name, LookupIterator::HIDDEN);
    return HasProperty(&it);
  }

  Maybe<PropertyAttributes> attributes =
      JSReceiver::GetOwnPropertyAttributes(object, name);
  MAYBE_RETURN(attributes, Nothing<bool>());
  return Just(attributes.FromJust() != ABSENT);
}

runtime-object.cc中:

static Object* HasOwnPropertyImplementation(Isolate* isolate,
                                            Handle<JSObject> object,
                                            Handle<Name> key) {
  Maybe<bool> maybe = JSReceiver::HasOwnProperty(object, key);
  if (!maybe.IsJust()) return isolate->heap()->exception();
  if (maybe.FromJust()) return isolate->heap()->true_value();
  // Handle hidden prototypes.  If there's a hidden prototype above this thing
  // then we have to check it for properties, because they are supposed to
  // look like they are on this object.
  if (object->map()->has_hidden_prototype()) {
    PrototypeIterator iter(isolate, object);
    DCHECK(!iter.IsAtEnd());

    // TODO(verwaest): The recursion is not necessary for keys that are array
    // indices. Removing this.
    // Casting to JSObject is fine because JSProxies are never used as
    // hidden prototypes.
    return HasOwnPropertyImplementation(
        isolate, PrototypeIterator::GetCurrent<JSObject>(iter), key);
  }
  RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate);
  return isolate->heap()->false_value();
}


RUNTIME_FUNCTION(Runtime_HasOwnProperty) {
  HandleScope scope(isolate);
  DCHECK(args.length() == 2);
  CONVERT_ARG_HANDLE_CHECKED(Object, object, 0)
  CONVERT_ARG_HANDLE_CHECKED(Name, key, 1);

  uint32_t index;
  const bool key_is_array_index = key->AsArrayIndex(&index);

  // Only JS objects can have properties.
  if (object->IsJSObject()) {
    Handle<JSObject> js_obj = Handle<JSObject>::cast(object);
    // Fast case: either the key is a real named property or it is not
    // an array index and there are no interceptors or hidden
    // prototypes.
    // TODO(jkummerow): Make JSReceiver::HasOwnProperty fast enough to
    // handle all cases directly (without this custom fast path).
    Maybe<bool> maybe = Nothing<bool>();
    if (key_is_array_index) {
      LookupIterator it(js_obj->GetIsolate(), js_obj, index,
                        LookupIterator::HIDDEN);
      maybe = JSReceiver::HasProperty(&it);
    } else {
      maybe = JSObject::HasRealNamedProperty(js_obj, key);
    }
    if (!maybe.IsJust()) return isolate->heap()->exception();
    DCHECK(!isolate->has_pending_exception());
    if (maybe.FromJust()) {
      return isolate->heap()->true_value();
    }
    Map* map = js_obj->map();
    if (!key_is_array_index && !map->has_named_interceptor() &&
        !map->has_hidden_prototype()) {
      return isolate->heap()->false_value();
    }
    // Slow case.
    return HasOwnPropertyImplementation(isolate, Handle<JSObject>(js_obj),
                                        Handle<Name>(key));
  } else if (object->IsString() && key_is_array_index) {
    // Well, there is one exception:  Handle [] on strings.
    Handle<String> string = Handle<String>::cast(object);
    if (index < static_cast<uint32_t>(string->length())) {
      return isolate->heap()->true_value();
    }
  } else if (object->IsJSProxy()) {
    Maybe<bool> result =
        JSReceiver::HasOwnProperty(Handle<JSProxy>::cast(object), key);
    if (!result.IsJust()) return isolate->heap()->exception();
    return isolate->heap()->ToBoolean(result.FromJust());
  }
  return isolate->heap()->false_value();
}

这是node.js Github repository。如果您知道要搜索什么并且有足够的耐心浏览所有搜索结果,您通常可以找到您需要的任何内容。在 Github 上搜索的不幸之处在于,我还没有找到任何方法从搜索中删除所有测试子目录,因此您最终会在测试代码中获得 95% 的搜索命中,而不是在实际实现中代码。但是,只要有足够的坚持,你最终会找到你需要的。