JavaScript'te 'a' ['toUpperCase'] () nasıl ve neden çalışır?


209

JavaScript beni şaşırtmaya devam ediyor ve bu başka bir örnek. Sadece ilk başta anlamadığım bazı kodlarla karşılaştım. Bu yüzden hata ayıkladım ve bu bulguya geldim:

alert('a'['toUpperCase']());  //alerts 'A'

toUpperCase()Dize türünün bir üyesi olarak tanımlanmışsa, bu açık olmalıdır , ancak başlangıçta bana mantıklı gelmedi.

Neyse,

  • toUpperCase'a' üyesi olduğu için bu işe yarıyor mu? Yoksa perde arkasında başka bir şey mi oluyor?
  • Kod aşağıdaki gibi okuyordum bir işlevi vardır:

    function callMethod(method) {
        return function (obj) {
            return obj[method](); //**how can I be sure method will always be a member of obj**
        }
    }
    
    var caps2 = map(['a', 'b', 'c'], callMethod('toUpperCase')); // ['A','B','C'] 
    // ignoring details of map() function which essentially calls methods on every 
    // element of the array and forms another array of result and returns it

    HERHANGİ BİR nesne üzerinde HERHANGİ yöntemleri çağırmak biraz genel bir işlevdir . Ancak bu, belirtilen yöntemin belirtilen nesnenin zaten örtülü bir üyesi olacağı anlamına mı geliyor?

JavaScript işlevlerinin temel kavramları hakkında ciddi bir anlayış eksik olduğuma eminim. Lütfen bunu anlamama yardım et.


24
Bir nesnenin özelliklerine erişmenin iki yolu vardır: nokta notasyonu ve köşeli ayraç notasyonu. Biraz ilgili: stackoverflow.com/a/11922384/218196 . Dizi elemanlarını erişirken daima kullanın çünkü zaten ayraç hakkında hakkında bilmek: arr[5]. : Geçerli bir tanımlayıcı adlar nokta işaretini kullanabilirsiniz sayılar ise arr.5.
Felix Kling

2
İle aynı 5['toString']().
0x499602D2


İlgili okuma: 1) Kalıtım ve prototip zinciri: developer.mozilla.org/en-US/docs/JavaScript/Guide/… 2) JavaScript İlkellerinin Gizli Yaşamı: javascriptweblog.wordpress.com/2010/09/27/…
Theraot

21
İlk okuduğumda başlığın "JavaScript nasıl ve neden çalışır?" Olduğunu düşündüm. Ah güzel.
Sorunlu

Yanıtlar:


293

Yıkmak için.

  • .toUpperCase() bir yöntemdir String.prototype
  • 'a'ilkel bir değerdir, ancak Nesne temsiline dönüştürülür
  • Nesne özelliklerine / yöntemlerine erişmek için iki olası gösterimimiz var: nokta ve köşeli ayraç gösterimi

Yani

'a'['toUpperCase'];

aracılığıyla erişim ayraç mülkiyet toUpperCaseitibaren String.prototype. Bu özellik bir yönteme başvurduğundan , ekleyerek bu yöntemi çağırabiliriz.()

'a'['toUpperCase']();

Bu komik bir röportaj sorusu olurdu
FluffyBeing

78

foo.barve foo['bar']eşittir, böylece yayınladığınız kod aynı

alert('a'.toUpperCase())

Kullanırken foo[bar](tırnak işareti olmadığına dikkat edin) değişmez adı bardeğil, değişkenin bariçerdiği değeri kullanırsınız . Bu nedenle, foo[]gösterimi kullanmak yerine foo.dinamik bir özellik adı kullanmanızı sağlar.


Şuna bir bakalım callMethod:

Her şeyden önce, objargümanı olarak alan bir işlev döndürür . Bu işlev yürütüldüğünde o methodnesneyi çağırır . Dolayısıyla, verilen yöntemin ya objkendi başına ya da prototip zincirinde bir yerde olması gerekir.

Bu toUpperCaseyöntemin gelmesi durumunda String.prototype.toUpperCase- var olan her bir dize için yöntemin ayrı bir kopyasına sahip olmak oldukça aptalca olur.


25

Herhangi bir nesnenin üyelerine .propertyNamegösterimle veya ["propertyName"]gösterimle erişebilirsiniz . JavaScript dilinin özelliği budur. Üyenin nesnede olduğundan emin olmak için tanımlanmış olup olmadığını kontrol edin:

function callMethod(method) {
    return function (obj) {
        if (typeof(obj[method]) == 'function') //in that case, check if it is a function
           return obj[method](); //and then invoke it
    }
}

17

Temel olarak javascript her şeye bir Nesne gibi davranır, daha doğrusu her nesne bir sözlük / ilişkisel dizi olarak görüntülenebilir. Ve işlevler / yöntemler, bu ilişkilendirilebilir dizideki bir giriş olarak nesne için aynı şekilde tanımlanır.

Yani, esasen, referans veriyorsunuz / çağırıyorsunuz (' () 'a' nesnesinin (bu durumda bir dize türü olan) 'toUpperCase' özelliğine ' ).

İşte başımın üst kısmından bazı kodlar:

function myObject(){
    this.msg = "hey there! ;-)";
    this.woop = function(){
        alert(this.msg); //do whatever with member data
    }
}

var obj = new myObject();
alert( obj.msg );
alert( obj['msg'] );
obj['woop']();

10

anyObject['anyPropertyName']anyObject.anyPropertyNamene zaman olduğu ile aynıanyPropertyName sorunlu karakterler değil sahiptir.

Bkz. Nesnelerle Çalışma . MDN'den .

toUpperCaseMetot, tip String takılır. Bir işlevi ilkel bir değerde çağırdığınızda, burada 'a', otomatik olarak bir nesneye, burada bir String'e yükseltilir :

İlkel bir dizede bir yöntemin çağrılacağı veya özellik aramasının gerçekleşeceği bağlamlarda, JavaScript otomatik olarak ilkel dizeyi sarar ve yöntemi çağırır veya özellik araması gerçekleştirir.

Günlüğe kaydederek işlevin var olduğunu görebilirsiniz String.prototype.toUpperCase.


9

Yani Javascript'te objectsvardır objects. İşte bu doğa {}. Nesne özellikleri şunlardan biri kullanılarak ayarlanabilir: a.greeting = 'hello';veya a['greeting'] = 'hello';. Her iki yol da işe yarıyor.

Erişim aynı şekilde çalışır. a.greeting(tırnak işaretleri olmadan) 'hello', a['greeting']öyle 'hello'. İstisna: özellik bir sayı ise, yalnızca köşeli ayraç yöntemi çalışır. Nokta yöntemi yoktur.

Yani aslında bir işlev olan özelliğe sahip 'a'bir nesne 'toUpperCase'. İşlevi alabilir ve daha sonra şu şekilde çağırabilirsiniz: 'a'.toUpperCase()veya'a'['toUpperCase']() .

Ancak imo, harita fonksiyonunu yazmanın daha iyi bir yolunun O zaman fonksiyona

var caps = ['a','b','c'].map( function(char) { return char.toUpperCase(); } )

kimin ihtiyacı olduğu gibi olur.callMethod


Güzel açıkladı :-)
Elisha Senoo

6

Her JavaScript nesnesi bir karma tablodur, böylece bir anahtar belirleyerek üyelerine erişebilirsiniz. örneğin, bir değişken dize ise, toUpperCase işlevine sahip olmalıdır. Böylece,

var str = "a"
str['toUpperCase'](). // you get the method by its name as a key and invoke it.

yani, satır içi str ile, aşağıda olabilir

"a"["toUpperCase"]()

6

toUpperCase standart bir javascript yöntemidir: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/toUpperCase

Bunun gibi çalışmasının nedeni 'a'['toUpperCase'](), toUpperCase işlevinin dize nesnesinin bir özelliği olmasıdır 'a'. Bir nesnedeki özelliklere object[property]veya öğesini kullanarak başvurabilirsiniz object.property. 'A''toUpperCase' sözdizimi, 'a' dize nesnesinin 'toUppercase' özelliğine başvurduğunuzu ve sonra () öğesini çağırdığınızı gösterir.


4

Ancak bu, belirtilen yöntemin belirtilen nesnenin zaten örtülü bir üyesi olacağı anlamına mı geliyor?

Hayır. Birisi bir nesneye

  1. adlı bir özelliği yok toUpperCase; veya
  2. toUpperCaseişlevi olmayan adlı bir özelliğe sahip

İlk durumda, var olmayan bir özelliğe erişim döndürüldüğünden bir hata atılır undefinedve undefinedişlev olarak çağrılamazız .

İkinci durumda, bir hata atılır çünkü tekrar işlev dışı bir işlevi işlev olarak çağıramazız.

JavaScript'in çok gevşek yazılmış bir dil olduğunu unutmayın. Zorunlu olmadıkça ve gerekmedikçe çok az tip kontrolü yapılır veya yapılmaz. Gösterdiğiniz kod, bazı durumlarda çalışır, çünkü bu durumlarda, iletilen nesnenin adlı toUpperCasebir işlevi vardır, bu bir işlevdir.

objArgümanın doğru tipte mülklere sahip olması garanti edilmemesi gerçeği, JavaScript'i hiç rahatsız etmiyor. "Bekle ve gör" tutumunu benimser ve çalışma zamanında gerçek bir sorun oluşana kadar hata atmaz.


4

Javascript'teki hemen hemen her şey bir nesne olarak ele alınabilir. Sizin durumunuzda, alfabenin kendisi bir dize nesnesi gibi davranır toUpperCaseve yöntemi olarak çağrılabilir. Köşeli parantezler, nesne özelliklerine erişmenin alternatif bir yoludur ve toUpperCasebir yöntem olduğundan , şekillendirmenin ()yanında basit bir parantez gereklidir.['toUpperCase']['toUpperCase']() .

'a'['toUpperCase']() eşittir 'a'.toUpperCase()

'a'['toUpperCase']() // returns A
'a'.toUpperCase() // returns A

3

Burada dikkat edilmesi gereken önemli olan şey, Javascript dinamik bir dil olduğundan, her nesnenin aslında sadece yüceltilmiş bir karma haritasıdır ( birkaç istisna dışında ). Ve bir Javascript nesnesindeki her şeye iki şekilde erişilebilir: köşeli ayraç notasyonu ve nokta notasyonu.

Sorunuzun ilk kısmını yanıtlayan iki gösterimi hızlıca ele alacağım ve sonra ikinci kısma geçeceğim.

Parantez gösterimi

Bu mod, diğer programlama dillerindeki hashmaps ve dizilere erişmeye daha benzer. Bu sözdizimini kullanarak herhangi bir bileşene (verilere (diğer nesneler dahil) veya işleve) erişebilirsiniz .

Örneğinizde yaptığınız şey tam olarak budur. Bu 'a', bir dize ( C ++ gibi bir dilde olduğu gibi, bir harf hazır bilgisi değil ) var.

Köşeli ayraç gösterimini kullanarak toUpperCaseyöntemine erişirsiniz . Ancak ona erişmek hala yeterli değildir; basitçe alert, örneğin, Javascript yazmak yöntemi çağırmaz. Bu sadece basit bir ifade. İşlevi çağırmak için parantez eklemeniz gerekir: parametre almadığı alert()için basit bir iletişim kutusu gösterir undefined. Şimdi bu bilgiyi kodunuzu deşifre etmek için kullanabiliriz;

alert('a'.toUpperCase());

Hangi çok daha okunabilir.

Aslında, bunu biraz daha iyi anlamanın iyi bir yolu aşağıdaki Javascript'i çalıştırmaktır:

alert(alert)

Bu , ikinci uyarıyı yürütmeden alertde bir işlev nesnesi ileterek çağırır alert. Gösterilenler (en azından Chrome 26'da) aşağıdakilerdir:

function alert() { [native code] } 

Arayan:

alert(alert())

içeren iki ardışık mesaj kutusu gösterir undefined. Bunu açıklamak kolaydır: önce iç alert()yürütülür, gösterir undefined(çünkü herhangi bir parametresi yoktu) ve hiçbir şey döndürmez. Dış alarm, iç alarmın dönüş değerini alır - bu hiçbir şey değildir ve ayrıca undefinedbir mesaj kutusunda da gösterilir .

JsFiddle'daki tüm vakaları deneyin!

Nokta gösterimi

Bu, bir nesnenin üyelerine dot ( .) operatörü kullanılarak erişilmesini sağlayan daha standart bir yaklaşımdır . Kodunuz nokta gösteriminde şöyle görünecektir:

alert('a'.toUpperCase())

Çok daha okunabilir. Peki nokta gösterimini ne zaman kullanmalıyız ve ne zaman parantez gösterimini kullanmalıyız?

karşılaştırma

İki yöntem arasındaki temel fark semantiktir. Başka ayrıntılar da var, ama bir saniye içinde bunlara ulaşacağım. En önemlisi, gerçekte yapmak istediğiniz şeydir - temel kural, bir nesnenin sahip olduğu iyi kurulmuş alanlar ve yöntemler için nokta gösterimini ve nesnenizi bir karma harita olarak kullandığınızda parantez gösterimini kullanmanızdır. .

Bu kuralın neden bu kadar önemli olduğuna dair harika bir örnek, örneğinizde gösterilebilir - kod, nokta gösteriminin çok daha mantıklı olacağı bir yerde parantez gösterimi kullandığından, kodun okunmasını zorlaştırır. Ve bu kötü bir şey, çünkü kod yazıldığından çok daha fazla okunur .

Bazı durumlarda, nokta gösterimini kullanmak daha mantıklı olsa bile, braket gösterimini kullanmanız gerekir :

  • bir nesnenin bir üyesinin bir veya daha fazla boşluk veya başka bir özel karakter içeren bir adı varsa, nokta gösterimini kullanamazsınız: foo.some method()çalışmıyor, ancak çalışıyor foo["some method"]();

  • bir nesnenin üyelerine dinamik olarak erişmeniz gerekiyorsa, köşeli ayraç gösterimi de takılı kalırsınız;

Misal:

for(var i = 0; i < 10; ++i) {
   foo["method" + i](); 
 }

Sonuç olarak, nesneyi karma eşleme ( foods["burger"].eat()) olarak kullanırken köşeli parantez sözdizimini ve "gerçek" alanlar ve yöntemlerle ( enemy.kill()) çalışırken nokta sözdizimini kullanmanız gerekir . Javascript dinamik bir dil olduğundan, bir nesnenin "gerçek" alanları ve yöntemleri ile içinde saklanan "diğer" veriler arasındaki çizgi oldukça bulanıklaşabilir. Ama onları kafa karıştırıcı yollarla karıştırmadıkça, iyi olmalısın.


Şimdi, sorunuzun geri kalanına (sonunda!: P).

yöntemin her zaman obj üyesi olacağından nasıl emin olabilirim

Yapamazsın. Dene. derpBir dizgiyi aramayı deneyin . Şu satırlarda bir hata alırsınız:

Uncaught TypeError: Object a has no method 'derp' 

HERHANGİ BİR nesne üzerinde HERHANGİ yöntemleri çağırmak biraz genel bir işlevdir. Ancak bu, belirtilen yöntemin belirtilen nesnenin zaten örtülü bir üyesi olacağı anlamına mı geliyor?

Evet, sizin durumunuzda olması gerekir. Aksi takdirde yukarıda bahsettiğim hatayla karşılaşırsınız. Ancak, yok olması kullanmaya return obj[method]();içinde callMethod()işleve. Daha sonra harita işlevi tarafından kullanılan kendi işlevlerinizi ekleyebilirsiniz. İşte tüm harfleri büyük harfe dönüştüren sabit kodlanmış bir yöntem:

function makeCap()
{
    return function(obj) {
        return obj.toUpperCase();
    }
}

var caps2 = map(['a', 'b', 'c'], makeCap()); // ['A','B','C'] 
console.log(caps2)

Bağlandığınız öğreticideki kod kısmi işlevler kullanır . Kendi başlarına zor bir kavram. Bu konuda daha fazla şey okumak, işleri yapabileceğimden daha açık hale getirmemize yardımcı olacaktır.


Not: Bu, burada kaynaktaki sorudaki kod tarafından kullanılan harita işlevinin kodudur .

function map(arr, iterator) {
  var narr = [];
  for (var i = 0; i < arr.length; i++) narr.push(iterator(arr[i], i));
  return narr;
}

2

Aslında nasıl çalıştığını soruyorsanız, bunu nasıl okurum. Tamam, bu basit bir matematik fonksiyonudur. Bunu anlamak için ascii masasına bakmanız gerekir. Her harfe sayısal bir değer atar. Bunu örtbas etmek için yarışmak sadece bir mantık ifadesi kullanır ve bu nedenle If (ChcrValue> 80 && charValue <106) // Bu küçük harflerden oluşan bir kümedir, sonra charValue = charValue - 38; // alt küme ve üst küme arasındaki mesafe

bu kadar basit, aslında doğru değerleri aramak için uğraşmadım, ancak bu temelde tüm küçük harfleri büyük harf değerine kaydırıyor.


Bu soru ile nasıl ilişkilidir?
Quasdunk

2
@glh, nasıl ve neden 'a'['toUpperCase']()çalıştığını sordu . Ancak birisi soruyu okumadıysa, yanlış anlama anlaşılabilir.
LarsH

gr8 cevap, hiçbir stil text-transform: uppercaseeklenmediğinden, JS'nin davayı nasıl değiştirmeyi başardığı, şüphe uyandırdığı ve bir ya da iki şey öğrendiği ile ilgili olarak bitirdim. çok teşekkürler.
dkjain
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.