Встроенная привязка внешней функции к методу класса

Допустим, у меня есть функция, которая возвращает такую ​​функцию:

function createGreeter(logger) {
  return function greet(greeting) {
    logger.log(greeting + ', ' + this.name);
  }
}

И класс

class Person {
  constructor(name) {this.name = name;}
}

Если я хочу назначить метод приветствия классу Person, который использует консоль в качестве регистратора, я могу придумать несколько способов:

1.

class Person {
  constructor(name) {this.name = name;}

  greet(greeting) {
    return createGreeter(console).call(this, greeting);
  }
}

2.

class Person {
  constructor(name) {this.name = name;}
}

Person.prototype.greet = createGreeter(console);

Однако я думаю, что оба они несколько уродливы; 1) создает, по сути, ненужный метод-оболочку, который просто связывает this и вызывает функцию, и 2) изменяет прототип вне тела класса, что, на мой взгляд, делает API класса менее понятным.

Нет ли более четкого/короткого синтаксиса для встроенного назначения и привязки внешней функции к методу класса. Я думаю что-то вроде:

class Person {
  constructor(name) {this.name = name;}

  greet: createGreeter(console)
}

... что похоже на то, как вы могли бы назначить функцию в литерале объекта. Но это не работает, очевидно. Есть ли что-то подобное (сейчас или в будущем)?

Кроме того, меня интересуют аспекты потребления памяти и/или производительности при возврате закрытия, как в 1), если возвращаемая функция велика. Каждый раз, когда метод приветствия вызывается для объекта Person, будет создаваться новый объект функции, хотя мы всегда хотим передавать ему одни и те же параметры (console). Таким образом, еще один подход может состоять в том, чтобы объявить const consoleGreeter = createGreeter(console) перед определением класса и реализовать приветствие как return consoleGreeter.call(this, greeting), но стоит ли это того?


person JHH    schedule 19.12.2017    source источник
comment
Можете ли вы создать фрагмент, чтобы воспроизвести проблему? Это работает Person.prototype.greet = createGreeter(console); var p1 = new Person( "abc" ); p1.greet( "10" );   -  person gurvinder372    schedule 19.12.2017
comment
?? Я знаю, что это работает. Я просил более четкий или более короткий способ достижения того же результата. В JS очень много удобных сокращений, я надеялся, что пропустил одно для этого случая.   -  person JHH    schedule 19.12.2017
comment
Вы упомянули, что но это явно не работает для Person.prototype.greet = createGreeter(console);. Можете ли вы повторить это?   -  person gurvinder372    schedule 19.12.2017
comment
Второй ваш подход выглядит хорошо для меня. Какие подводные камни вы нашли с этим?   -  person Rajaprabhu Aravindasamy    schedule 19.12.2017
comment
@ gurvinder372 Подход к прототипу - это не тот, о котором он говорит. Он работает и должен. Но он просто дал пример кода, который предназначен только для иллюстрации, и он не будет работать, сказал он.   -  person Rajaprabhu Aravindasamy    schedule 19.12.2017
comment
Точно, спасибо Раджапрабху. Приветствие моего придуманного кода: createGreeter(console) не работает.   -  person JHH    schedule 19.12.2017


Ответы (3)


class Person {
  constructor(name) {this.name = name;}
}

Person.prototype.greet = createGreeter(console);

это правильный способ сделать это, если нет других проблем (у него будут проблемы с TypeScript).

Это также можно сделать с помощью полей класса, которые являются предложением этапа 3 и, скорее всего, попадут в ЕС2018:

class Person {
  greet = createGreeter(console);
}

Что является ярлыком для

class Person {
  constructor() {
    this.greet = createGreeter(console);
  }
}

Первый фрагмент оценивает createGreeter(console) один раз и присваивает метод прототипу класса. Второй фрагмент кода оценивает его каждый раз, когда создается экземпляр класса, и присваивает метод экземпляру класса, что менее эффективно.

person Estus Flask    schedule 19.12.2017
comment
Это очень интересно, на самом деле это очень похоже на мой придуманный код, но интересно узнать, что код будет оцениваться при построении, а не при объявлении. Я мог бы, конечно, сохранить здесь const consoleGreeter = createGreeter(console);, чтобы избежать затрат на создание нового объекта функции каждый раз, но я определенно склоняюсь к прямой манипуляции с прототипом, чтобы избежать необходимости делать это. - person JHH; 19.12.2017
comment
См. также этот связанный ответ, stackoverflow.com/a/46729995/3731501. Случай действительно близок к вашему, но в некоторой степени специфичен для TS, и OP намеренно пытался избежать прямого prototype доступа. - person Estus Flask; 19.12.2017

Итак, вот альтернатива bind внешнему методу class

class Person {
  constructor(name) {this.name = name;}
  get greet() { return createGreeter(console) }
}

Только что проверил, this тоже работает нормально.

person Vipin Kumar    schedule 19.12.2017
comment
Спасибо, вы и Патрик, по-видимому, одновременно ответили на одно и то же. Умное решение на самом деле. - person JHH; 19.12.2017

Это приведет к тому же эффекту № 1, поскольку оболочка вызывается каждый раз при доступе к методу. Если с вами все в порядке, чтобы улучшить читаемость...

function createGreeter(logger) {
  return function greet(greeting) {
    logger.log(`${greeting}, ${this.name}`);
  };
}

class Person {
  constructor(name) {
    this.name = name;
  }

  get greet() {
    return createGreeter(console);
  }
}

let person = new Person('Patrick');

person.greet('Hello');

person Patrick Roberts    schedule 19.12.2017
comment
Я должен признать, что это использование get на некоторое время сбило меня с толку; иметь свойство, возвращающее функцию, действительно довольно умно! И то, что он был автоматически привязан к this, также было полезно знать. - person JHH; 19.12.2017
comment
Также оцените полный, работоспособный фрагмент кода, спасибо! - person JHH; 19.12.2017