Расширение JavaScript типы возвращают


На самом деле я изучаю Javascript Крокфорда : хорошие части. Я новичок в JavaScript, поэтому мне трудно понять, как работает этот код:

Function.prototype.method = function (name, func) {
    this.prototype[name] = func;
    return this;
};

Вот что я думаю:

Будучи методом (функцией внутри объекта), this указывает на объект Function, но зачем возвращать объект, если у меня есть доступ к нему изнутри метода? Если я прав, this является ссылкой, а не локальной копией, поэтому:

Function.prototype.method = function (name, func) {
    this.prototype[name] = func;
};

Должен работать также.

С другой стороны, в JavaScript функция без оператора return возвращает undefined и присваивает его Function.prototype.method.

Вопрос

Какой смысл возвращаться this?


Рабочий пример #1

Function.prototype.method = function (name, func) {
    this.prototype[name] = func;
    return this;
};
var add = function(a, b) {
    return a+b;
};

Function.method('add', add);
var f = function() {};

print(f.add(1,2));

Number.method('integer', function () {
        return Math[this < 0 ? 'ceil' : 'floor'](this);
        });

print((-10/3).integer());

Вывод:

-3 3


Рабочий Пример #2

Function.prototype.method = function (name, func) {
    this.prototype[name] = func;
};

var add = function(a, b) {
    return a+b;
};

Function.method('add', add);
var f = function() {};

print(f.add(1,2));

Number.method('integer', function () {
        return Math[this < 0 ? 'ceil' : 'floor'](this);
        });

print((-10/3).integer());

Вывод:

-3 3

3 7

3 ответа:

Позвольте мне попытаться объяснить это. Я не читал эту книгу, но в статье классическое наследование в JavaScript Дугласа Крокфорда есть одно важное предложение, связанное с этим примером о функции.прототип.метод:

Он возвращает это. Когда я пишу метод это не должно возвращать значение, я обычно он возвращает это. Это позволяет для каскадного стиля программирования.

На самом деле я не знаком с этим термином, я думаю, что хорошо известный термин-это "Fluent Interface " или "Method Chaining", прочитайте эту страницу вики, там есть примеры на разных языках, так что вы поймете это..

ПС. @Gianluca Bargelli был немного быстрее, чтобы предоставить пример использования функции.прототип.метод такой, поэтому я не публикую его в своем ответе

ADDON: как вы можете использовать его в терминах вашего примера:

Function.prototype.method = function (name, func) {
    this.prototype[name] = func;
    return this;
}

Number.method('integer', function () {  // you add 'integer' method
        return Math[this < 0 ? 'ceil' : 'floor'](this);
        })
      .method('square', function () {  // you add 'square' method with help of chaining
        return this * this;
        });

console.info( (-10/3).integer().square() ); // <- again chaining in action

Вы видите, integer() возвращает объект Number, поэтому вы можете вызвать другой метод, а не пишет:

var a = (-10/3).integer();
console.info( a.square() ); 

И несколько слов о моем способе его использования, большую часть времени я предпочитаю писать "каждый метод-новая строка с отступом, для меня этот способ более удобочитаем:

Function.method('add', add)
        .method('sub', sub)
        .method('mul', mul)
        .method('div', div);

Таким образом, я вижу, где я начинаю, и "новая строка/отступ" говорит мне, что я все еще изменяю этот объект. Сравните его с длинной строкой:

Function.method('add', add).method('sub', sub).method('mul', mul).method('div', div);

Или типичный подход:

Function.method('add', add);
Function.method('sub', sub);
Function.method('mul', mul);
Function.method('div', div);

ADDON2: обычно я использую этот подход (Fluent interface pattern) при работе с сущностями, например Java код:

public class Person {
  private String name;
  private int age;
  ..

  public String getName() {
    return this.name;
  }

  public Person setName( String newName ) {
    this.name = newName;
    return this;
  }

  public int getAge() {
    return this.age;
  }

  public Person setAge( int newAge ) {
    this.age = newAge;
    return this;
  }

  ..
}

Это позволяет мне легко построить объект Person:

Person person = new Person().setName("Leo").setAge(20);

Некоторые люди делают его немного другим, они добавляют новый вид методов к set/get, и назовем его with:

public class Person {
  private String name;
  private int age;
  ..

  public String getName() {
    return this.name;
  }

  public void setName( String newName ) {
    this.name = newName;
  }

  public Person withName( String newName ) {
    this.setName( newName ); // or this.name = newName; up to you
    return this;
  }

  public int getAge() {
    return this.age;
  }

  public void setAge( int newAge ) {
    this.age = newAge;
  }

  public Person withAge( int newAge ) {
    this.setAge( newAge ); // or this.age = newAge; up to you
    return this;
  }
  ..
}

Теперь мой предыдущий пример выглядит так:

Person person = new Person().withName("Leo").withAge(20);

Таким образом, мы не меняем значение метода set (я имею в виду, что мы не улучшаем его, поэтому он работает так, как ожидает большинство разработчиков... по крайней мере, люди не ожидают, что метод set может вернуть что-либо ;) ). Один интересный дело в этих специальных методах - они могут потерять свою самодокументацию, но они улучшают читаемость, когда вы их используете (как в Примере с Person creation, withName очень хорошо говорит, что именно мы делаем..

Читать дальше:
FluentInterface - описание этого паттерна Мартином Фаулером
свободные интерфейсы на PHP
Еженедельный исходный код 14-Fluent Interface Edition - как по мне короткий и достаточно хороший, чтобы увидеть плюсы и минусы (а также ссылки на другие ресурсы)

Сегодня днем я отправил электронное письмо Дугласу Крокфорду с этим вопросом, и его ответ был:

F. метод (а).метод (b).метод (c)

Я не шучу. Это была единственная вещь, которую он написал.

Так или иначе, моя личная интерпретация его (загадочного) ответа- создание цепного метода :

Function.prototype.method = function (name, func) {
    this.prototype[name] = func;
    return this; //This returns the same Function object into the chain below
};

var add = function (a, b) { return a+b; };
var sub = function (a, b) { return a-b; };
var mul = function (a, b) { return a*b; };
var div = function (a, b) { return a/b; };

Function.method('add', add).method('sub', sub).method('mul', mul).method('div', div);
То есть вместо того, чтобы создавать новые методы, используя одну строку за раз, можно повторно применить следующую метод в цепочке по возвращаемому объекту предыдущего, Function.

В этом примере цепочка идет от слева направо :

|Function|--method-->|add|--returns-->|Function|--method-->|sub|--returns-->|Function|--method-->|mul|--returns-->|Function|--method-->|div|-->returns-->|Function|

Я не совсем понимаю, о чем вы просите, но если вы ничего не вернете, вы ничего не назначите функции.прототип.метод, ведьма вроде как делает это предложение бесполезным, не так ли?