Изолировать исполнение JavaScript
Одним из недостатков JS, которое меня больше всего беспокоит, является плохая способность изолировать выполнение кода.
Я хочу иметь возможность контролировать контекст, в котором выполняется код, что-то такое, что дает эффект, аналогичный тому, что Script.createContext
& Script.runInContext
в node.js (узел использует привязку к движку V8, поэтому я не могу эмулировать их реализацию).
Вот несколько причин, почему я хочу изолировать выполнение кода:
- Изолировать код от глобального пространства имен (
window
объект, а такжеDOM
), но мне, однако, нужно иметь возможность ссылаться на вызов функции для объектов, представленных в контексте, которые должны выполняться синхронно, что делает практически невозможным использованиеWebWorker
для изоляции. - Изолируя выполнение кода, можно было бы также иметь возможность освобождать его определения, когда они больше не нужны (управление памятью).
Я знаю, что можно добиться частично изолированного выполнения, загрузив скрипт в 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.