What is the equivalent of Python’s dir function in Javascript?

Asked

Viewed 147 times

8

I was looking for a javascript function equivalent to the function dir python. In Python, if I want to see all methods associated with a given object, just pass the function dir directly to constructor of the object or an object instance. So, to see methods associated with strings, for example, I do:

print(dir(str))

Or:

print(dir("stack"))

In Javascript, I discovered the method getOwnPropertyNames. However, when I used this method I had access to what appears to be only a portion of the methods of the object in question. Example:

console.log(Object.getOwnPropertyNames(Array))

Returns:

[ 'length', 'name', 'prototype', 'isArray', 'from', 'of' ]

It turns out that Arrays in Javascript also has the method sort():

var names = ['Jesse', 'Walter', 'Hank']
console.log(names.sort())

Resulting:

[ 'Hank', 'Jesse', 'Walter' ]

Because the sort did not appear in the list of Array when I used Object.getOwnPropertyNames? There is a Javascript function equivalent to dir Python returns the complete list of methods of an object?

1 answer

7


This is because, in fact, methods such as sort do not belong to the builder Array, but yes to the Array.prototype.

Note, in the exit code you ran, that the Array has a property called prototype. This is where are all the methods and properties that can be accessed in any instance of Array.

So, to list all the methods of instances of a Array, do:

console.log(Object.getOwnPropertyNames(Array.prototype));

In Javascript, when you instantiate an object from a constructor, the object <constructor>.prototype will be associated with the prototype of the built object. This is explained in more detail in How the Javascript prototype chain works?. The question of nomenclature is explained in more detail here.

Think of the properties of Array as "static methods" and in the properties of Array.prototype as "instance methods".

Note that the syntax of array literal is basically syntactic sugar for new Array(). So the two ways to create a array below are equivalent:

const arr1 = new Array();
const arr2 = [];

Note that you cannot use the function Object.getOwnPropertyNames directly in an instance to try to access its "inherited" methods, since, as the function name says, only the "Own properties", that is, the properties defined in the object itself will be returned. As properties like sort and forEach were not defined directly in each instance, but in your prototype, you will not have access to them.

You can use the function Reflect.getPrototypeOf to access the prototype of any object, which, theoretically, cannot be accessed "directly":

console.log(
  // Retorna as propriedades do protótipo de `[]` (uma instância de `Array`)
  Object.getOwnPropertyNames(
    // Acessa o protótipo do 1º argumento. No caso, `[]`.
    Reflect.getPrototypeOf([])
  )
);

I used the term "theoretically" above because, by specification, the prototype of an object is an "internal property of language". However, there are ways to access it, such as property __proto__ and the functions Object.getPrototypeOf and Reflect.getPrototypeOf.

When you give a console.dir in some object (such as an instance of Array[]), the property __proto__ that appears there is a reference to the prototype of the object. It is this prototype (which is not "accessible" directly, as we have seen above) which contains the methods (such as sort or forEach) that you use.

It’s a bit confusing. Be sure to understand what is prototype in Javascript (something essential to understand this answer). This another answer is also excellent. Also, be sure to know how to nomenclature works not to write wrong (Array.prototype.forEach is different from Array.forEach).


If you really want to list all properties (including nonenumerable, symbols and "inherited" via prototypical inheritance), you can do your own function dir. Thus:

function dir(obj) {
  const allKeys = new Set();
  let currProto = Object(obj);

  while (currProto) {
    for (const key of Reflect.ownKeys(currProto)) {
      allKeys.add(key);
    }

    currProto = Reflect.getPrototypeOf(currProto);
  }

  // Retorna em formato de array por conveniência.
  return [...allKeys];
}

The code basically traverses the entire chain of prototypes of the given value by inserting into the set all the properties he finds. I used the Set because it avoids duplicated elements (which may occur).

If there are duplicated properties, the "next" one will be returned - this behavior is due to the prototypical Javascript model. Learn more about the Reflect.ownKeys.

Example of use:

function dir(obj) {
  const allKeys = new Set();
  let currProto = Object(obj);

  while (currProto) {
    for (const key of Reflect.ownKeys(currProto)) {
      allKeys.add(key);
    }

    currProto = Reflect.getPrototypeOf(currProto);
  }

  return [...allKeys];
}

const myArr = [1, 2, 3];
const allKeys = dir(myArr);

console.log(allKeys.includes('0')); //=> true; índice 0 (é uma propriedade, já que arrays nada mais são que objetos)
console.log(allKeys.includes('map')); //=> true; herdada via herança prototípica
console.log(allKeys.includes('from')); //=> false; é método "estático" de array. Logo, não existe na instância

console.log(allKeys);

The null that appeared on the last console.log of the above example are the properties with symbolic key (type symbol). The console of snippet Stack Overflow does not know how to display symbols correctly, which justifies the impression of null.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.