Yapamamam gerekirken neden TypeScript özel üyelerine erişebilirim?


108

TypeScript'te özel üyelerin uygulanmasına bakıyorum ve bunu biraz kafa karıştırıcı buluyorum. Intellisense, özel üyeye erişime izin vermez, ancak saf JavaScript'te, hepsi orada. Bu bana TS'nin özel üyeleri doğru şekilde uygulamadığını düşündürüyor. Düşüncesi olan var mı?

class Test{
  private member: any = "private member";
}
alert(new Test().member);

IntelliSense'in neden size alert () ile satırdaki özel üyeyi vermediğini merak ediyorsunuz?
Esrange

7
Hayır! Gerçekten derlediği JavaScript için değil, sadece intellisense için şeker iken TS'nin neden özel olduğunu merak ediyorum. Typescriptlang.org/Playground içinde çalıştırılan bu kod, özel üye değerini uyarır.
Sean Feldman

Belirtildiği gibi, öğeleri özel bir bağlamda bir değişken olarak tanımlamanız gerekir. Sanırım typcript bunu yapmaz çünkü prototipe eklenmek yerine verimsiz olabilir. Ayrıca tür tanımını da karıştırıyor (özel üyeler gerçekten sınıfın bir parçası değil)
Shane

Prototipte var olan gerçek özel değişkenler istiyorsanız, biraz ek yük gerektirir, ancak bunu GitHub'da yapan ClassJS adlı bir kitaplık yazdım: github.com/KthProg/ClassJS .
KthProg

Yanıtlar:


97

Tıpkı tür kontrolünde olduğu gibi, üyelerin gizliliği yalnızca derleyici içinde uygulanır.

Özel bir özellik, normal bir özellik olarak uygulanır ve sınıf dışındaki kodun ona erişmesine izin verilmez.

Sınıf içinde gerçekten özel bir şey yapmak için, sınıfın bir üyesi olamaz, nesneyi oluşturan kodun içinde bir işlev kapsamında oluşturulan yerel bir değişken olacaktır. Bu, ona sınıfın bir üyesi gibi, yani thisanahtar kelimeyi kullanarak erişemeyeceğiniz anlamına gelir .


25
Bir javascript programcısının bir nesne yapıcısına yerel bir değişken koyması ve bunu özel bir alan olarak kullanması alışılmadık bir durum değildir. Böyle bir şeyi desteklememelerine şaşırdım.
Eric

2
@Eric: TypeScript, oluşturucu içinde prototipler olarak yöntemler eklemek yerine yöntemler için prototip kullandığından, yapıcıdaki yerel bir değişkene yöntemlerden erişilemez. Sınıf için işlev sarmalayıcısının içinde yerel bir değişken oluşturmak mümkün olabilir, ancak bunu yapmanın bir yolunu henüz bulamadım. Bununla birlikte, bu yine de yerel bir değişken olacaktır ve özel bir üye değildir.
Guffa

40
Bu, hakkında geri bildirim sağladığım bir konu. Bir Açığa Çıkarma Modülü Modeli oluşturma seçeneği sunması gerektiğine inanıyorum, böylece özel üyeler gizli kalabilir ve genel olanlara JavaScript'te erişilebilir. Bu yaygın bir modeldir ve TS ve JS'de aynı erişilebilirliği sağlayacaktır.
John Papa

Özel statik üyeler için kullanabileceğiniz bir çözüm var: basarat.com/2013/03/real-private-static-class-members-in.html
basarat

1
@BasaratAli: Bu, sınıfın yöntemleri içinde bulunan statik bir değişkendir, ancak sınıfın bir üyesi değildir, yani thisanahtar sözcüğünü kullanarak ona erişemezsiniz .
Guffa

37

JavaScript, özel değişkenleri desteklemez.

function MyClass() {
    var myPrivateVar = 3;

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

TypeScript'te bu şu şekilde ifade edilir:

class MyClass {

    doSomething: () => number;

    constructor() {
        var myPrivateVar = 3;

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

DÜZENLE

Bu yaklaşım sadece kesinlikle gerekli olduğu yerlerde AYRICA kullanılmalıdır . Örneğin, bir parolayı geçici olarak önbelleğe almanız gerekiyorsa.

Bu kalıbı kullanmanın performans maliyetleri vardır (Javascript veya Typescript ile ilgisi yoktur) ve yalnızca kesinlikle gerekli olduğu yerlerde kullanılmalıdır.


Daktilo, bunu BÜTÜN zaman var _thiskapsamlı işlevlerde kullanım için ayarlayarak yapmıyor mu? Neden sınıf kapsamında bunu yapmaktan çekiniyorsunuz?
DrSammyD

Hayır. Var _bu sadece buna referanstır.
Martin

2
Onları özel değil, yapıcı değişkenler olarak adlandırmak daha doğrusu. Bunlar prototip yöntemlerde görünmez.
Roman M. Koss

1
oh evet, üzgünüm sorun bunun yerine başka bir soruydu, yarattığınız her örnek için doSomething yeniden yaratılacak, çünkü bu prototip zincirinin bir parçası değil.
Barbu Barbu

1
@BarbuBarbu Evet katılıyorum. Bu, bu yaklaşımla ilgili BÜYÜK bir konudur ve kaçınılması gereken nedenlerden biridir.
Martin

11

WeakMap desteği daha yaygın bir şekilde kullanılabilir olduğunda, burada örnek 3'te ayrıntılı olarak açıklanan ilginç bir teknik vardır .

Özel verilere izin verir VE verilere yalnızca örnek yöntemleri yerine prototip yöntemlerinden erişilmesine izin vererek Jason Evans örneğinin performans maliyetlerinden kaçınır.

Bağlantılı MDN WeakMap sayfası Chrome 36, Firefox 6.0, IE 11, Opera 23 ve Safari 7.1'de tarayıcı desteğini listeler.

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

Bunu sevdim! Temel olarak, özel mülkleri birleştirilmiş sınıfa gizlemek anlamına gelir. En eğlenceli olanı ... protectedParametreler için destek eklemeye ne dersiniz ? : D
Roman M. Koss

2
@RamtinSoltani Bağlantılı makale, zayıf haritaların nasıl çalıştığından dolayı çöp toplamayı engellemeyeceğini gösteriyor. Birisi bu tekniği kullanırken ekstra güvenli olmak isterse, sınıf örneği anahtarını zayıf haritaların her birinden silen kendi elden çıkarma kodunu uygulayabilir.
Ryan Thomas

1
MDN sayfasından: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… . Buna karşılık, yerel WeakMap'ler, anahtar nesnelere yönelik "zayıf" referansları tutar; bu, anahtar nesneye başka bir referans olmaması durumunda çöp toplamayı engellemeyecekleri anlamına gelir. Bu aynı zamanda haritadaki değerlerin gereksiz yere toplanmasının önlenmesini de önler.
Ryan Thomas

@RyanThomas Doğru, bu bir süre önce bıraktığım eski bir yorumdu. Haritalar'ın aksine WeakMap'ler bellek sızıntılarına neden olmaz. Yani bu tekniği kullanmak güvenlidir.
Ramtin Soltani

@RamtinSoltani Yani eski yorumunuzu
silinsin

10

TypeScript 3.8 yayınlanacağından, erişilemeyen ve hatta içeren sınıfın dışında tespit edilemeyen özel alanı bildirebileceksiniz .

class Person {
    #name: string

    constructor(name: string) {
        this.#name = name;
    }

    greet() {
        console.log(`Hello, my name is ${this.#name}!`);
    }
}

let jeremy = new Person("Jeremy Bearimy");

jeremy.#name
//     ~~~~~
// Property '#name' is not accessible outside class 'Person'
// because it has a private identifier.

Özel alanlar #karakterle başlar

Lütfen bu özel alanların privateanahtar kelime ile işaretlenmiş alanlardan farklı olacağını unutmayın.

Ref. https://devblogs.microsoft.com/typescript/announcing-typescript-3-8-beta/


4

Bu konudaki resmi tartışmaya bağlantı için Sean Feldman'a teşekkürler - bağlantı için cevabına bakın .

Bağlandığı tartışmayı okudum ve işte temel noktaların bir özeti:

  • Öneri: yapıcıdaki özel özellikler
    • sorunlar: prototip işlevlerinden erişilemiyor
  • Öneri: yapıcıda özel yöntemler
    • sorunlar: özelliklerde olduğu gibi, ayrıca prototipte sınıf başına bir işlev oluşturmanın performans avantajını kaybedersiniz; bunun yerine her örnek için işlevin bir kopyasını oluşturursunuz
  • Öneri: soyut mülk erişimine ortak metin ekleyin ve görünürlüğü zorunlu kılın
    • sorunlar: büyük performans ek yükü; TypeScript, büyük uygulamalar için tasarlanmıştır
  • Öneri: TypeScript, yapıcı ve prototip yöntem tanımlarını bir kapanışta zaten sarar; oraya özel yöntemler ve özellikler koyun
    • özel özelliklerin bu kapanışa yerleştirilmesiyle ilgili sorunlar: statik değişkenler haline gelirler; örnek başına bir tane yok
    • özel yöntemlerin bu kapanışa yerleştirilmesiyle ilgili sorunlar:this bir tür geçici çözüm olmadan erişimleri yok
  • Öneri: özel değişken adlarını otomatik olarak karıştırın
    • karşı argümanlar: bu bir adlandırma kuralı, bir dil yapısı değil. Kendin çöz
  • Öneri: Özel yöntemlere @private, ek açıklamanın yöntem adlarını etkili bir şekilde küçültebileceğini fark eden küçültme araçlarıyla açıklama ekleyin
    • Buna karşı önemli bir karşı argüman yok

Yayınlanan kodda görünürlük desteği eklemeye yönelik genel karşı argümanlar:

  • sorun JavaScript'in kendisinin görünürlük değiştiricilere sahip olmamasıdır - bu TypeScript'in sorunu değildir
  • JavaScript topluluğunda zaten yerleşik bir model var: özel mülkleri ve yöntemleri "kendi sorumluluğunuzdadır devam edin" yazan bir alt çizgi ile önek olarak ekleyin
  • TypeScript tasarımcıları gerçekten özel özelliklerin ve yöntemlerin "mümkün" olmadığını söylediklerinde, "tasarım kısıtlamalarımıza göre mümkün değil" demek istediler, özellikle:
    • Yayılan JS deyimseldir
    • Ortak metin minimumdur
    • Normal JS OOP ile karşılaştırıldığında ek yük yok

Bu yanıt bu sohbetten geldiyse : typescript.codeplex.com/discussions/397651 -, lütfen bir bağlantı sağlayın: D
Roman M. Koss

1
Evet, sohbet bu - ama Sean Feldman'ın bu soruya verdiği yanıta , bağlantıyı sağladığı yere bağladım. Bağlantıyı bulma işini yaptığı için, ona kredi vermek istedim.
alexanderbird

1

TypeScript'te Özel işlevlere yalnızca sınıfın içinden erişilebilir. Sevmek

görüntü açıklamasını buraya girin

Ve özel bir üyeye erişmeye çalıştığınızda bir hata gösterecektir. İşte örnek:

görüntü açıklamasını buraya girin

Not: JavaScript ile sorun olmaz ve her iki işleve dışarıdan erişilebilir.


4
OP: "ama saf JavaScript'te, hepsi orada" - Oluşturulan JavaScript'in "özel" işlevleri herkese açık olarak ortaya çıkarması sorununu ele aldığınızı sanmıyorum
alexanderbird

1
@alexanderbird TypeScript'in genellikle yeterli olduğunu söylemek istediğini düşünüyorum. TypeScript'te geliştirme yaparken, proje kapsamında kalıyoruz, bu nedenle JavaScript'ten gizlilik çok önemli değil. Çünkü her şeyden önce, orijinal kod geliştirici için önemlidir, kopyalanmış (JavaScript) kodlar değil.
Roman M. Koss

1
Bir JavaScript kitaplığı yazıp yayınlamıyorsanız, aktarılan kodun önemi yoktur
alexanderbird

cevabınız konu dışı.
canbax

1

Bunun daha eski bir tartışma olduğunun farkındayım, ancak, derlenmiş JavaScript sınıfının genel arayüzüne "sızan" bir TypeScript'teki sözde özel değişkenler ve yöntemler sorununa yönelik çözümümü paylaşmak yine de faydalı olabilir.

Bana göre bu sorun tamamen kozmetik, yani tamamen DevTools'ta bir örnek değişkeni görüntülendiğinde oluşan görsel karmaşa ile ilgili. Benim düzeltmem, özel bildirimleri, daha sonra ana sınıfta başlatılan ve private(ancak yine de JS'de genel olarak görünür olan) bir değişkene __(çift alt çizgi) gibi bir adla atanan başka bir sınıf içinde gruplamaktır .

Misal:

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

Ne zaman myClassörneği yerine tüm "özel" üyeleri bunları düzgünce daraltılmış içinde gruplandırılan (bkz çok görsel içinde dağınık düzgün gerçek yaşam kodunu refactored alabilirsiniz olan) gerçekten kamu olanlarla karışmış görme, DevTools'un görüntülendiğinde __mülkiyet:

görüntü açıklamasını buraya girin


1
Bunu sevdim. Temiz görünüyor.

0

Uygun özel mülkleri eklemek için yeniden kullanılabilir yaklaşım:

/**
 * Implements proper private properties.
 */
export class Private<K extends object, V> {

    private propMap = new WeakMap<K, V>();

    get(obj: K): V {
        return this.propMap.get(obj)!;
    }

    set(obj: K, val: V) {
        this.propMap.set(obj, val);
    }
}

Diyelim ki Clientiki özel mülke ihtiyaç duyan bir sınıfınız var :

  • prop1: string
  • prop2: number

Bunu nasıl uygulayacağınız aşağıda açıklanmıştır:

// our private properties:
interface ClientPrivate {
    prop1: string;
    prop2: number;
}

// private properties for all Client instances:
const pp = new Private<Client, ClientPrivate>();

class Client {
    constructor() {
        pp.set(this, {
            prop1: 'hello',
            prop2: 123
        });
    }

    someMethod() {
        const privateProps = pp.get(this);

        const prop1 = privateProps.prop1;
        const prop2 = privateProps.prop2;
    }
}

Ve ihtiyacınız olan tek şey tek bir özel mülkse, o zaman daha da basitleşir, çünkü ClientPrivatebu durumda herhangi birini tanımlamanıza gerek kalmaz .

Çoğunlukla, sınıfın Privatesadece güzelce okunabilir bir imza sunarken, doğrudan kullanımının WeakMapsağlamadığını belirtmek gerekir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.