Вопрос по typescript, javascript – Почему я могу получить доступ к закрытым членам TypeScript, если не могу?

89

Я смотрю на реализацию закрытых членов в TypeScript, и я нахожу это немного запутанным. Intellisense не позволяет получить доступ к закрытому члену, но в чистом JavaScript это все есть. Это заставляет меня думать, что TS неправильно внедряет частных участников. Какие-нибудь мысли?

class Test{
  private member: any = "private member";
}
alert(new Test().member);
Вы задаетесь вопросом, почему IntelliSense не предоставляет вам частного участника в строке с функцией alert ()? esrange
Если вам нужны реальные частные переменные, которые существуют в прототипе, это потребует некоторых накладных расходов, но я написал библиотеку под названием ClassJS, которая делает именно это на GitHub:github.com/KthProg/ClassJS. KthProg
Как уже упоминалось, вы должны объявить элементы как переменные в частном контексте, чтобы они были приватными. Я предполагаю, что машинопись не делает этого, потому что она может быть неэффективна против добавления в прототип. Это также портит определение типа (закрытые члены не являются частью класса) Shane
Нету. Мне интересно, почему у TS есть private, когда это только сахар для intellisense, а не для JavaScript, с которым он компилируется. Этот код выполняется вtypescriptlang.org/Playground предупреждает значение частного члена. Sean Feldman

Ваш Ответ

6   ответов
1

Я понимаю, что это более старая дискуссия, но все же было бы полезно поделиться своим решением проблемы предположительно закрытых переменных и методов в TypeScript & quot; leaking & quot; в открытый интерфейс скомпилированного класса JavaScript.

Для меня эта проблема является чисто косметической, то есть все дело в визуальном беспорядке, когда переменная экземпляра просматривается в DevTools. Мое исправление состоит в том, чтобы сгруппировать частные объявления вместе в другом классе, который затем создается в главном классе и назначаетсяprivate (но все еще публично видимый в JS) переменная с именем как__ (двойное подчеркивание).

Пример:

class Privates {
    readonly DEFAULT_MULTIPLIER = 2;
    foo: number;
    bar: number;

    someMethod = (multiplier: number = this.DEFAULT_MULTIPLIER) => {
        return multiplier * (this.foo + this.bar);
    }

    private _class: MyClass;

    constructor(_class: MyClass) {
        this._class = _class;
    }
}

export class MyClass {
    private __: Privates = new Privates(this);

    constructor(foo: number, bar: number, baz: number) {
        // assign private property values...
        this.__.foo = foo;
        this.__.bar = bar;

        // assign public property values...
        this.baz = baz;
    }

    baz: number;

    print = () => {
        console.log(`foo=${this.__.foo}, bar=${this.__.bar}`);
        console.log(`someMethod returns ${this.__.someMethod()}`);
    }
}

let myClass = new MyClass(1, 2, 3);

КогдаmyClass Экземпляр просматривается в DevTools, вместо того, чтобы видеть все его "частные" члены, смешанные с действительно общедоступными (которые могут быть очень визуально запутанными в правильно переработанном реальном коде), вы видите их аккуратно сгруппированными внутри свернутого__ имущество:

enter image description here

Мне это нравится. Выглядит чисто
35

JavaScript поддерживает частные переменные.

function MyClass() {
    var myPrivateVar = 3;

    this.doSomething = function() {
        return myPrivateVar++;        
    }
}

В TypeScript это будет выражаться так:

class MyClass {

    doSomething: () => number;

    constructor() {
        var myPrivateVar = 3;

        this.doSomething = function () {
            return myPrivateVar++;
        }
    }
}

EDIT

Этот подход должен использоваться толькоSPARINGLY где это абсолютно необходимо. Например, если вам нужно временно кешировать пароль.

Использование этого шаблона сопряжено с затратами на производительность (не имеет отношения к Javascript или Typescript) и должно использоваться только в случае крайней необходимости.

Нет. Это просто ссылка на это.
Точнее называть их переменными конструктора, а не частными. Это не видно в методах прототипа.
Разве машинопись не делает это ВСЕ время, устанавливаяvar _this для использования в ограниченных функциях? Зачем вам делать это в классе?
Можете ли вы добавить ссылку на стоимость производительности? Имхо, это будет только потеря производительности, когда вы инициализируете огромное количество объектов из этого класса.
@BarbuBarbu Да, я согласен. Это БОЛЬШАЯ проблема с этим подходом, и одной из причин этого следует избегать.
12

Однажды поддержкаWeakMap более широко доступен, есть интересная методика, подробно описанная в примере № 3Вот.

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

На связанной странице MDN WeakMap приведена поддержка браузеров в Chrome 36, Firefox 6.0, IE 11, Opera 23 и Safari 7.1.

let _counter = new WeakMap();
let _action = new WeakMap();
class Countdown {
  constructor(counter, action) {
    _counter.set(this, counter);
    _action.set(this, action);
  }
  decrement() {
    let counter = _counter.get(this);
    if (counter < 1) return;
    counter--;
    _counter.set(this, counter);
    if (counter === 0) {
      _action.get(this)();
    }
  }
}
Мне понравилось! По сути это означает скрытие частных свойств в агрегированном классе. Самое интересное будет ... Как насчет добавления поддержкиprotectedпараметры? : D
@RyanThomas Правда, это был старый комментарий, который я оставил некоторое время назад. WeakMaps, в отличие от Maps, не вызовет утечек памяти. Так что использовать эту технику безопасно.
@RamtinSoltani В статье по ссылкам указывается, что из-за того, как работают слабые карты, это не предотвратит сборку мусора. Если кто-то хотел быть более безопасным при использовании этого метода, он мог бы реализовать свой собственный код удаления, который удаляет ключ экземпляра класса из каждой слабой карты.
Это предотвратит сборку мусора экземплярами класса, так как их ссылка хранится в WeakMaps, и все переменные в них также никогда не будут удалены. Это стоит памяти!
Со страницы MDN:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…, Напротив, нативные WeakMaps держат «слабый» ссылки на ключевые объекты, что означает, что они не предотвращают сборку мусора в случае, если не будет другой ссылки на ключевой объект. Это также позволяет избежать сбора мусора значений на карте.
3

Спасибо Шону Фельдману за ссылку на официальное обсуждение этого вопроса - см.его ответ по ссылке.

Я прочитал дискуссию, с которой он связался, иhere's a summary of the key points:

  • Suggestion: private properties in constructor
    • problems: can't access from prototype functions
  • Suggestion: private methods in constructor
    • problems: same as with properties, plus you lose the performance benefit of creating a function once per class in the prototype; instead you create a copy of the function for each instance
  • Suggestion: add boilerplate to abstract property access and enforce visibility
    • problems: major performance overhead; TypeScript is designed for large applications
  • Suggestion: TypeScript already wraps the constructor and prototype method definitions in a closure; put private methods and properties there
    • problems with putting private properties in that closure: they become static variables; there is not one per instance
    • problems with putting private methods in that closure: they do not have access to this without some sort of workaround
  • Suggestion: automatically mangle the private variable names
    • counter arguments: that's a naming convention, not a language construct. Mangle it yourself
  • Suggestion: Annotate private methods with @private so minifiers that recognize that annotation can effectively minify the method names
    • No significant counter arguments to this one

Overall counter-arguments to adding visibility support in emitted code:

  • the problem is that JavaScript itself doesn't have visibility modifiers - this isn't TypeScript's problem
  • there is already an established pattern in the JavaScript community: prefix private properties and methods with an underscore, which says "proceed at your own risk"
  • when TypeScript designers said that truly private properties and methods aren't "possible", they meant "not possible under our design constraints", specifically:
    • The emitted JS is idiomatic
    • Boilerplate is minimal
    • No additional overhead compared to normal JS OOP
Если этот ответ был из этого разговора:typescript.codeplex.com/discussions/397651 -, пожалуйста, предоставьте ссылку: D
Да, это разговор, но я связался сSean Feldman's answer to this questionгде он предоставляет ссылку. Поскольку он занимался поиском ссылки, я хотел отдать ему должное.
83

Как и при проверке типов, конфиденциальность членов обеспечивается только внутри компилятора.

Закрытое свойство реализовано как обычное свойство, и код вне класса не имеет доступа к нему.

Чтобы сделать что-то действительно закрытое внутри класса, оно не может быть членом класса, это была бы локальная переменная, созданная внутри области действия функции внутри кода, который создает объект. Это будет означать, что вы не можете получить к нему доступ как член класса, то есть с помощьюthis ключевое слово.

Это то, что я предоставляю обратную связь. Я полагаю, что он должен предложить возможность создания шаблона модуля раскрытия, чтобы частные члены могли оставаться частными, а публичные - доступными в JavaScript. Это общий шаблон, который обеспечивает одинаковую доступность в TS и JS.
Для программиста javascript весьма обычно помещать локальную переменную в конструктор объекта и использовать ее в качестве частного поля. Я удивлен, что они не поддерживали что-то подобное.
Существует решение, которое вы можете использовать для частных статических членов:basarat.com/2013/03/real-private-static-class-members-in.html
@BasaratAli: Это статическая переменная, которая доступна внутри методов класса, но она не является членом класса, т.е. вы не обращаетесь к ней с помощьюthis ключевое слово.
@Eric: поскольку TypeScript использует прототип для методов вместо добавления методов в качестве прототипов внутри конструктора, локальная переменная в конструкторе недоступна из методов. Возможно, можно создать локальную переменную внутри функции-обертки для класса, но я пока не нашел способа сделать это. Однако это все равно будет локальная переменная, а не частный член.
2

В TypeScript закрытые функции доступны только внутри класса. подобно

enter image description here

И это покажет ошибку при попытке доступа к приватному члену. Вот пример:

enter image description here

Note: It will be fine with javascript and both function are accessible outside.

@alexanderbird Я думаю, он хотел сказать, что обычно достаточно TypeScript. Когда мы разрабатываем на языке TypeScript, мы остаемся с ним в рамках проекта, поэтому конфиденциальность от JavaScript не имеет большого значения. Потому что, во-первых, оригинальный код имеет значение для разработчика, а не для транспаранта (JavaScript).
Если вы не пишете и не публикуете библиотеку JavaScript, переносимый код имеет значение
OP: "но в чистом JavaScript это все" - Я не думаю, что вы решаете проблему, связанную с тем, что сгенерированный JavaScript предоставляет "частный" функционирует публично

Похожие вопросы