Vyhledávání na webu

javascript - Proč TypeScript zabalí třídu do IIFE?


Zde je třída 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;
    }
}

Při cílení TS na ES5 je to přeloženo do IIFE:

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

Nicméně obecně funguje stejným způsobem, když je prezentováno jako konstruktorová funkce. Což samozřejmě vypadá více JavaScriptově a ručně psané :)

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

Použití :

Oba bloky kódu fungují stejným způsobem:

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

Jaký je přínos nebo motivace zabalit to do IIFE?

Udělal jsem naivní benchmark:

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

Ukázalo to prakticky stejnou rychlost instanciace. Samozřejmě nemůžeme očekávat žádný rozdíl, protože IIFE je vyřešeno pouze jednou.

Myslel jsem si, že to možná je kvůli closure, ale IIFE nepřijímá argumenty. Nemusí to být closure.

Odpověď č. 1

TypeScript předá argumenty do IIFE v případech, kdy existuje dědičnost mezi třídami. Například níže uvedená closure se používá, když Greeter rozšiřuje třídu 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));

Odpověď č. 2

Provádí se to k zachování nativního chování třídy v okrajových případech, jako je tento, kdy se někdo pokusí použít třídu Greeter dříve, než je definována:

// this is javascript code, not TypeScript

console.log(Greeter.What());

class Greeter {
}

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

S implementací nativní třídy by toto mělo vypsat ReferenceError: Greeter is not defined.

Když je přeloženo a zabalené do IIFE, výsledek je dostatečně blízký: TypeError: Nelze číst vlastnost 'What' z undefined.

Bez IIFE je neobalená funkce zvednuta a jméno Greeter je v rozsahu před jejím definováním, takže je vyprodukována jiná chyba: TypeError: Greeter.What is not a function

Všimněte si, že IIFE se nepoužívá k ukrytí soukromých vlastností instance nebo třídy, protože to stejně není nutné. Po transpilaci jsou vlastnosti instance přiřazeny jako vlastnosti pro this uvnitř konstruktoru a statické vlastnosti jsou přiřazeny jako vlastnosti objektu Greeter - žádné proměnné nejsou vytvářeny.

Související články