# Читать один модуль npm каждый день (7) - делегаты

внешний интерфейс koa NPM jQuery

Серия статей:

  1. Читать один модуль npm в день (1) — имя пользователя
  2. Читать один модуль npm в день (2) - mem
  3. Читать один модуль npm в день (3) — mimic-fn
  4. Чтение одного модуля npm в день (4) — Throttle-debounce
  5. Читать один модуль npm в день (5) - ee-first
  6. Читать один модуль npm в день (6) - pify

Поскольку я планирую подробно изучить коа и связанную с ним экологию, модули npm, которые я буду читать в следующем периоде, будут тесно связаны с коа ^_^

введение в одно предложение

Модули, прочитанные сегодня,delegates, в состав которого входят знаменитыеTJВ письменном виде это может помочь нам удобно и быстро использовать шаблоны проектирования.Шаблон делегирования, то есть объект, выставленный внешним слоем, делегирует запрос другим внутренним объектам для обработки.Текущая версия — 1.0.0, а еженедельный объем загрузки — около 3,64 млн.

Применение

delegatesОсновное использование состоит в том, чтобы связать переменные или функции внутреннего объекта с переменными, представленными во внешнем слое, непосредственно черезdelegatesМетод выполняет следующее делегирование.Основные методы делегирования включают в себя:

  • геттер: внешний объект может напрямую обращаться к значению внутреннего объекта
  • setter: внешний объект может напрямую изменять значение внутреннего объекта
  • access: содержит функции геттеров и сеттеров
  • метод: внешний объект может напрямую вызывать функцию внутреннего объекта
const delegates = require('./index');

const petShop = {
  dog: {
    name: '旺财',
    age: 1,
    sex: '猛汉',
    bar() {
      console.log('bar!');
    }
  },
}

// 将内部对象 dog 的属性、函数
// 委托至暴露在外的 petShop 上
delegates(petShop, 'dog')
  .getter('name')
  .setter('age')
  .access('sex')
  .method('bar');

// 访问内部对象属性
console.log(petShop.name)
// => '旺财'

// 修改内部对象属性
petShop.age = 2;
console.log(petShop.dog.age)
// => 2

// 同时访问和修改内部对象属性
console.log(petShop.sex)
// => '猛汉'
petShop.sex = '公主';
console.log(petShop.sex);
// => '公主'

// 调用内部对象函数
petShop.bar();
// 'bar!'

В дополнение к описанному выше методу вы также можете добавить к внешним объектам jQuery-подобные функции, а именно:

  • Когда функция не передает параметры, получить соответствующее значение
  • Когда функция передает параметры, измените соответствующее значение
const delegates = require('./index');

const petShop = {
  dog: {
    name: '旺财',
  },
}

delegates(petShop, 'dog')
  .fluent('name');

// 不传参数,获取内部属性
console.log(petShop.name());

// 传参数,修改内部属性
// 还可以链式调用
console.log(
    petShop.name('二哈')
    	.name('蠢二哈')
    	.name();
);

Изучение исходного кода

инициализация

// 源码 7 - 1
function Delegator(proto, target) {
  if (!(this instanceof Delegator)) return new Delegator(proto, target);
  this.proto = proto;
  this.target = target;
  this.methods = [];
  this.getters = [];
  this.setters = [];
  this.fluents = [];
}

thisМетоды | геттеры | сеттеры | красуются в объекте — все это массивы, которые используются для записи того, какие свойства и функции делегируются.

Стоит отметить первую строку приведенной выше функции инициализации: ifthisнетDelegatorэкземпляр, затем позвонитеnew Delegator(proto, target). Таким образом, вы можете не забыть написать при вызове функции инициализацииnewПроблема вызвана тем, что в настоящее время следующие два способа записи эквивалентны:

  • let x = new Delegator(petShop, 'dog')
  • let x = Delegator(petShop, 'dog')

Кроме того, давайте поговорим о вызовеnewВ основном сделайте следующее:

  1. внутри конструктораthisуказывает на вновь созданный пустой объект{}
  2. выполнить тело конструктора
  3. Если конструктор имеет явное возвращаемое значение, и значение является объектом, он возвращает ссылку на объект.
  4. Если конструктор не показывает возвращаемое значение или показывает, что возвращаемое значение не является объектом (например, показывает возвращаемое значение 1, «ха-ха» и т. д.), верните это

getter

// 源码 7-2
Delegator.prototype.getter = function(name){
  var proto = this.proto;
  var target = this.target;
  this.getters.push(name);

  proto.__defineGetter__(name, function(){
    return this[target][name];
  });

  return this;
};

Ключ в приведенном выше коде заключается в том, что__defineGetter__Он может добавлять читаемые свойства к существующим объектам, где первый параметр — это имя свойства, второй параметр — функция, а возвращаемое значение — соответствующее значение свойства:

const obj = {};
obj.__defineGetter__('name', () => 'elvin');

console.log(obj.name);
// => 'elvin'

obj.name = '旺财';
console.log(obj.name);
// => 'elvin'
// 我怎么能被改名叫旺财呢!

Следует отметить, что хотя__defineGetter__Он широко использовался, но больше не рекомендуется, рекомендуется пройтиObject.definePropertyдостичь той же функции или путемgetАналогичную функцию реализует оператор:

const obj = {};
Object.defineProperty(obj, 'name', {
  value: 'elvin',
});

Object.defineProperty(obj, 'sex', {
  get() {
    return 'male';
  }
});

const dog = {
  get name() {
    return '旺财';
  }
};

Кто-то на Github предложил соответствующийPR#20, но поскольку TJ покинул сообщество Node.js, предполагается, что этот репозиторий не будет обновляться.

setter

// 源码 7-3
Delegator.prototype.setter = function(name){
  var proto = this.proto;
  var target = this.target;
  this.setters.push(name);

  proto.__defineSetter__(name, function(val){
    return this[target][name] = val;
  });

  return this;
};

Приведенный выше код аналогиченgetterпочти идентичны, но с использованием__defineSetter__, который может добавлять читаемые свойства к существующему объекту, где первый параметр — это имя свойства, второй параметр — функция, а параметр — входящее значение:

const obj = {};
obj.__defineSetter__('name', function(value) {
  this._name = value;
});

obj.name = 'elvin';
console.log(obj.name, obj._name);
// undefined 'elvin'

Аналогично, хотя__defineSetter__Он широко использовался, но больше не рекомендуется, рекомендуется пройтиObject.definePropertyдостичь той же функции или путемsetАналогичную функцию реализует оператор:

const obj = {};
Object.defineProperty(obj, 'name', {
  set(value) {
    this._name = value;
  }
});

const dog = {
  set(value) {
    this._name = value;
  }
};

method

// 源码 7-4
Delegator.prototype.method = function(name){
  var proto = this.proto;
  var target = this.target;
  this.methods.push(name);

  proto[name] = function(){
    return this[target][name].apply(this[target], arguments);
  };

  return this;
};

methodРеализация тоже очень простая, только обратите внимание на вотapplyПервый параметр функции — это внутренний объектthis[target], тем самым обеспечивая выполнение функцииthis[target][name], тело функцииthisявляется указателем на соответствующий внутренний объект.

разноеdelegatesПредусмотрены такие функции, какfluent | accessВсе они похожи и не повторяются.

использовать в коа

В коа его ядро ​​лежит вcontextобъект, многие операции чтения и записи основаны на нем, например:

  • ctx.header Получить заголовок запроса

  • ctx.method Получить метод запроса

  • ctx.url Получить URL запроса

  • ...

Получение этих параметров запроса выигрывает от koacontext.requestМногие атрибуты довереныcontextначальство:

// Koa 源码 lib/context.js
delegate(proto, 'request')
  .method('acceptsLanguages')
  .method('acceptsEncodings')
  .access('path')
  .access('url')
  .getter('headers')
  .getter('ip');
  // ...

Другой пример:

  • ctx.body устанавливает тело ответа
  • ctx.status устанавливает код состояния ответа
  • ctx.redirect() запрос перенаправления
  • ...

Эти настройки для параметров ответа выигрывают от koacontext.responseМногие свойства и методы довереныcontextначальство:

// Koa 源码 lib/context.js
delegate(proto, 'response')
  .method('redirect')
  .method('vary')
  .access('status')
  .access('body')
  .getter('headerSent')
  .getter('writable');
  // ...

О себе: Окончил Хуаке, работаю в Tencent,блог ЭлвинаДобро пожаловать в гости ^_^