Почему TypeScript упаковывает класс в IIFE?

Вот класс TypeScript:

class Greeter {
    public static what(): string {
        return "Greater";
    }

    public subject: string;

    constructor(subject: string) {
        this.subject = subject;
    }

    public greet(): string {
        return "Hello, " + this.subject;
    }
}

Он передается в IIFE, когда TS нацелен на ES5:

var Greeter = /** @class */ (function () {
    function Greeter(subject) {
        this.subject = subject;
    }
    Greeter.what = function () {
        return "Greater";
    };
    Greeter.prototype.greet = function () {
        return "Hello, " + this.subject;
    };
    return Greeter;
}());

Однако обычно он работает точно так же, когда он представлен в виде функции-конструктора. Что, конечно, больше похоже на JavaScript и рукописное :)

function Greeter(subject) {
    this.subject = subject;
}
Greeter.what = function () {
    return "Greater";
};
Greeter.prototype.greet = function () {
    return "Hello, " + this.subject;
};

Применение:

Оба блока кода работают одинаково:

Greater.what();  // -> "Greater"
var greater = new Greater("World!");
greater.greet(); // -> "Hello, World!

Какова польза или мотивы для упаковки в IIFE?

Я сделал наивный тест:

console.time("Greeter");
for(let i = 0; i < 100000000; i++) {
    new Greeter("world" + i);
}
console.timeEnd("Greeter");

Он показал практически такую ​​же скорость создания экземпляров. Конечно, мы не можем ожидать никакой разницы, потому что IIFE разрешается только один раз.

Я думал, что, может быть, это из-за закрытия, но IIFE не принимает аргументов. Это не должно быть замыканием.

@charlietfl ТС не использует IIFE для скрытия private полей, вообще-то. В JS они все общедоступны.

Aaron Beall 11.05.2019 06:54
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
17
1
658
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

// this is javascript code, not TypeScript

console.info(Greeter.What());

class Greeter {
}

Greeter.What = function What() {
    return "Greater";
}

С реализацией нативного класса это должно печатать ReferenceError: Greeter is not defined.

При транспиляции и заворачивании в IIFE результат достаточно близок: TypeError: Cannot read property 'What' of undefined.

Без IIFE развернутая функция — это поднятый, а имя Greeter находится в области видимости до того, как оно определено, поэтому возникает другая ошибка: TypeError: Greeter.What is not a function

Обратите внимание, что IIFE не используется для сокрытия частных свойств экземпляра или класса, поскольку в этом нет необходимости. При переносе свойства экземпляра назначаются как свойства для this внутри конструктора, а статические свойства назначаются как свойства объекта Greeter — переменные не создаются.

var не будет поднят

charlietfl 11.05.2019 03:42

Да, другой ответ правильный - вы должны иметь возможность ввести имя для ссылки на базовый класс, потому что, например, базовый класс может быть миксин. IIFE — это единственный способ безопасно ввести имя, которое не будет конфликтовать ни с чем во внешней области видимости.

artem 11.05.2019 04:20

Я думаю, что оба ответа верны в отношении разных вещей. Да, var не поднят, но function Greeter сам по себе (как показал ОП) будет, что, как я думал, было точкой зрения Артема. Также оцените тот факт, что TS фактически не использует это для частных полей.

Aaron Beall 11.05.2019 06:50
Ответ принят как подходящий

TypeScript будет передавать аргументы IIFE в случаях, когда между классами существует наследование. Например, замыкание ниже используется, когда Greeter расширяет класс BaseGreeter:

var Greeter = /** @class */ (function (_super) {
    // __extends is added by the TS transpiler to simulate inheritance
    __extends(Greeter, _super);
    function Greeter(subject) {
        var _this = _super.call(this) || this;
        _this.subject = subject;
        return _this;
    }
    Greeter.What = function () {
        return "Greater";
    };
    Greeter.prototype.greet = function () {
        return "Hello, " + this.subject;
    };
    return Greeter;
}(BaseGreeter));

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