Могу ли я привязать методы прототипа конструктора к построенным экземплярам, ​​оставив проблемы разделенными?

Скажем, у меня есть конструктор объекта и метод прототипа, например:

function Human(name) {
  this.name = name;
}

Human.prototype.sayName = function(){
  console.log('my name' + this.name);
};

В другом месте моего кода я определил экземпляр человека:

let jeff = new Human('jeff');

и, наконец, я хочу передать jeff.sayName как обратный вызов какой-либо другой функции, например (для особо тривиального примера)

function callFunction(callback) {
  callback();
}

callFunction(jeff.sayName);

Когда я вызываю callFunction(jeff.sayName) выше, контекст должен быть привязан к jeff сам, например callFunction(jeff.sayName.bind(jeff)). Но это неудобно, и я бы не стал беспокоиться об этой логике каждый раз, когда я использую этот метод в качестве обратного вызова.

Альтернативой было бы заменить Human.prototype.sayName чем-то вроде

Human.prototype.createSayName = function(context){
  return function() {
    console.log(context.name);
  };
};

а затем определите функцию с

jeff.sayName = jeff.createSayName(jeff)

но это немного неудобно, и я хотел бы сохранить агностик jeff для методов, которые он наследует от Human.prototype.

В идеале я бы хотел обработать эту логику самого Human.prototype, что-то вроде

Human.prototype.sayName.bind(WhateverObjectHoldsThisMethod)

но я не знаю, что у javascript есть способ сделать это.

TL; DR Мне нужен способ привязки метода прототипа объекта к любому наследуемому ему объекту, без необходимости делать это каждый раз, когда я передаю этот метод в качестве аргумента или каждый раз, когда я определяю новый объект. Извините, если это мало смысла. Спасибо!

Ответ 1

Из-за того, что в JavaScript работают лексические среды, разрешение области, прототипное наследование и записи среды, то, что вы просите, невозможно без изменения функции, вызывающей функцию обратного вызова.

Однако вы могли бы вместо передачи ссылки Human#sayName в качестве обратного вызова использовать функцию стрелки, которая в свою очередь вызывает ссылку Human#sayName, которую вы хотите вызвать.

Это не идеальный, но простой, чистый и читаемый.

function Human(name) {
  this.name = name;
}

Human.prototype.sayName = function(){
  console.log('my name' + this.name);
};

let jeff = new Human('jeff');

function callFunction(callback) {
  callback();
}

callFunction(_ => jeff.sayName());

Ответ 2

Расширяясь в ответ TinyGiant, вы можете использовать функцию стрелки, но если вы объедините ее с геттером, вы можете определить ее как метод прототипа и не беспокоиться о том, чтобы определить ваш обратный вызов как функцию стрелки, что может быть больше гибкий в зависимости от ваших потребностей. Вот так:

function Human(name) {
  this.name = name;
}

Object.defineProperty(Human.prototype, "sayName", {
  get: function() {
    return () => {
      console.log("my name is", this.name);
    }
  }
});


function callfunction(callback) {
  callback();
}

let jeff = new Human('jeff');

callfunction(jeff.sayName);

// just to show it works even as a regular function
jeff.sayName();

// in fact it overrides every context you bind
jeff.sayName.bind(window)()

Ответ 3

Я полагаю, вы можете достичь этого

function Human(name) {
  this.name = name;
}

Human.prototype.sayName = function() {
  console.log('my name' + this.name);
};

let jeff = new Human('jeff');

function callFunction(callback) {
  callback();
}

callFunction(function() {
  jeff.sayName()
});