将函数附加到 JavaScript 对象文字的 __proto__ 属性 是个好主意吗?

Is it ever a good idea to append functions to a JavaScript Object Literal's __proto__ property?

我有一个对象字面量:

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 对象,它会减慢执行速度。但是我不是重新分配它,而是附加到它。

是否有更好的替代方案?

实际上没有必要为此使用原型。您不会创建许多需要在更高级别抽象的通用功能的实例,您只需在 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's documentation 的这些警告:

Warning: Changing the [[Prototype]] of an object is, by the nature of how modern JavaScript engines optimize property accesses, a very slow operation, in every browser and JavaScript engine. The effects on performance of altering inheritance are subtle and far-flung, and are not limited to simply the time spent in obj.proto = ... statement, but may extend to any code that has access to any object whose [[Prototype]] has been altered. If you care about performance you should avoid setting the [[Prototype]] of an object. Instead, create a new object with the desired [[Prototype]] using Object.create().

--

Warning: While Object.prototype.proto is supported today in most browsers, its existence and exact behavior has only been standardized in the ECMAScript 2015 specification as a legacy feature to ensure compatibility for web browsers. For better support, it is recommended that only Object.getPrototypeOf() be used instead.