Классы ES6: как насчет инстроспекции?
В ES5 я мог проверить существование "класса" (функции конструктора) в объекте окна:
if (window.MyClass) {
... // do something
}
В ES6, согласно этой статье, глобально объявленные классы являются глобальными, а не свойствами глобального объекта (window
в браузерах):
Но теперь есть и глобальные переменные, которые не являются свойствами глобального объекта. В глобальной области видимости следующие переменные создают следующие переменные:
let
декларацииconst
декларации- Объявления класса
Так что, если я не могу использовать if (window.MyClass)
Есть ли способ сделать то же самое?
На самом деле есть ли правильный способ сделать это без использования оконного объекта?
1 ответ
В ES5 мы могли бы проверить существование класса в объекте окна
Только если функция конструктора была глобальной, что является плохой практикой.
В ES6, согласно этой статье, глобально объявленные классы являются глобальными, а не свойствами глобального объекта...
Правильный. (То же самое относится и к let
а также const
объявления в глобальной области видимости.) Это определено в §8.1.1.4: Глобальные записи среды:
Глобальная запись среды логически представляет собой одну запись, но она указывается как составная инкапсуляция записи среды объекта и декларативной записи среды. Объектная запись среды имеет в качестве базового объекта глобальный объект связанной области. Этот глобальный объект является значением, возвращаемым конкретным методом GetThisBinding глобальной записи среды. (Например, глобальный объект, на который ссылается
window
в браузерах - TJ) Компонент Object Environment Record глобальной записи Environment содержит привязки для всех встроенных глобальных переменных (раздел 18) и все привязки, представленные FunctionDeclaration, GeneratorDeclaration или VariableStatement, содержащимися в глобальном коде. Привязки для всех других объявлений ECMAScript в глобальном коде содержатся в декларативном компоненте записи среды глобальной записи среды.
(Мой акцент) Итак, вещи, которые раньше использовались в глобальном объекте в ES5 и более ранних версиях, все еще работают (плюс генераторы, потому что это было бы еще более запутанным, если бы они этого не сделали), но новые вещи (let
, const
, а также class
декларации) нет. Они глобальные, но не свойства глобального объекта.
Вернуться к вашему вопросу...
Так что, если я не могу использовать
if (window.MyClass)
Есть ли способ сделать то же самое?
Вы могли бы использовать
if (typeof MyClass === "function") {
...поскольку typeof
на неразрешимый символ не бросает ReferenceError
, Это также имеет преимущество проверки MyClass
находится в области действия кода, даже если он не глобален.
Там есть ошибка, хотя: если этот код находится в той же области, где MyClass
объявлен через class
(или же let
или же const
) но это выше MyClass
в этой области, даже typeof
чек бросит ReferenceError
потому что вы не можете получить доступ к привязке, которую он создает вообще (даже с typeof
) перед class
(или же let
или же const
).
Например, это бросит:
if (typeof MyClass === "function") { // ReferenceError here
// Yup, it's defined
// ...
}
// ...
class MyClass {
}
Пространство от начала области до class
, let
, или же const
линия называется временной мертвой зоной (TDZ), и вы вообще не можете получить доступ к привязке переменной. Следовательно, вы должны поймать ReferenceError
:
let exists = false;
try {
exists = typeof MyClass === "function";
} catch (e) {
}
На самом деле есть ли правильный способ сделать это без использования оконного объекта?
Пока модули JavaScript не дойдут до широкой поддержки браузера, есть несколько способов:
Используйте асинхронную библиотеку определений модулей для загрузки ваших модулей. Некоторые примеры: RequireJS, SystemJS, CommonJS
Имейте единственную глобальную переменную, которую вы будете использовать для ссылки на объект, и сделайте ваши глобальные свойства приложения для этого объекта. Вот типичный способ сделать это:
var MyApp = MyApp || {}; if (!MyApp.ThisModule) { // You can leave this `if` out // if there's no chance of the file // being loaded more than once MyApp.ThisModule = function(module) { module.MyClass = class MyClass { // ...class definition here... } }({}); }
Это также дает вам удобную область (анонимную функцию), в которую можно помещать любые глобальные переменные уровня модуля.