Как получить ссылки на объекты JavaScript или счетчик ссылок? - PullRequest
54 голосов
/ 30 мая 2010

Как получить счетчик ссылок для объекта

  • Можно ли определить, имеет ли объект javascript несколько ссылок на него?
  • Или, если у него есть ссылки помимо той, к которой я обращаюсь с ?
  • Или даже просто получить счетчик ссылок сам по себе?
  • Могу ли я найти эту информацию из самого javascript или мне нужно будет отслеживать мои собственные счетчики ссылок.

Очевидно, что для моего кода доступа к объекту должна быть хотя бы одна ссылка на него. Но я хочу знать, есть ли какие-либо другие ссылки на него, или если мой код - единственное место, к которому он доступен. Я хотел бы иметь возможность удалить объект, если на него ничего не ссылается.

Если вы знаете ответ, нет необходимости читать остальную часть этого вопроса. Ниже приведен лишь пример, чтобы прояснить ситуацию.


Вариант использования

В моем приложении у меня есть Repository экземпляр объекта с именем contacts, который содержит массив ALL моих контактов. Существует также несколько экземпляров объекта Collection, таких как коллекция friends и коллекция coworkers. Каждая коллекция содержит массив с различным набором элементов из contacts Repository.

Пример кода

Чтобы конкретизировать эту концепцию, рассмотрим код ниже. Каждый экземпляр объекта Repository содержит список всех элементов определенного типа. Возможно, у вас есть хранилище Contacts и отдельное хранилище Events . Для простоты вы можете просто получать, добавлять и удалять элементы, а также добавлять многие с помощью конструктора.

var Repository = function(items) {
  this.items = items || [];
}
Repository.prototype.get = function(id) {
  for (var i=0,len=this.items.length; i<len; i++) {
    if (items[i].id === id) {
      return this.items[i];
    }
  }
}
Repository.prototype.add = function(item) {
  if (toString.call(item) === "[object Array]") {
    this.items.concat(item);
  }
  else {
    this.items.push(item);
  }
}
Repository.prototype.remove = function(id) {
  for (var i=0,len=this.items.length; i<len; i++) {
    if (items[i].id === id) {
      this.removeIndex(i);
    }
  }
}
Repository.prototype.removeIndex = function(index) {
  if (items[index]) {
    if (/* items[i] has more than 1 reference to it */) {
      // Only remove item from repository if nothing else references it
      this.items.splice(index,1);
      return;
    }
  }
}  

Обратите внимание на строку в remove с комментарием. Я хочу удалить элемент из моего главного хранилища объектов только в том случае, если другие объекты не имеют ссылки на этот элемент. Вот Collection:

var Collection = function(repo,items) {
  this.repo = repo;
  this.items = items || [];
}
Collection.prototype.remove = function(id) {
  for (var i=0,len=this.items.length; i<len; i++) {
    if (items[i].id === id) {
      // Remove object from this collection
      this.items.splice(i,1);
      // Tell repo to remove it (only if no other references to it)
      repo.removeIndxe(i);
      return;
    }
  }
}

А затем этот код использует Repository и Collection:

var contactRepo = new Repository([
    {id: 1, name: "Joe"},
    {id: 2, name: "Jane"},
    {id: 3, name: "Tom"},
    {id: 4, name: "Jack"},
    {id: 5, name: "Sue"}
  ]);

var friends = new Collection(
  contactRepo,
  [
    contactRepo.get(2),
    contactRepo.get(4)
  ]
);

var coworkers = new Collection(
  contactRepo,
  [
    contactRepo.get(1),
    contactRepo.get(2),
    contactRepo.get(5)
  ]
);

contactRepo.items; // contains item ids 1, 2, 3, 4, 5 
friends.items;  // contains item ids 2, 4
coworkers.items;  // contains item ids 1, 2, 5

coworkers.remove(2);

contactRepo.items; // contains item ids 1, 2, 3, 4, 5 
friends.items;  // contains item ids 2, 4
coworkers.items;  // contains item ids 1, 5

friends.remove(4);

contactRepo.items; // contains item ids 1, 2, 3, 5 
friends.items;  // contains item ids 2
coworkers.items;  // contains item ids 1, 5

Обратите внимание, как coworkers.remove(2) не удалил id 2 из contactRepo? Это потому, что на него все еще ссылаются из friends.items. Однако friends.remove(4) приводит к удалению идентификатора 4 из contactRepo, поскольку ни одна другая коллекция не ссылается на него.

Резюме

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

Ответы [ 2 ]

16 голосов
/ 30 мая 2010

Нет, нет, нет, нет; и да, если вам действительно нужно сосчитать ссылки, вам придется делать это вручную. У JS нет интерфейса с этим, GC или слабых ссылок.

Хотя вы могли бы реализовать список объектов с ручным подсчетом ссылок, сомнительно, что все дополнительные издержки (с точки зрения производительности, но, что более важно, сложность кода) того стоят.

В вашем примере кода было бы проще забыть Repository, использовать простой Array для своих списков и позволить стандартной сборке мусора позаботиться о том, чтобы выбрасывать неиспользуемых людей. Если вам нужно было получить список всех используемых пользователей, вам нужно просто concat списки friends и coworkers (и сортировать / унифицировать их, если вам нужно).

1 голос
/ 18 июля 2016

Вам может быть интересно взглянуть на функции Reduce и функции array.map. карту можно использовать, чтобы помочь определить, где пересекаются ваши коллекции или есть ли пересечение вообще. Определяемая пользователем функция сокращения может использоваться как слияние (что-то вроде переопределения оператора сложения, так что вы можете применить операцию к объектам или объединить все коллекции по «id», если именно так вы определяете свою функцию сокращения - затем присвойте результат Ваш основной справочный массив, я рекомендую сохранить теневой массив, который содержит все корневые объекты / значения на случай, если вы захотите REWIND или что-то еще). Примечание: нужно быть осторожным с цепочками прототипов при уменьшении объекта или массива. Функция карты будет очень полезна в этом случае.

Я бы предложил , а не , чтобы удалить объект или запись, находящуюся в вашем репозитории, так как вы, возможно, захотите сослаться на нее позже. Мой подход заключается в создании ShadowRepository, который будет отражать все записи / объекты, которые имеют хотя бы одну «Ссылку». Из вашего описания и кода, представленного здесь, видно, что вы инициализируете все данные и сохраняете ссылку на 1,2,4,5, как показано в вашем коде.

var contactRepo = new Repository([
    {id: 1, name: "Joe"},
    {id: 2, name: "Jane"},
    {id: 3, name: "Tom"},
    {id: 4, name: "Jack"},
    {id: 5, name: "Sue"}
]);
var friends = new Collection(contactRepo,[
    contactRepo.get(2),
    contactRepo.get(4)
]);

var coworkers = new Collection(contactRepo,[
    contactRepo.get(1),
    contactRepo.get(2),
    contactRepo.get(5)
]);

Начиная с инициализации Репозитория и коллекций, то, что вы просите «Удалить элемент из репозитория, если на него нет ссылок», пункт 3 должен быть немедленно удален. Однако вы можете отслеживать ссылки несколькими разными способами.

Я рассмотрел использование Object.observe для аналогичной ситуации. Однако Object.observe не работает во всех браузерах. Я недавно обратился к WatchJS

Я работаю над пониманием кода, стоящего за Watch.JS, чтобы позволить динамически создавать список наблюдателей на объекте, что позволило бы также удалить элемент, который больше не отслеживается, хотя я предлагаю удалить ссылку на точка доступа. То, что я имею в виду, - это переменная, которая разделяет непосредственную лексическую область видимости с объектом, который дал единственную точку ссылки на своего родного брата, и может быть удален, делая его более недоступным вне объекта, который выставил запись / элемент / свойство / объект его родного брата. Со ссылкой, что все ваши другие ссылки зависели от удаленного доступа к базовым данным, остановлен. Я создаю уникальный идентификатор для ссылок на источники, чтобы избежать случайного повторного использования одного и того же.

Спасибо за то, что поделились своим вопросом и структурой, которую вы используете, это помогло мне рассмотреть один из моих собственных конкретных случаев, когда я генерировал уникально идентифицированные ссылки на лексического родственного брата, эти уникальные идентификаторы были сохранены для ОДНОГО объекта, который имел область После прочтения здесь я пересмотрел и решил раскрыть только одну ссылку, а затем присвоить эту ссылку имени переменной там, где это необходимо, например, при создании наблюдателя, наблюдателя или другой коллекции.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...