Bagaimana dan mengapa 'a' ['toUpperCase'] () di JavaScript berfungsi?

209

JavaScript terus mengejutkan saya dan ini adalah contoh lain. Saya baru saja menemukan beberapa kode yang saya tidak mengerti pada awalnya. Jadi saya debug dan datang ke temuan ini:

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

Sekarang ini harus jelas jika toUpperCase()didefinisikan sebagai anggota tipe string, tetapi pada awalnya tidak masuk akal.

Bagaimanapun,

  • apakah ini berhasil karena toUpperCasemerupakan anggota 'a'? Atau ada hal lain yang terjadi di balik layar?
  • yang kode saya membaca memiliki fungsi sebagai berikut:

    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
    

    Hal ini fungsi agak generik untuk memanggil APAPUN metode pada APAPUN objek. Tetapi apakah itu berarti metode yang ditentukan sudah akan menjadi anggota implisit dari objek yang ditentukan?

Saya yakin bahwa saya kehilangan beberapa pemahaman serius tentang konsep dasar fungsi JavaScript. Tolong bantu saya untuk memahami ini.

Mahesha999
sumber
24
Ada dua cara untuk mengakses properti dari objek: notasi titik dan notasi braket. Sedikit terkait: stackoverflow.com/a/11922384/218196 . Anda sudah tahu tentang tentang notasi braket karena Anda selalu menggunakannya ketika mengakses elemen array: arr[5]. Jika nomor di mana nama-nama identifier yang valid Anda bisa menggunakan notasi titik: arr.5.
Felix Kling
2
Itu sama dengan 5['toString']().
0x499602D2
1
Juga terkait: stackoverflow.com/q/4968406/218196 .
Felix Kling
Bacaan terkait: 1) Warisan dan rantai prototipe: developer.mozilla.org/en-US/docs/JavaScript/Guide/… 2) Kehidupan Rahasia JavaScript Primitives: javascriptweblog.wordpress.com/2010/09/27/…
Theraot
21
Pada bacaan pertama saya pikir judulnya adalah "bagaimana dan mengapa JavaScript berfungsi?" Baiklah
Bermasalah

Jawaban:

293

Untuk memecahnya.

  • .toUpperCase() adalah metode String.prototype
  • 'a'adalah nilai primitif, tetapi akan dikonversi menjadi representasi Object -nya
  • Kami memiliki dua notasi yang mungkin untuk mengakses properti / metode objek, notasi titik dan braket

Begitu

'a'['toUpperCase'];

adalah akses melalui notasi braket pada properti toUpperCase, dari String.prototype. Karena properti ini mereferensikan metode , kami dapat memohonnya dengan melampirkan()

'a'['toUpperCase']();
jandy
sumber
Ini akan menjadi pertanyaan wawancara yang lucu
FluffyBeing
78

foo.bardan foo['bar']sama sehingga kode yang Anda posting sama dengan

alert('a'.toUpperCase())

Saat menggunakan foo[bar](perhatikan bahwa tidak ada tanda kutip) Anda tidak menggunakan nama literal bartetapi nilai apa pun yang bardikandung variabel . Jadi, menggunakan foo[]notasi alih-alih foo.memungkinkan Anda menggunakan nama properti dinamis.


Mari kita lihat callMethod:

Pertama-tama, ia mengembalikan fungsi yang diambil objsebagai argumennya. Ketika fungsi itu dieksekusi akan memanggil methodobjek itu. Jadi metode yang diberikan hanya perlu ada pada objdirinya sendiri atau di suatu tempat di rantai prototipe.

Dalam hal toUpperCasemetode itu berasal String.prototype.toUpperCase- itu akan agak bodoh untuk memiliki salinan terpisah dari metode untuk setiap string yang ada.

Pencuri
sumber
25

Anda dapat mengakses anggota objek apa pun dengan .propertyNamenotasi atau ["propertyName"]notasi. Itu adalah fitur bahasa JavaScript. Untuk memastikan bahwa anggota ada di objek, cukup periksa, jika sudah ditentukan:

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
    }
}
Artyom Neustroev
sumber
17

Pada dasarnya javascript memperlakukan segala sesuatu sebagai Obyek, atau lebih tepatnya setiap objek dapat dilihat sebagai kamus / array asosiatif. Dan fungsi / metode didefinisikan dengan cara yang sama persis untuk objek - sebagai entri dalam array asosiatif ini.

Jadi pada dasarnya, Anda mereferensikan / memanggil (perhatikan ' () ') properti 'toUpperCase', dari objek 'a' (yang merupakan tipe string, dalam hal ini).

Inilah beberapa kode di bagian atas kepala saya:

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']();
Nisk
sumber
10

anyObject['anyPropertyName']sama seperti anyObject.anyPropertyNameketika anyPropertyNamekarakter tidak bermasalah.

Lihat Bekerja dengan Objek , dari MDN.

The toUpperCaseMetode melekat ke tipe String. Saat Anda memanggil fungsi pada nilai primitif, di sini 'a', fungsi itu dipromosikan secara otomatis ke objek, di sini sebuah String :

Dalam konteks di mana metode akan dipanggil pada string primitif atau pencarian properti terjadi, JavaScript akan secara otomatis membungkus string primitif dan memanggil metode atau melakukan pencarian properti.

Anda dapat melihat fungsi yang ada dengan masuk String.prototype.toUpperCase.

Denys Séguret
sumber
9

Jadi dalam Javascript, objectsadalah objects. Itulah sifat mereka {}. Properti objek dapat diatur menggunakan salah satu dari ini: a.greeting = 'hello';atau a['greeting'] = 'hello';. Keduanya bekerja.

Pengambilan bekerja sama. a.greeting(tanpa tanda kutip) adalah 'hello', a['greeting']adalah 'hello'. Pengecualian: jika properti adalah angka, hanya metode braket yang berfungsi. Metode dot tidak.

Jadi 'a'adalah objek dengan 'toUpperCase'properti yang sebenarnya merupakan fungsi. Anda dapat mengambil fungsi dan memanggilnya selanjutnya dengan cara: 'a'.toUpperCase()atau 'a'['toUpperCase']().

Tapi apakah cara yang lebih baik untuk menulis fungsi peta adalah sebagai

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

Siapa yang butuh callMethodfungsi itu?

Yaw Boakye
sumber
Menjelaskan dengan indah :-)
Elisha Senoo
6

Setiap objek JavaScript adalah tabel hash sehingga Anda dapat mengakses anggotanya dengan menentukan kunci. misalnya, jika variabel adalah string, maka harus memiliki fungsi toUpperCase. Jadi, Anda bisa memintanya

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

jadi, dengan inline str, Anda bisa memiliki di bawah ini

"a"["toUpperCase"]()
Shuping
sumber
6

toUpperCase adalah metode javascript standar: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/toUpperCase

Alasan kerjanya 'a'['toUpperCase']()adalah bahwa fungsi toUpperCase adalah properti dari objek string 'a'. Anda bisa mereferensikan properti pada objek menggunakan object[property]atau object.property. Sintaks 'a''toUpperCase' menunjukkan Anda mereferensikan propert 'toUppercase' dari objek string 'a', dan kemudian memanggilnya ().

SteveP
sumber
4

Tetapi apakah itu berarti metode yang ditentukan sudah akan menjadi anggota implisit dari objek yang ditentukan?

Tidak. Seseorang bisa lewat di objek itu

  1. tidak memiliki properti bernama toUpperCase; atau
  2. memiliki properti bernama toUpperCaseyang bukan fungsi

Dalam kasus pertama, kesalahan akan terjadi karena mengakses properti yang tidak ada kembali undefined, dan kami tidak dapat memanggil undefinedsebagai fungsi.

Dalam kasus kedua, kesalahan akan terjadi karena sekali lagi, kami tidak dapat menjalankan fungsi yang tidak berfungsi.

Ingat bahwa JavaScript adalah bahasa yang diketik dengan sangat longgar. Pemeriksaan tipe sedikit atau tidak ada terjadi kecuali dan sampai harus. Kode yang Anda perlihatkan berfungsi dalam kasus-kasus tertentu karena, dalam kasus-kasus itu, objek yang dikirimkan memiliki properti bernama toUpperCase, yang merupakan fungsi.

Fakta bahwa objargumen tersebut tidak dijamin memiliki jenis properti yang tepat tidak mengganggu JavaScript sama sekali. Itu mengadopsi sikap "tunggu dan lihat", dan tidak melemparkan kesalahan sampai masalah yang sebenarnya terjadi pada waktu berjalan.

Lars
sumber
4

Hampir semua yang ada di javascript dapat diperlakukan sebagai objek. Dalam kasus Anda, alfabet itu sendiri bertindak sebagai objek string dan toUpperCasedapat dipanggil sebagai metode. Kurung kotak hanyalah cara alternatif untuk mengakses properti objek dan karena toUpperCasemerupakan metode maka simplebracket ()diperlukan di sebelah ['toUpperCase']pembentukan ['toUpperCase']().

'a'['toUpperCase']() setara dengan 'a'.toUpperCase()

'a'['toUpperCase']() // returns A
'a'.toUpperCase() // returns A
Sanjay
sumber
3

Hal penting yang perlu diperhatikan di sini adalah, karena Javascript adalah bahasa yang dinamis , setiap objek pada dasarnya hanyalah peta hash yang dimuliakan ( dengan beberapa pengecualian ). Dan segala sesuatu dalam objek Javascript dapat diakses dengan dua cara - notasi braket dan notasi titik.

Saya akan segera membahas dua notasi yang menjawab bagian pertama dari pertanyaan Anda, dan kemudian saya akan sampai ke bagian kedua.

Notasi braket

Mode ini lebih mirip dengan mengakses hashmaps dan array dalam bahasa pemrograman lain. Anda dapat mengakses komponen apa pun (data (termasuk objek lain) atau fungsi) menggunakan sintaks ini.

Inilah yang Anda lakukan dalam contoh Anda. Anda miliki 'a', yang merupakan string (dan bukan karakter literal, seperti itu akan dalam bahasa seperti C ++).

Menggunakan notasi braket, Anda mengakses toUpperCasemetodenya. Tetapi mengaksesnya masih belum cukup; cukup mengetik alert, misalnya, dalam Javascript, tidak memanggil metode. Itu hanya pernyataan sederhana. Untuk memanggil fungsi, Anda perlu menambahkan tanda kurung: alert()menunjukkan kotak dialog sederhana yang berisi undefined, karena tidak menerima parameter. Kami sekarang dapat menggunakan pengetahuan ini untuk menguraikan kode Anda, yang menjadi:

alert('a'.toUpperCase());

Yang jauh lebih mudah dibaca.

Sebenarnya, cara yang baik untuk memahami ini sedikit lebih baik adalah dengan mengeksekusi Javascript berikut:

alert(alert)

Ini panggilan alertdengan melewatkannya objek fungsi, juga alert, tanpa juga menjalankan peringatan kedua. Apa yang ditampilkan (setidaknya di Chrome 26) adalah sebagai berikut:

function alert() { [native code] } 

Panggilan:

alert(alert())

menampilkan dua kotak pesan berurutan yang berisi undefined. Ini mudah dijelaskan: bagian dalam alert()dieksekusi pertama kali, menunjukkan undefined(karena tidak memiliki parameter apa pun) dan tidak mengembalikan apa pun. Lansiran luar menerima nilai balik lansiran batin - yang bukan apa-apa, dan juga ditampilkan undefineddi kotak pesan.

Cobalah semua kasing di jsFiddle!

Notasi dot

Ini adalah pendekatan yang lebih standar, yang memungkinkan anggota objek diakses menggunakan .operator dot ( ). Ini akan menjadi seperti apa kode Anda dalam notasi titik:

alert('a'.toUpperCase())

Jauh lebih mudah dibaca. Jadi kapan kita harus menggunakan notasi titik, dan kapan kita harus menggunakan notasi braket?

Perbandingan

Perbedaan utama antara kedua metode ini adalah semantik. Ada juga beberapa detail lain, tetapi saya akan sampai di sana sebentar lagi. Yang paling penting adalah apa yang sebenarnya ingin Anda lakukan - aturan praktisnya adalah Anda menggunakan notasi titik untuk bidang yang sudah mapan dan metode yang dimiliki objek, dan notasi braket ketika Anda benar-benar menggunakan objek Anda sebagai peta hash .

Contoh yang bagus tentang mengapa aturan ini sangat penting dapat ditunjukkan dalam contoh Anda - karena kode menggunakan notasi braket di tempat di mana notasi titik jauh lebih masuk akal, itu membuat kode lebih sulit dibaca. Dan itu hal yang buruk, karena kode dibaca lebih banyak daripada yang tertulis .

Dalam beberapa kasus, Anda harus menggunakan notasi braket walaupun menggunakan notasi titik lebih masuk akal:

  • Jika anggota objek memiliki nama yang mengandung satu atau lebih spasi atau karakter khusus lainnya, Anda tidak dapat menggunakan notasi titik: foo.some method()tidak berfungsi, tetapi foo["some method"]()tidak;

  • jika Anda perlu mengakses anggota objek secara dinamis, Anda juga terjebak menggunakan notasi braket;

Contoh:

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

Intinya adalah bahwa Anda harus menggunakan sintaks braket saat menggunakan objek sebagai peta hash ( foods["burger"].eat()) dan sintaksis titik ketika bekerja dengan bidang dan metode "aktual" ( enemy.kill()). Dengan Javascript menjadi bahasa yang dinamis, garis antara bidang "aktual" dan metode suatu objek dan data "lainnya" yang disimpan di dalamnya bisa menjadi cukup buram. Tetapi selama Anda tidak mencampurnya dengan cara yang membingungkan, Anda harus baik-baik saja.


Sekarang, ke sisa pertanyaan Anda (akhirnya!: P).

bagaimana saya bisa yakin metode akan selalu menjadi anggota obj

Kamu tidak bisa Cobalah. Cobalah untuk memanggil derpsebuah string. Anda akan mendapatkan kesalahan di baris:

Uncaught TypeError: Object a has no method 'derp' 

Ini adalah fungsi yang agak umum untuk memanggil metode APA SAJA pada objek APAPUN. Tetapi apakah itu berarti metode yang ditentukan sudah akan menjadi anggota implisit dari objek yang ditentukan?

Ya, dalam kasus Anda itu harus. Kalau tidak, Anda berakhir dengan kesalahan yang saya sebutkan di atas. Namun, Anda tidak harus menggunakan return obj[method]();dalam callMethod()fungsi. Anda dapat menambahkan fungsionalitas Anda sendiri yang kemudian digunakan oleh fungsi peta. Inilah metode hard-coded yang mengubah semua huruf menjadi huruf besar:

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

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

Kode dalam tutorial yang Anda tautkan menggunakan fungsi parsial . Mereka konsep yang rumit sendiri. Membaca lebih banyak tentang subjek itu akan membantu memperjelas hal-hal yang saya tidak bisa membuatnya.


Catatan: ini adalah kode fungsi peta yang digunakan oleh kode dalam pertanyaan, sumber di sini .

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

Jika Anda bertanya bagaimana cara kerjanya, itulah cara saya membacanya. Ok ini adalah fungsi matematika sederhana. Untuk memahaminya, Anda perlu melihat tabel ascii. Yang menetapkan nilai numerik untuk setiap huruf. Untuk terselubung para pesaing cukup menggunakan pernyataan logika untuk tersembunyi jadi misalnya If (ChcrValue> 80 && charValue <106) // Ini adalah himpunan huruf kecil kemudian charValue = charValue - 38; // jarak antara set bawah dan set atas

sesederhana itu, saya sebenarnya tidak repot-repot mencari nilai yang benar namun ini pada dasarnya menggeser semua huruf kecil ke nilai huruf besar.

Daniel Haro
sumber
Bagaimana ini terkait dengan pertanyaan?
Quasdunk
2
@ Glh, dia bertanya bagaimana dan mengapa 'a'['toUpperCase']()bekerja. Tetapi kesalahpahaman itu bisa dimengerti, jika seseorang tidak membaca pertanyaan itu.
LarsH
Jawabannya, karena tidak ada gaya text-transform: uppercaseyang ditambahkan, saya akhirnya kehabisan akal bagaimana JS berhasil mengubah kasus ini, menghilangkan keraguan dan belajar satu atau dua hal. Terima kasih banyak.
dkjain