typescript
GitHub
  • README
  • Давайте Почнемо
    • Why TypeScript
  • JavaScript
    • Equality
    • References
    • Null vs. Undefined
    • this
    • Closure
    • Number
    • Truthy
  • Future JavaScript Now
    • Classes
      • Classes Emit
    • Arrow Functions
    • Rest Parameters
    • let
    • const
    • Destructuring
    • Spread Operator
    • for...of
    • Iterators
    • Template Strings
    • Promise
    • Generators
    • Async Await
  • Проект / Project
    • Контекст компіляції / Compilation Context
      • tsconfig.json
      • Які файли / Which Files?
    • Простори Оголошень / Declaration Spaces
    • Модулі / Modules
      • File Module Details
      • global.d.ts
    • Namespaces
    • Dynamic Import Expressions
  • Node.js QuickStart
  • Browser QuickStart
  • Library QuickStart
  • TypeScript's Type System
    • JS Migration Guide
    • @types
    • Ambient Declarations
      • Declaration Files
      • Variables
    • Interfaces
    • Enums
    • lib.d.ts
    • Functions
    • Callable
    • Type Assertion
    • Freshness
    • Type Guard
    • Literal Types
    • Readonly
    • Generics
    • Type Inference
    • Type Compatibility
    • Never Type
    • Discriminated Unions
    • Index Signatures
    • Moving Types
    • Exception Handling
    • Mixins
  • JSX
    • React
    • Non React JSX
  • Options
    • noImplicitAny
    • strictNullChecks
  • Errors in TypeScript
    • Interpreting Errors
    • Common Errors
  • NPM
  • Testing
    • Jest
    • Cypress
  • Tools
    • Prettier
    • Husky
    • ESLint
    • Changelog
  • TIPs
    • String Based Enums
    • Nominal Typing
    • Stateful Functions
    • Currying
    • Type Instantiation
    • Lazy Object Literal Initialization
    • Classes are Useful
    • Avoid Export Default
    • Limit Property Setters
    • outFile caution
    • JQuery tips
    • static constructors
    • singleton pattern
    • Function parameters
    • Build Toggles
    • Barrel
    • Create Arrays
    • Typesafe Event Emitter
  • StyleGuide
  • TypeScript Compiler Internals
    • Program
    • AST
      • TIP: Visit Children
      • TIP: SyntaxKind enum
      • Trivia
    • Scanner
    • Parser
      • Parser Functions
    • Binder
      • Binder Functions
      • Binder Declarations
      • Binder Container
      • Binder SymbolTable
      • Binder Error Reporting
    • Checker
      • Checker Diagnostics
      • Checker Error Reporting
    • Emitter
      • Emitter Functions
      • Emitter SourceMaps
    • Contributing
Powered by GitBook
On this page
Edit on GitHub
  1. Future JavaScript Now
  2. Classes

Classes Emit

What's up with the IIFE

Що відбувається з IIFE. js код, згенерований для класу, міг бути таким:

function Point(x, y) {
    this.x = x;
    this.y = y;
}
Point.prototype.add = function (point) {
    return new Point(this.x + point.x, this.y + point.y);
};

Причина, по якій він загорнутий у функцію, що негайно викликається (Immediately-Invoked Function Expression - IIFE)

(function () {

    // BODY

    return Point;
})();

має відношення до спадщини. Це дозволяє TypeScript роботать з базовим класом як со змінною _super.

var Point3D = (function (_super) {
    __extends(Point3D, _super);
    function Point3D(x, y, z) {
        _super.call(this, x, y);
        this.z = z;
    }
    Point3D.prototype.add = function (point) {
        var point2D = _super.prototype.add.call(this, point);
        return new Point3D(point2D.x, point2D.y, this.z + point.z);
    };
    return Point3D;
})(Point);

Зверніть увагу, що IIFE дозволяє TypeScript легко фіксувати базовий клас Point у змінній _super яка постійно використовується в тілі класу.

__extends

Розширення Ви помітите, що як тільки ви успадкуєте клас, TypeScript також генерує таку функцію:

var __extends = this.__extends || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    __.prototype = b.prototype;
    d.prototype = new __();
};

Тут d відноситься до похідного класу, а b відноситься до базового класу. Ця функція робить дві речі:

  1. копіює статичні члени базового класу в дочірній клас, тобто for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];

  2. встановлює прототип дочірнього класу як посилання на батьківське proto тобто фактично d.prototype.__proto__ = b.prototype

Люди рідко мають проблеми з розумінням 1 пункту, але багатьом людям важко зрозуміти пункт 2. Тож пояснення є доречним.

d.prototype.__proto__ = b.prototype

Після того як я навчав багатьох людей цьому, я вважаю наступне пояснення найпростішим. Спочатку ми пояснимо, як код із __extends еквівалентний простому d.prototype.proto = b.prototype , а потім пояснимо, чому цей рядок сам по собі важливий. Щоб все це зрозуміти, потрібно знати такі речі:

  1. __proto__

  2. prototype

  3. вплив new на this всередині викликаної функції

  4. вплив new на prototype та __proto__

Усі об’єкти в JavaScript містять __proto__ . Цей елемент часто недоступний у старих браузерах (іноді в документації ця магічна властивість згадується як [[prototype]] ). Він має одну мету: якщо властивість не знайдено в об’єкті під час пошуку (наприклад, obj.property ), тоді вона шукається за obj.__proto__.property . Якщо його все ще не знайдено, тоді obj.__proto__.__proto__.property доки: його не буде знайдено , або останній .__proto__сам по собі буде нульовим . Це пояснює, чому кажуть, що JavaScript підтримує успадкування прототипів із коробки. Це показано в наступному прикладі, який можна запустити в консолі Chrome або Node.js:

var foo = {}

// setup on foo as well as foo.__proto__
foo.bar = 123;
foo.__proto__.bar = 456;

console.log(foo.bar); // 123
delete foo.bar; // remove from object
console.log(foo.bar); // 456
delete foo.__proto__.bar; // remove from foo.__proto__
console.log(foo.bar); // undefined

Круто, що ви зрозуміли __proto__ . Іншим корисним фактом є те, що всі function в JavaScript мають властивість під назвою prototype і що вони мають constructor- член , що вказує на функцію. Це показано нижче:

function Foo() { }
console.log(Foo.prototype); // {} i.e. it exists and is not undefined
console.log(Foo.prototype.constructor === Foo); // Has a member called `constructor` pointing back to the function

Тепер давайте розглянемо вплив new на this всередині викликаної функції . По суті, this всередині викликаної функції вказуватиме на щойно створений об’єкт, який буде повернено функцією. Легко побачити, якщо ви змінюєте властивість this всередині функції:

function Foo() {
    this.bar = 123;
}

// call with the new operator
var newFoo = new Foo();
console.log(newFoo.bar); // 123

Єдине, що вам потрібно знати, це те, що виклик new для функції призначає prototype функції __proto__ новоствореного об’єкта, який повертається викликом функції. Ось код, який ви можете запустити щоб повністю зрозуміти це:

function Foo() { }

var foo = new Foo();

console.log(foo.__proto__ === Foo.prototype); // True!

Тепер подивіться на наступне прямо з __extends . Я взяв на себе сміливість пронумерувати ці рядки:

1  function __() { this.constructor = d; }
2   __.prototype = b.prototype;
3   d.prototype = new __();

Читання цієї функції у зворотному порядку d.prototype = new __() у рядку 3 фактично означає d.prototype = {__proto__ : __.prototype} (через вплив new на prototype та __proto__ ), поєднуючи його з попереднім рядком ( тобто рядок 2 __.prototype = b.prototype; ) ви отримаєте d.prototype = {__proto__ : b.prototype} . Але зачекайте, ми хотіли d.prototype.__proto__ , тобто лише прото змінили та зберегли старий d.prototype.constructor . Ось де значення першого рядка (тобто function __() { this.constructor = d; } ) з’являється. Тут ми фактично матимемо d.prototype = {__proto__ : __.prototype, constructor : d} (оскільки вплив new на this всередині викликаної функції). Отже, оскільки ми відновлюємо d.prototype.constructor , єдине, що ми дійсно змінили, це __proto__, отже, d.prototype.__proto__ = b.prototype.

d.prototype.__proto__ = b.prototype significance

Значення d.prototype.__proto__ = b.prototype

Важливість полягає в тому, що він дозволяє вам додавати функції-члени до дочірнього класу та успадковувати інші від базового класу. Це демонструє такий простий приклад:

function Animal() { }
Animal.prototype.walk = function () { console.log('walk') };

function Bird() { }
Bird.prototype.__proto__ = Animal.prototype;
Bird.prototype.fly = function () { console.log('fly') };

var bird = new Bird();
bird.walk();
bird.fly();

Загалом bird.fly шукатиметься з bird.__proto__.fly (пам’ятайте, що new робить bird.__proto__ вказівним на Bird.prototype ), а bird.walk (успадкований член) шукатиметься з bird.__proto__.__proto__.walk (як bird.__proto__ == Bird.prototype і bird.__proto__.__proto__ == Animal.prototype).

PreviousClassesNextArrow Functions

Last updated 1 year ago