Изолировать исполнение JavaScript

Одним из недостатков JS, которое меня больше всего беспокоит, является плохая способность изолировать выполнение кода.

Я хочу иметь возможность контролировать контекст, в котором выполняется код, что-то такое, что дает эффект, аналогичный тому, что Script.createContext & Script.runInContext в node.js (узел использует привязку к движку V8, поэтому я не могу эмулировать их реализацию).

Вот несколько причин, почему я хочу изолировать выполнение кода:

  1. Изолировать код от глобального пространства имен (window объект, а также DOM), но мне, однако, нужно иметь возможность ссылаться на вызов функции для объектов, представленных в контексте, которые должны выполняться синхронно, что делает практически невозможным использование WebWorker для изоляции.
  2. Изолируя выполнение кода, можно было бы также иметь возможность освобождать его определения, когда они больше не нужны (управление памятью).

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

Мне нужно поделиться определением конструктора, а также определениями объекта, которые совместно используются изолированными контейнерами / контекстами, которые должны выполняться в основном потоке пользовательского интерфейса. В основном я хочу использовать эти изолированные контейнеры для размещения плагинов / модулей (мини-приложений), каждый из которых представляет и динамически обновляет область просмотра, вызывая команды рисования самостоятельно Context2D объект.

Если эти контейнеры не работают в главном потоке пользовательского интерфейса, будет очень трудно выполнить прокси-вызовы, такие как ctx.measureText() а также ctx.drawImage() было бы бесполезно, поскольку объекты изображения не могут быть созданы в Worker,

Кто-нибудь знает будущую спецификацию, которая сделает это возможным?

Существуют ли какие-либо текущие (скрытые) API-интерфейсы на стороне браузера, которые можно использовать для достижения этой цели?

Будет ли лучше использовать виртуальную машину, такую ​​как Goggle Dart VM, а также повторно реализовать мою текущую кодовую базу? Моя текущая кодовая база чуть выше 20 000 строк кода.

Было бы лучше повторно внедрить структуру в *

4 ответа

Ближайшая библиотека, которую я видел для этого, - Каха.

По сути, в нестрогом коде JavaScript есть много способов получить доступ к глобальному объекту (window в браузерах), делая настоящую изоляцию очень сложной проблемой. Каха делает несколько хитростей, чтобы исправить это, но, честно говоря, я не совсем уверен, как это работает.

Вы можете изолировать свой код от глобального пространства имен с помощью простого самозапускаемого функционального объекта:

(function() {
   // all your code goes here
   // nobody outside of your code can reach your top level variables here
   // your top level variables are not on the window object

   // this is a protected, but top level variable
   var x = 3;

   // if you want anything to be global, you can assign it to the window object.
   window.myGlobal = {};

   function myTopLevelFunction(x,y,z) {
       // code here
   }

})();

Если вы хотите иметь несколько из этих контекстов выполнения и иметь возможность обмениваться ими между собой, вам придется выполнять рандеву через одно общеизвестное местоположение, либо действительно глобальную переменную, либо свойство известного объекта DOM или что-то в этом роде. Относительно часто объявляют один объект глобального пространства имен и используют его свойства для любого доступа к вещам, которыми вы делитесь между модулями. Я знаю, что это не совсем идеально, но это работает. Вот пример рандеву с использованием единого объекта глобального пространства имен:

// module AAA
(function() {
   // module AAA code goes here

   // set up global namespace object and whatever references we want to be global
   window.myModuleTop = window.myModuleTop || {};
   myModuleTop.AAA = {};
   myModuleTop.AAA.myFuncA = function() {};

})();


// module BBB
(function() {
   // module BBB code goes here

   // set up global namespace object and whatever references we want to be global
   window.myModuleTop = window.myModuleTop || {};
   myModuleTop.BBB = {};
   myModuleTop.BBB.myFuncB = function() {};

})();

Не могли бы вы использовать замыкание, как и в других упомянутых ответах, а затем использовать теневой домен, чтобы гарантировать, что пользователь не сможет добраться до остального домена? Что-то вроде этого:

var containerNode = someDomNode
var root = containerNode.createShadowRoot()
;(function(root){
  var window = null, document = null, history = null,
      screen = null, navigator = null, location = null

  // isolated code goes here

})(root)

Предостережения:

  • Если вы создаете другие глобальные объекты вне контекста изолированного кода, вам нужно явно скрыть переменную, как я это делал с окном, документом и т. Д., Иначе изолированный код будет иметь к нему доступ.
  • Это не будет работать в браузерах, в которых явно нет теневого домена, если только ваш изолированный код вообще не нуждается в взаимодействии с ним.
  • Вы должны быть очень осторожны, чтобы объекты, к которым вы предоставляете доступ к изолированному коду, не содержали ссылок на вещи, к которым вы не хотите иметь доступ. Иногда это супер ошибка, склонная делать.
  • Я делаю это предложение, потому что вполне вероятно, что оно работает, но я понятия не имею, есть ли дополнительные способы добраться до таких вещей, как объекты окна и документа.

Является ли "стандартным" пространством имен вариант? Подобно:

var myNamespace = {};

myNamespace.myFunc = function() { return true; }

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

Есть предложение для Realms API, которое, похоже, решает аналогичные проблемы. Это все еще обсуждается, но для него уже есть полифилл - realms-shim.

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