Як і чому працює функція 'a' ['toUpperCase'] () у JavaScript?


209

JavaScript мене дивує, і це ще один приклад. Я просто натрапив на якийсь код, який я спочатку не зрозумів. Тож я налагодив це і дійшов до цього висновку:

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

Тепер це повинно бути очевидним, якщо toUpperCase()він визначений як член типу рядка, але спочатку це не мало сенсу для мене.

У будь-якому випадку,

  • це працює, тому що toUpperCaseє членом "a"? Або щось ще відбувається за лаштунками?
  • код я читав має функцію наступним чином :

    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
    

    Це своєрідна загальна функція викликати будь-які методи на будь-якому об’єкті. Але чи означає це, що зазначений метод вже буде неявним членом зазначеного об'єкта?

Я впевнений, що мені не вистачає серйозного розуміння основних понять функцій JavaScript. Будь ласка, допоможіть мені зрозуміти це.


24
Існує два способи доступу до властивостей об'єкта: позначення крапок і позначення дужок. Трохи пов'язані: stackoverflow.com/a/11922384/218196 . Ви вже знаєте про те, про кронштейні нотації , тому що ви завжди використовувати його при доступі до елементів масиву: arr[5]. Якщо числа , де дійсні імена ідентифікаторів можна використовувати точкову нотацію: arr.5.
Фелікс Клінг

2
Це те саме, що 5['toString']().
0x499602D2


Пов'язане читання: 1) Спадщина та прототип ланцюга: developer.mozilla.org/en-US/docs/JavaScript/Guide/… 2) Таємне життя примітивів JavaScript: javascriptweblog.wordpress.com/2010/09/27/…
Тераот

21
Спочатку прочитавши, я подумав, що назва "як і чому працює JavaScript?" Ну добре.
Проблемні

Відповіді:


293

Щоб її розбити.

  • .toUpperCase() є методом String.prototype
  • 'a'є примітивним значенням, але перетворюється на його об'єктне представлення
  • У нас є дві можливі позначення для доступу до властивостей / методів об'єкта, позначення крапок і дужок

Так

'a'['toUpperCase'];

- це доступ через позначення дужок до ресурсу toUpperCase, з String.prototype. Оскільки ця властивість посилається на метод , ми можемо викликати його, приєднавши()

'a'['toUpperCase']();

Це було б веселе запитання про інтерв'ю
FluffyBeing

78

foo.barі foo['bar']рівні, тому код, який ви опублікували, такий самий, як

alert('a'.toUpperCase())

При використанні foo[bar](зверніть увагу на відсутність лапок) ви використовуєте не буквальне ім'я, barа будь-яке значення, яке barмістить змінна . Тож використання foo[]позначень замість foo.дозволяє використовувати ім’я динамічної властивості.


Давайте подивимось на callMethod:

Перш за все, він повертає функцію, яка бере objсвій аргумент. Коли ця функція буде виконана, вона буде викликати methodцей об'єкт. Отже, даний метод просто повинен існувати або на objсобі, або десь на своєму прототипі.

У випадку з toUpperCaseцим методом виходить String.prototype.toUpperCase- було б досить нерозумно мати окрему копію методу для кожної окремої рядки.


25

Ви можете отримати доступ до членів будь-якого об'єкта з .propertyNameпозначеннями або ["propertyName"]нотаціями. У цьому полягає особливість мови JavaScript. Щоб переконатися, що член знаходиться в об'єкті, просто перевірте, чи він визначений:

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

В основному javascript розглядає все як Об'єкт, точніше кожен об'єкт може розглядатися як словник / асоціативний масив. І функції / методи визначаються точно таким же чином для об'єкта - як запис у цьому асоціативному масиві.

Отже, ви посилаєтесь / викликаєте (помічаєте ' () ') властивість 'toUpperCase' об'єкта 'a' (що в цьому випадку є рядковим типом).

Ось якийсь код верхньої частини моєї голови:

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.anyPropertyNameколи anyPropertyNameне має проблемних символів.

Див. Робота з об'єктами , з MDN.

toUpperCaseМетод прикріплений до типу String. Коли ви викликаєте функцію на примітивному значенні, тут 'a'вона автоматично просувається до об'єкта, тут String :

У контекстах, де метод повинен викликати примітивний рядок або відбувається пошук властивостей, JavaScript автоматично загортає примітивний рядок і викликає метод або здійснює пошук властивостей.

Ви можете побачити, що функція існує за допомогою реєстрації String.prototype.toUpperCase.


9

Так у Javascript, objectsє objects. Це вони такого характеру {}. Властивості об'єкта можна встановити, використовуючи будь-яке з них: a.greeting = 'hello';або a['greeting'] = 'hello';. Обидва способи працюють.

Пошук працює так само. a.greeting(без лапок) є 'hello', a['greeting']є 'hello'. Виняток: якщо властивість є числом, працює лише метод дужок. Метод крапок не робить.

Так 'a'є об'єкт із 'toUpperCase'властивістю, який фактично є функцією. Ви можете отримати функцію та викликати її згодом будь-яким способом: 'a'.toUpperCase()або 'a'['toUpperCase']().

Але імо кращим способом написання функції карти було б таке:

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

Кому тоді потрібна callMethodфункція?


Чудово пояснено :-)
Елісей Сенуо

6

Кожен об’єкт JavaScript є хеш-таблицею, тому ви можете отримати доступ до його членів, вказавши ключ. наприклад, якщо змінною є рядок, то вона повинна мати функцію toUpperCase. Отже, ви можете посилатися на це

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

так, за рядком strline, ви могли мати нижче

"a"["toUpperCase"]()

6

toUpperCase - це стандартний метод javascript: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/toUpperCase

Причина, по якій вона працює, 'a'['toUpperCase']()полягає в тому, що функція toUpperCase є властивістю рядкового об'єкта 'a'. Ви можете посилатися на властивості об’єкта, використовуючи object[property]або object.property. Синтаксис "a''toUpperCase" вказує на те, що ви посилаєтеся на "toUppercase" експерта рядкового об'єкта "a", а потім викликаєте його ().


4

Але чи означає це, що зазначений метод вже буде неявним членом зазначеного об'єкта?

Ні. Хтось міг передати об’єкт, який

  1. не має власності, названого toUpperCase; або
  2. має властивість, яке названо, toUpperCaseщо не є функцією

У першому випадку буде видано помилку, оскільки звертається до ресурсу, який не існує undefined, і ми не можемо викликати undefinedфункцію.

У другому випадку буде видано помилку, оскільки знову ж таки ми не можемо викликати нефункціональну функцію.

Пам’ятайте, що JavaScript - це дуже вільно набрана мова. Перевірка типу або невелика перевірка типу не відбувається, якщо і поки це не відбудеться. Код, який ви показали, працює в певних випадках, оскільки в цих випадках переданий об'єкт має властивість toUpperCase, яке називається , тобто функцією.

Те, що objаргумент не гарантовано має правильні типи властивостей, взагалі не турбує JavaScript. Він приймає ставлення "чекай і дивись" і не видає помилку, поки не виникає справжня проблема під час виконання.


4

Майже все в JavaScript може трактуватися як об’єкт. У вашому випадку сам алфавіт виступає в якості рядкового об'єкта і toUpperCaseйого можна викликати як його метод. Квадратні дужки є лише альтернативним способом доступу до властивостей об'єкта, і оскільки toUpperCaseце метод, значить, ()поряд з ['toUpperCase']формуванням потрібен простий брекет ['toUpperCase']().

'a'['toUpperCase']() еквівалентно 'a'.toUpperCase()

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

3

Тут важливо зазначити, що оскільки Javascript - це динамічна мова, кожен об’єкт, по суті, є лише прославленою хеш-картою ( за кількома винятками ). І все в об'єкті Javascript можна отримати двома способами - нотацією дужок і нотацією крапок.

Я швидко перейду на два позначення, відповідаючи на першу частину вашого питання, а потім перейду до другої частини.

Нотація дужки

Цей режим більше схожий на доступ до хешмапів та масивів іншими мовами програмування. Ви можете отримати доступ до будь-якого компонента (даних (включаючи інші об'єкти) чи функції) за допомогою цього синтаксису.

Це саме те, що ти робиш у своєму прикладі. У вас є 'a', що є рядком (а не буквеним символом, як це було б у такій мові, як C ++).

Використовуючи позначення дужок, ви отримуєте доступ до його toUpperCaseметоду. Але доступ до нього все ще недостатній; просто введення alert, наприклад, у Javascript, метод не викликає. Це просто просте твердження. Для виклику функції потрібно додати круглі дужки: alert()показує просте діалогове вікно, що містить undefined, оскільки воно не отримало жодних параметрів. Тепер ми можемо використовувати ці знання для розшифровки вашого коду, який стає:

alert('a'.toUpperCase());

Що набагато читабельніше.

Насправді, хороший спосіб зрозуміти це трохи краще - це виконати наступний Javascript:

alert(alert)

Це викликає alert, передаючи йому об'єкт функції, також alert, не виконуючи також другого попередження. Показано (як мінімум, у Chrome 26):

function alert() { [native code] } 

Дзвінки:

alert(alert())

показує два послідовних вікна повідомлень, що містять undefined. Це легко пояснити: внутрішній alert()виконується спочатку, показує undefined(бо не мав жодних параметрів) і нічого не повертає. Зовнішнє сповіщення отримує зворотне значення внутрішнього сповіщення - яке є нічого, а також відображається undefinedу вікні повідомлення.

Спробуйте всі випадки на jsFiddle!

Точкове позначення

Це більш стандартний підхід, який дозволяє отримати доступ до членів об'єкта за допомогою .оператора dot ( ). Ось як виглядатиме ваш код у крапкових позначеннях:

alert('a'.toUpperCase())

Набагато читабельніший. Отже, коли нам слід використовувати позначення крапок, а коли слід використовувати позначення дужок?

Порівняння

Основна відмінність двох методів - семантична. Є ще деякі деталі, але я дістанусь до них за секунду. Найголовніше - це те, що ви насправді хочете зробити. Основне правило полягає в тому, що ви використовуєте крапкові позначення для добре встановлених полів і методів, якими володіє об'єкт, і дужки-позначення, коли ви фактично використовуєте ваш об'єкт як хеш-карту .

Чудовий приклад того, чому це правило є настільки важливим, може бути показаний у вашому прикладі - оскільки код використовує позначення дужок у місці, де нотація крапок була б набагато більш розумною, це робить код важче читати. І це погано, адже код читається набагато більше разів, ніж написано .

У деяких випадках вам доводиться використовувати позначення дужок, навіть якщо використання позначень крапок було більш розумним:

  • якщо член об’єкта має ім'я, що містить один або кілька пробілів або будь-які інші спеціальні символи, ви не можете використовувати позначення крапок: foo.some method()не працює, але foo["some method"]()робить;

  • якщо вам потрібно динамічно отримати доступ до членів об’єкта, ви також застрягли, використовуючи позначення дужок;

Приклад:

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

Суть полягає в тому, що ви повинні використовувати синтаксис дужок при використанні об'єкта як хеш-карти ( foods["burger"].eat()) і крапки синтаксису під час роботи з "фактичними" полями та методами ( enemy.kill()). Оскільки Javascript є динамічною мовою, лінія між "фактичними" полями та методами об'єкта та "іншими" даними, що зберігаються всередині, може бути досить розмитою. Але поки ви не змішуєте їх заплутаними способами, вам слід добре.


Тепер, на решту вашого питання (нарешті!: P).

як я можу бути впевненим, що метод завжди буде членом obj

Ви не можете. Спробуй це. Спробуйте зателефонувати derpна рядок. Ви отримаєте помилку в рядках:

Uncaught TypeError: Object a has no method 'derp' 

Це своєрідна загальна функція викликати будь-які методи на будь-якому об’єкті. Але чи означає це, що зазначений метод вже буде неявним членом зазначеного об'єкта?

Так, у вашому випадку це мало бути. Інакше ви стикаєтесь з помилкою, про яку я згадував вище. Однак вам не потрібно використовувати return obj[method]();цю callMethod()функцію. Ви можете додати власну функціональність, яку потім використовує функція карти. Ось важко закодований метод, який перетворює всі літери у великі літери:

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

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

Код у підручнику, до якого ви пов’язані, використовує часткові функції . Вони самі по собі хитрі поняття. Читання детальніше про цю тему має допомогти зробити речі яснішими, ніж я коли-небудь можу їх зробити.


Примітка: це код функції карти, який використовується кодом у питанні, джерело тут .

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

2

Якщо ви запитуєте, як це насправді працює, це те, як я це читаю. Ок, це проста математична функція. Щоб зрозуміти це, вам потрібно переглянути таблицю ascii. Який присвоює кожній букві числове значення. Щоб приховати його, конкурент просто використовує логічний вираз для його приховання, наприклад, якщо If (ChcrValue> 80 && charValue <106) // Це набір малих літер, то charValue = charValue - 38; // відстань між нижньою множиною та верхнім набором

це так просто, я насправді не намагався шукати правильні значення, проте це в основному зміщує всі малі літери на великі величини.


Як це пов’язано з питанням?
Квасдунк

2
@glh, він запитав, як і навіщо 'a'['toUpperCase']()працювати. Але непорозуміння зрозуміле, якщо хтось не прочитав питання.
LarsH

Відповідь gr8, оскільки жодних стилів text-transform: uppercaseне додано, я був на кмітливості, як JS зумів змінити справу, очистив сумніви і дізнався щось або два. дуже дякую.
dkjain
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.