Как я могу получить все методы предков в JS (прототипное наследование)?

#javascript #inheritance #methods #prototype #prototypal-inheritance

Вопрос:

Мне нужно создать функцию в JS, которая получает все методы из всего дерева наследования, чтобы вставить по одному <button> для каждого метода. У меня есть этот код

 function A () {
}

A.prototype.a = function () {
    console.log('a');
}

function B () {
    this.super = A;
    this.super();
}

B.prototype = new A;
B.prototype.constructor = B;

B.prototype.b = function () {
    console.log('b');
}

function C () {
    this.super = B;
    this.super();
}

C.prototype = new B;
C.prototype.constructor = C;

C.prototype.c = function () {
    console.log('c');
}

function D () {
}

D.prototype = C;
D.prototype.constructor = D;

D.prototype.d = function () {
    console.log('d');
}

const dd = new D();
 

Мне нужен какой-то способ найти методы всего дерева на случай, если я не знал, сколько предков у объекта. Например: Если объект C имел A и B в качестве своих предков, мне нужны методы из A, B и C; Если объект D был дочерним объектом A-B-C, мне нужны методы из них четырех (A, B, C и D). Я могу вручную отследить каждый метод, чтобы написать код, который делает это за меня, но мне нужно, чтобы он был динамичным.

Это то, что я использую:

 console.log(Object.getOwnPropertyNames(dd.__proto__).filter(function (property) {
    return (
        typeof dd.__proto__[property] === 'function' amp;amp; property !== 'constructor'
    );
}))
 

Комментарии:

1. Каков точный результат, который вы ожидаете для примера? Вам нужно встретиться с владельцем метода? Какой-нибудь конкретный заказ?

2. Во-вторых, ожидаете ли вы, что будут перечислены функции прототипа, которые принадлежат Object.prototype ?

3. Привет! В коде, который я написал, выводится только метод D. Мне не нужно видеть владельца метода, и порядок не имеет значения. Мне не нужны методы Объекта. Если я изменю dd.__proto__ на dd.prototype, я получу метод C; Если вместо этого я напишу dd.prototype.__proto__, я получу метод B; чтобы получить метод A, я должен поместить dd.prototype._________________________________. Это затрудняет автоматизацию работы.

Ответ №1:

В вашем установочном коде допущена ошибка:

Изменить:

 D.prototype = C;
 

Для:

 D.prototype = new C;
 

Затем, чтобы получить список методов (даже если они не перечислимы), выполните рекурсивный вызов. Вы даже можете сделать из него генератор.

Чтобы прояснить, откуда берутся методы, я добавил к ним префикс с именем объекта-прототипа (например A.prototype ). Вы увидите, что когда функции с одинаковым именем существуют на нескольких уровнях цепочки прототипов, они попадают в список:

 function * iterMethods(o) {
    if (!o || o === Object.prototype) return;
    for (let name of Object.getOwnPropertyNames(o)) {
        try {
            if (name !== "constructor" amp;amp; typeof o[name] === "function") {
                yield o.constructor.name   ".prototype."   name;
            }
        } catch {}
    }
    yield * iterMethods(Object.getPrototypeOf(o));
}

// Your code:
function A () {
}

A.prototype.a = function () {
    console.log('a');
}

function B () {
    this.super = A;
    this.super();
}

B.prototype = new A;
B.prototype.constructor = B;

B.prototype.b = function () {
    console.log('b');
}

function C () {
    this.super = B;
    this.super();
}

C.prototype = new B;
C.prototype.constructor = C;

C.prototype.c = function () {
    console.log('c');
}

function D () {
}

D.prototype = new C;
D.prototype.constructor = D;

D.prototype.d = function () {
    console.log('d');
}

const dd = new D();

// The call:
console.log(Array.from(iterMethods(Object.getPrototypeOf(dd)))); 

Эту иерархию прототипов легче настроить, когда вы используете class ... extends синтаксис. Я бы также не стал определять super , как это ключевое слово, которое изначально доступно. Вот как это будет выглядеть:

 function * iterMethods(o) {
    if (!o || o === Object.prototype) return;
    for (let name of Object.getOwnPropertyNames(o)) {
        try {
            if (name !== "constructor" amp;amp; typeof o[name] === "function") {
                yield o.constructor.name   ".prototype."   name;
            }
        } catch {}
    }
    yield * iterMethods(Object.getPrototypeOf(o));
}

// class syntax version:
class A {
    a() {
        console.log('a');
    }
}

class B extends A {
    b() {
        console.log('b');
    }
}

class C extends B {
    c() {
        console.log('c');
    }
}

class D extends C {
    d() {
        console.log('d');
    }
}

const dd = new D();

// The call:
console.log(Array.from(iterMethods(Object.getPrototypeOf(dd)))); 

Комментарии:

1. Большое спасибо, чувак. Это именно то, что я искал. Единственное, что я изменил, — это первое yield , чтобы получить только имя метода.