Является ли когда-нибудь хорошей идеей добавлять функции в свойство __proto__ JavaScript Object Literal?

У меня есть объектный литерал:

var tasks = {};

То, что я в основном добавляю, чтобы вещи нравились так

function addTask(task) {
  tasks[task.id] = task
}

Я хочу изменить это так, чтобы я мог позвонить start функция на каждую задачу. Так:

var tasks = {};
tasks.__proto__.start = function(key) {
  // do stuff with this[key]
}

function addTask(task) {
  tasks[task.id] = task
  tasks.start(task.id)
}

Я слышал, что лучше избегать объекта proto и что он замедляет выполнение. Однако я не переназначаю его, я добавляю его.

Есть ли альтернатива, которая будет считаться лучшей?

2 ответа

Решение

Нет никакой необходимости использовать прототип для этого. Вы не создаете много случаев, когда вам нужна общая функциональность, выделенная на более высоком уровне, вы можете просто добавить метод на tasks объект.

const tasks = {
  start(key) {
    const task = this[key]
    // do stuff with task
  }
}

// example call
tasks.start('123'); 

Если вы хотите убедиться в отсутствии конфликта с существующим ключом, вы можете использовать Symbol вместо этого.

const startSymbol = Symbol('start');
const tasks = {
  [startSymbol](key) {
    const task = this[key]
    // do stuff with task
  }
}

// example call
tasks[startSymbol]('123'); 

Вы также можете просто иметь автономную функцию, которая делает это, как и ваш addTask функция:

function start(tasks, key) {
  const task = tasks[key]
  // do stuff with task
}

// example call
start(tasks, '123')

Наличие этой автономной функции, вероятно, лучше, потому что вам не придется беспокоиться о столкновениях между клавишами задач и именами методов.

Вы также можете создать объект-оболочку, который выполняет это разделение:

const taskManager = {

  tasks: {} // map of key to task

  // methods
  add(task) {
    this.tasks[task.id] = task;
    this.start(task.id);
  }
  start(key) {
    const task = this.tasks[key];
    // do stuff with task
  }
}

// example usage
taskManager.start('123')

Преимущество этого подхода в том, что ваш tasks инкапсулированы в контейнере, который манипулирует ими, ограничивая область, в которой tasks следует использовать и сделать его более понятным (предлагая программисту), какие функции предназначены для использования в задачах.

Если вы планируете иметь несколько менеджеров задач, тогда использование прототипов может иметь смысл здесь:

class TaskManager {
  constructor() {
    this.tasks = {}  // map of key to task
  }

  // methods
  add(task) {
    this.tasks[task.id] = task;
    this.start(task.id);
  }
  start(key) {
    const task = this.tasks[key];
    // do stuff with task
  }
}

// example usage
new TaskManager().start('123')

Это не очень хорошая идея как с точки зрения производительности, так и с точки зрения совместимости браузера.

Смотрите эти предупреждения из документации Mozilla:

Предупреждение. Изменение [[Prototype]] объекта в силу того, как современные движки JavaScript оптимизируют доступ к свойствам, является очень медленной операцией в каждом браузере и движке JavaScript. Эффекты на производительность изменения наследования являются тонкими и обширными, и не ограничиваются просто временем, проведенным в obj. операторproto =..., но может распространяться на любой код, который имеет доступ к любому объекту, чей [[Prototype]] был изменен. Если вы заботитесь о производительности, вам следует избегать установки [[Prototype]] объекта. Вместо этого создайте новый объект с желаемым [[Prototype]], используя Object.create ().

-

Предупреждение: пока Object.prototype.Протокол proto поддерживается сегодня в большинстве браузеров, его существование и точное поведение стандартизированы только в спецификации ECMAScript 2015 как устаревшая функция, обеспечивающая совместимость с веб-браузерами. Для лучшей поддержки рекомендуется использовать только Object.getPrototypeOf().

Другие вопросы по тегам