ES6'ya Sembol getirme motivasyonu nedir?


368

GÜNCELLEME : Son zamanlarda Mozilla'dan parlak bir makale çıktı. Merak ediyorsanız okuyun.

Bildiğiniz gibi ECMAScript 6'ya yeni Symbol ilkel tipini eklemeyi planlıyorlar (diğer çılgın şeylerden bahsetmiyoruz). Her zaman :symbolRuby'deki kavramın gereksiz olduğunu düşündüm ; JavaScript'te yaptığımız gibi düz dizeleri kolayca kullanabiliriz. Ve şimdi JS'deki şeyleri bununla karmaşıklaştırmaya karar veriyorlar.

Motivasyonu anlamıyorum. Birisi bana JavaScript'te gerçekten sembollere ihtiyacımız olup olmadığını açıklayabilir mi?


6
Bu açıklamanın ne kadar gerçek olduğunu bilmiyorum, ama bu bir başlangıç: tc39wiki.calculist.org/es6/symbols .
Felix Kling

8
Semboller çok şey sağlar , nesneler üzerinde kapsamlı benzersiz tanımlayıcılara izin verir. Örneğin, yalnızca tek bir yerden erişilebilen nesnelerde özelliklere sahip olmak.
Benjamin Gruenbaum

5
Emin değilim yaklaşık sen Object.getOwnPropertySymbols (o) kullanabilirsiniz beri bu
Yanis

4
Gizlilikten ziyade benzersizdir.
Qantas 94 Heavy

2
Daha basit bir sınıf uygulaması için hendek açmaya karar verdikleri daha karmaşık bir sınıf uygulamasına privateve publicsınıf özellik anahtar kelimelerine sahip olacaklardı. this.x = xSizin yerine public x = xve özel değişkenler için yapmanız gerekiyordu private y = y. Bunu çok daha az sınıf uygulaması için terk etmeye karar verdiler. Daha sonra, minimal uygulamada özel mülkler elde etmek için sembol gerekli bir çözüm olacaktır.
lyschoening

Yanıtlar:


224

Javascript'e sembol tanıtmak için orijinal motivasyon, özel mülkleri etkinleştirmekti .

Ne yazık ki, ciddi şekilde indirgeniyorlardı. Bunları artık özel değiller, çünkü bunları yansıma, örneğin kullanma Object.getOwnPropertySymbolsveya proxy'ler aracılığıyla bulabilirsiniz .

Artık benzersiz semboller olarak biliniyorlar ve tek amaçlanan özellikleri, özellikler arasında isim çakışmalarını önlemek. Örneğin, ECMAScript'in kendisi artık, kullanıcı adlarıyla çakışma riski taşımadan nesnelere (örneğin yineleme protokollerini tanımlamak için) koyabileceğiniz belirli yöntemlerle uzantı kancaları tanıtabilir.

Bunun dile sembol eklemek için yeterince güçlü olup olmadığı tartışmalıdır.


93
Çoğu dil (tüm ana akım olanlar afaik), yine de özel olana erişmek için genellikle yansıma gibi bir mekanizma sağlar.
Esailija

19
@Esailija, bunun doğru olduğunu düşünmüyorum - özellikle, birçok dil ilk etapta yansıma sunmadığından. Yansıma yoluyla özel durum sızması (örneğin Java'da olduğu gibi) bir özellik olarak değil, bir hata olarak düşünülmelidir. Bu, özellikle güvenilir özel duruma sahip olmanın güvenlikle ilgili olabileceği web sayfalarında geçerlidir. Şu anda, JS'de bunu elde etmenin tek yolu hem sıkıcı hem de maliyetli olabilen kapaklardır.
Andreas Rossberg

38
Mekanizmanın yansıma olması gerekmez - C ++, Java, C #, Ruby, Python, PHP, Objective-C hepsi gerçekten isterse bir şekilde erişime izin verir. Bu gerçekten yetenek değil iletişim ile ilgili.
Esailija

4
@plalx, ​​web üzerinde, kapsülleme bazen güvenlikle de ilgilidir.
Andreas Rossberg

3
@RolandPihlakas maalesef Object.getOwnPropertySymbolstek sızıntı değil; daha zor olanı, "özel" bir mülke erişimi engellemek için proxy kullanma yeteneğidir.
Andreas Rossberg

95

Semboller gerçek gizliliği garanti etmez, ancak nesnelerin genel ve dahili özelliklerini ayırmak için kullanılabilir. SymbolÖzel mülkiyete sahip olmak için kullanabileceğimiz bir örnek verelim .

Bir nesnenin özelliğinin özel olmadığı bir örnek verelim.

var Pet = (function() {
  function Pet(type) {
    this.type = type;
  }
  Pet.prototype.getType = function() {
    return this.type;
  }
  return Pet;
}());

var a = new Pet('dog');
console.log(a.getType());//Output: dog
a.type = null;
//Modified outside
console.log(a.getType());//Output: null

Yukarıda, Petclass özelliği typeözel değildir. Özel hale getirmek için bir kapanış yaratmalıyız. Aşağıdaki örnek, typebir kapatma kullanarak nasıl özel hale getirebileceğimizi göstermektedir .

var Pet = (function() {
  function Pet(type) {
    this.getType = function(){
      return type;
    };
  }
  return Pet;
}());

var b = new Pet('dog');
console.log(b.getType());//dog
b.type = null;
//Stays private
console.log(b.getType());//dog

Yukarıdaki yaklaşımın dezavantajı: PetOluşturulan her örnek için performansa zarar verebilecek ekstra bir kapanış sunuyoruz .

Şimdi tanıtıyoruz Symbol. Bu, ekstra gereksiz kapatmalar kullanmadan bir mülkü özel yapmamıza yardımcı olabilir. Aşağıdaki kod örneği:

var Pet = (function() {
  var typeSymbol = Symbol('type');
  function Pet(type) {
    this[typeSymbol] = type;
  }
  Pet.prototype.getType = function(){
    return this[typeSymbol];
  }
  return Pet;
}());

var a = new Pet('dog');
console.log(a.getType());//Output: dog
a.type = null;
//Stays private
console.log(a.getType());//Output: dog

15
Sembol özelliklerinin özel olmadığına dikkat edin ! Semboller çarpışmasızdır . Kabul edilen cevabı okumak isteyebilirsiniz.
Bergi

3
Evet, sembol gerçek gizliliği garanti etmez, ancak nesnelerin genel ve dahili özelliklerini ayırmak için kullanılabilir. Üzgünüm, cevabıma bu noktayı eklemeyi unuttum. Cevabımı buna göre güncelleyeceğim.
Samar Panda

@SamarPanda, Ön ekli üyelerin _gerçek gizliliği garanti etmediğini, ancak nesnelerin genel ve dahili özelliklerini ayırmak için kullanılabileceğini söyleyebilirsiniz. Başka bir deyişle anlamsız cevap.
Pacerier

10
Anlamsız demem, çünkü semboller varsayılan olarak numaralandırılamaz, aynı zamanda 'hata' ile erişilemez, diğer herhangi bir anahtar olabilir.
Patrick

5
Cevabınızı, aslında normal bir özellik yerine nesnenin özel niteliğini neden bir Sembol olarak tanımlamak istediğinize dair mantıklı bir örneği olan tek cevap buluyorum.
Luis Lobo Borobia

42

Symbolsnesnelerde benzersiz bir özellik adı olarak kullanılabilen yeni, özel bir nesne türüdür. 'S Symbolyerine kullanmak string, farklı modüllerin birbiriyle çakışmayan özellikler oluşturmasına izin verir. Symbolsözelliklerine zaten doğrudan erişimi olmayan hiç kimse tarafından erişilememesi için özel yapılabilir Symbol.

Symbolsyeni bir ilkel . Tıpkı number, stringveboolean ilkel, Symbolbunları oluşturmak için kullanılabilecek bir işlevi var. Diğer ilkellerden farklı olarak, Symbolsgerçek bir sözdizimine sahip olmayın (örneğin, nasıl stringvar '') - bunları oluşturmanın tek yolu Symbolyapıcı ile şu şekilde:

let symbol = Symbol();

Gerçekte, Symbolbir nesneye özellikler eklemenin biraz farklı bir yoludur - iyi bilinen Symbolsstandart yöntemler olarak kolayca sağlanabilir , tıpkı Object.prototype.hasOwnPropertymiras alınan her şeyde olduğu gibi Object.

İşte Symbolilkel tipin bazı faydaları .

Symbols hata ayıklama yerleşik

Symbols bir konsola kaydederken hayatı biraz daha kolaylaştırmak için hata ayıklama için kullanılan bir açıklama verilebilir.

SymbolsObjectanahtar olarak kullanılabilir

Burası Symbolgerçekten ilginç oluyor. Nesnelerle iç içe geçmişlerdir. Symbolnesnelere anahtar olarak atanabilir, yani Symbolbir nesneye sınırsız sayıda benzersiz ' atayabilir ve bunların hiçbir zaman stringanahtarlarla veya başka bir benzersizle çakışmayacağı garanti edilebilir Symbols.

Symbols benzersiz bir değer olarak kullanılabilir.

Let Diyelim ki gibi birden günlük düzeylerini içeren bir günlük kütüphane, sahip varsayalım logger.levels.DEBUG, logger.levels.INFO, logger.levels.WARNvb. ES5 kodunda, bunları string(s logger.levels.DEBUG === 'debug') veya numbers ( logger.levels.DEBUG === 10) yapmak istersiniz . Her ikisi de ideal değildir, çünkü bu değerler benzersiz değerler değildir, ancak Symbols! Yani logger.levelsbasitçe şöyle olur:

log.levels = {
  DEBUG: Symbol('debug'),
  INFO: Symbol('info'),
  WARN: Symbol('warn'),
};
log(log.levels.DEBUG, 'debug message');
log(log.levels.INFO, 'info message');

Bu harika makalede daha fazlasını okuyun .


10
Eminim senin örneği anlamak değilim ve neden gerekir log.levels = {DEBUG: Symbol('debug')basitçe değil log.levels = {DEBUG:'debug'}. sonunda aynı. Bir nesnenin anahtarları üzerinde yineleme yaparken Sembollerin görünmez olduğunu belirtmek gerekir. bu onların "şey"
vsync

Bir faydası, birisinin yanlışlıkla bir değişmez kelime kullanamaması ve sonsuza dek çalışacağına inanmasıdır. (Bunun gerçekten güçlü bir argüman olmadığını unutmayın, çünkü sadece {}aynı sonucu (benzersiz değer olarak) kullanabilir ve elde edebilir ya da belki de bu projede değişmez bir kelime tercih edilir ya da önce dokümanı okumanız gerektiğini söyleyebilirsiniz.) I Şahsen kod benzersiz bir anlamı iyi bir okunabilirlik sağlamak düşünüyorum
apple apple

benzersiz değer olarak kullanıldığında, nesne değişmezi yerleşik olarak hata ayıklama özelliğine sahiptir , yani birden çok alan ekleyebileceğiniz için nesne burada daha iyi Symbol("some message")olur {message:'some message'}.
elma elması

38

Bu yazı hakkında Symbol() bulabildiğim / yapabileceğim gerçek örnekler ve bulabildiğim gerçekler ve tanımlar ile ilgili.

TLDR;

Symbol()ECMAScript'te 6 (ES6) ile birlikte tanıtılan bir veri türü vardır.

Sembol hakkında iki ilginç gerçek var.

  • ilk veri türü ve yalnızca JavaScript'te değişmez değeri olmayan veri türü

  • ile tanımlanan herhangi bir değişken Symbol()benzersiz içerik alır, ancak gerçekten özel değildir .

  • herhangi bir verinin kendi Sembolü vardır ve aynı veriler için Semboller aynı olacaktır . Aşağıdaki paragrafta daha fazla bilgi, aksi takdirde bir TLRD değildir; :)

Sembolü nasıl başlatabilirim?

1. Hata ayıklanabilir bir değere sahip benzersiz bir tanımlayıcı almak için

Bunu şu şekilde yapabilirsiniz:

var mySymbol1 = Symbol();

Veya bu şekilde:

var mySymbol2 = Symbol("some text here");

"some text here"Dize sembolü çıkarılan edilemez, bu hata ayıklama amacıyla sadece açıklaması bulunmaktadır. Sembolün davranışını hiçbir şekilde değiştirmez. Her ne kadar bunu yapabilirsiniz console.log(bu, adil, çünkü değer günlüğe kaydetme içindir, bu günlüğü başka bir günlük girişi ile karıştırmamak için):

console.log(mySymbol2);
// Symbol(some text here)

2. Bazı dize verileri için bir sembol elde etmek

Bu durumda, sembolün değeri aslında dikkate alınır ve bu şekilde iki sembol benzersiz olmayabilir.

var a1 = Symbol.for("test");
var a2 = Symbol.for("test");
console.log(a1 == a2); //true!

Bu sembollere "ikinci tip" sembolleri diyelim. Hiçbir şekilde "birinci tip" sembollerle (yani tanımlanmış olanlarla Symbol(data)) kesişmezler .

Sonraki iki paragraf yalnızca ilk tür sembolüyle ilgilidir.

Eski veri türleri yerine Symbol kullanmaktan nasıl yararlanabilirim?

Önce bir nesneyi, standart bir veri türünü ele alalım. Orada bazı anahtar / değer çiftleri tanımlayabilir ve anahtarı belirterek değerlere erişebiliriz.

var persons = {"peter":"pan","jon":"doe"};
console.log(persons.peter);
// pan

Ya Peter adında iki kişimiz varsa?

Bunu yapmak:

var persons = {"peter":"first", "peter":"pan"};

pek mantıklı değil.

Yani, aynı ada sahip iki farklı kişinin sorunu olduğu görülüyor. O zaman yeniye bakalım Symbol(). Gerçek hayatta bir insan gibi - herhangi bir kişi benzersizdir , ancak adları eşit olabilir. İki "kişi" tanımlayalım.

 var a = Symbol("peter");
 var b = Symbol("peter");

Şimdi aynı isimli iki farklı kişimiz var. Kişilerimiz gerçekten farklı mı? Onlar; bunu kontrol edebilirsiniz:

 console.log(a == b);
 // false

Orada nasıl faydalanırız?

Nesnenizde farklı kişiler için iki giriş yapabiliriz ve hiçbir şekilde karıştırılamazlar.

 var firstPerson = Symbol("peter");
 var secondPerson = Symbol("peter");
 var persons = {[firstPerson]:"first", [secondPerson]:"pan"};

Not:
Bununla birlikte, nesneyi dizmenin JSON.stringifybir Sembol olarak başlatılan tüm çiftleri anahtar olarak bırakacağına dikkat etmek gerekir.
Yürütme Object.keysde bu Symbol()->valueçiftleri döndürmez .

Bu başlatmayı kullanarak, birinci ve ikinci kişilerin girişlerini yanlış yapmak kesinlikle imkansızdır. console.logOnları aramak , ikinci adlarını doğru şekilde verecektir.

 console.log(persons[a]);
 // first
 console.log(persons[b]);
 // pan

Nesnede kullanıldığında, numaralandırılamayan özelliklerin tanımlanmasına kıyasla farkı nedir?

Gerçekten de, gizlenecek bir mülkü Object.keysve numaralandırmayı tanımlamanın bir yolu zaten vardı . İşte burada:

var anObject = {};
var fruit = "apple";    

Object.defineProperty( anObject, fruit, {
    enumerable: false,
    value: "green"
});

Ne fark eder Symbol()ki? Aradaki fark, ile tanımlanan özelliği Object.definePropertynormal şekilde elde edebilmenizdir:

console.log(anObject[fruit]); //green
console.log(anObject["apple"]); //green
console.log(anObject.apple); //green

Ve önceki paragrafta olduğu gibi Symbol ile tanımlanırsa:

fruit = Symbol("apple");

Değerini yalnızca değişkenini biliyorsanız, yani

console.log(anObject[fruit]); //green
console.log(anObject["apple"]); //undefined
console.log(anObject.apple); //undefined

Dahası, anahtarın altında başka bir özellik tanımlamak "apple", nesnenin eskisini düşürmesini sağlar (ve sabit kodlanmışsa bir hata verebilir). Artık elma yok! Ne yazık. Önceki paragrafa bakıldığında, Semboller benzersizdir ve bir anahtarı Symbol()benzersiz hale getirecek şekilde tanımlar .

Tür dönüştürme ve denetleme

  • Diğer veri türlerinin aksine, Symbol()herhangi bir veri türüne dönüştürmek imkansızdır .

  • İlkel veri türüne dayalı bir sembolü arayarak "yapmak" mümkündür Symbol(data).

  • Türü kontrol etmek açısından hiçbir şey değişmez.

    function isSymbol ( variable ) {
        return typeof someSymbol === "symbol";
    }
    
    var a_Symbol = Symbol("hey!");
    var totally_Not_A_Symbol = "hey";
    
    console.log(isSymbol(a_Symbol)); //true
    console.log(isSymbol(totally_Not_A_Symbol)); //false


Bu SO Dokümantasyonundan mı taşındı?
Knu

1
@KNU değildi; Ben bilgi topladım ve bu yanıtı kendim yazdım
nicael

Gerçekten güzel cevap!
Mihai Alexandru-Ionut

1
Sembol üzerinde büyük cevap, ancak yine de neden bir dizi yerine sembol tuşları ile nesne kullanmak istiyorum bilmiyorum. {"Peter": "pan"} {"john": "doe"} gibi birden fazla kişi varsa, bunları tek bir nesneye koymak benim için kötü hissettiriyor. PersonFirstName1, personFirstName2 gibi yinelenen özelliklere sahip sınıflar yapmıyorum aynı nedenle. Bu onu dize edememe ile birlikte yararları sadece dezavantajları görmüyorum.
Eldo

18

İşte böyle görüyorum. Semboller, bir nesnenin anahtarlarının / özelliklerinin Object.keys () ve JSON.stringify () gibi bazı popüler yöntemlerle gösterilmesini engelleyerek 'ekstra bir gizlilik düzeyi' sağlar.

var age = Symbol();  // declared in another module perhaps?
class Person {
   constructor(n,a){
      this.name = n;
      this[age] = a;  
   }
   introduce(){
       console.log(`My name is ${this.name}. I am ${this[age]-10}.`);
   }
}
var j = new Person('Jane',45);
j.introduce();  // My name is Jane. I am 35.
console.log(JSON.stringify(j)); // {"name":"Jane"}
console.log(Object.keys(j)); // ["name"]
console.log(j[age]); // 45   (well…only if you know the age in the first place…)

Kendi başına bir nesne verilmesine rağmen, bu özellikler hala yansıma, proxy, Object.getOwnPropertySymbols () vb. Yoluyla açığa çıkarılabilir, ancak bazen bir OOP perspektifinden yeterli olabilecek birkaç doğrudan yöntemle bunlara erişmek için doğal bir yol yoktur.


2

JS sembolü yeni bir ilkel veri türüdür. Bunlar, benzersiz kimlikler olarak kullanılan jetonlardır . SymbolYapıcı kullanılarak bir sembol oluşturulabilir . Örneğin bu snippet'i MDN'den alın:

// The symbol constructor takes one optional argument, 
// the descriptions which is used for debugging only.
// Here are two symbols with the same description
let Sym1 = Symbol("Sym");
let Sym2 = Symbol("Sym");
  
console.log(Sym1 == Sym2); // returns "false"
// Symbols are guaranteed to be unique.
// Even if we create many symbols with the same description,
// they are different values.

Sembolleri benzersiz nesne özellik anahtarları olarak kullanmak genellikle kullanışlıdır, örneğin:

let obj = {};
let prop = Symbol();

obj[prop] = 123;  // the symbol prop is assigned 123
obj.prop  = 456;  // the string prop is assigned 456

console.log(obj.prop, obj[prop]); // logs 456, 123


0

Sembollerin iki ana kullanım durumu vardır:

  1. “Gizli” nesne özellikleri. Başka bir komut dosyasına veya kitaplığa “ait” bir nesneye özellik eklemek istiyorsak, bir sembol oluşturabilir ve onu özellik anahtarı olarak kullanabiliriz. Sembolik bir özellikfor..in , bu nedenle yanlışlıkla diğer özelliklerle birlikte işlenmez. Ayrıca doğrudan erişilemez, çünkü başka bir komut dosyasının sembolümüz yok. Böylece mülk yanlışlıkla kullanılmayacak veya üzerine yazılmayacak.

    Böylece, bir şeyi ihtiyacımız olan nesnelere “gizlice” gizleyebiliriz, ancak diğerleri sembolik özellikleri kullanarak görmemelidir.

  2. JavaScript tarafından kullanılan ve erişilebilen birçok sistem sembolü vardır Symbol.*. Bunları bazı yerleşik davranışları değiştirmek için kullanabiliriz. Örneğin, ...... Symbol.iteratoryinelemeler için, Symbol.toPrimitivenesneden ilkeye dönüşüm ayarlamak vb.

Kaynak

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.