Классы 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 не дойдут до широкой поддержки браузера, есть несколько способов:

  1. Используйте асинхронную библиотеку определений модулей для загрузки ваших модулей. Некоторые примеры: RequireJS, SystemJS, CommonJS

  2. Имейте единственную глобальную переменную, которую вы будете использовать для ссылки на объект, и сделайте ваши глобальные свойства приложения для этого объекта. Вот типичный способ сделать это:

    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...
            }
        }({});
    }
    

Это также дает вам удобную область (анонимную функцию), в которую можно помещать любые глобальные переменные уровня модуля.

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