Как и почему работает 'a' ['toUpperCase'] () в JavaScript?

209

JavaScript продолжает удивлять меня, и это еще один пример. Я только что наткнулся на код, который сначала не понял. Поэтому я отладил его и пришел к такому выводу:

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

Теперь это должно быть очевидно, если toUpperCase()оно определено как член строкового типа, но изначально оно не имело смысла для меня.

Тем не мение,

  • это работает, потому что toUpperCaseявляется членом «а»? Или что-то еще происходит за кулисами?
  • код я читал имеет функцию следующим образом :

    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. Пожалуйста, помогите мне понять это.

Mahesha999
источник
24
Есть два способа получить доступ к свойствам объекта: точечная нотация и нотация в скобках. Немного связано: stackoverflow.com/a/11922384/218196 . Вы уже знаете о том, о кронштейне нотации , потому что вы всегда использовать его при доступе к элементам массива: arr[5]. Если числа , где действительные имена идентификаторов можно использовать точечную нотацию: arr.5.
Феликс Клинг
2
Это так же, как 5['toString']().
0x499602D2
1
Также связано: stackoverflow.com/q/4968406/218196 .
Феликс Клинг
Связанное чтение: 1) Наследование и цепочка прототипов: developer.mozilla.org/en-US/docs/JavaScript/Guide/… 2) Секретная жизнь примитивов JavaScript: javascriptweblog.wordpress.com/2010/09/27/…
Theraot
21
При первом прочтении я подумал, что заголовок был "как и почему работает JavaScript?" Ах хорошо.
Проблемное

Ответы:

293

Чтобы сломать это.

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

Так

'a'['toUpperCase'];

является доступ через обозначение скобки на имущество toUpperCase, с String.prototype. Поскольку это свойство ссылается на метод , мы можем вызвать его, прикрепив()

'a'['toUpperCase']();
JANDY
источник
Это был бы веселый вопрос для интервью
FluffyBeing
78

foo.barи foo['bar']равны, поэтому код, который вы разместили, такой же, как

alert('a'.toUpperCase())

При использовании foo[bar](обратите внимание на отсутствие кавычек) вы используете не буквальное имя, barа любое значение, которое barсодержит переменная . Таким образом, использование foo[]обозначения вместо foo.позволяет использовать динамическое имя свойства.


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

Прежде всего, он возвращает функцию, которая принимает в objкачестве аргумента. Когда эта функция выполняется, она вызывает methodэтот объект. Таким образом, данный метод просто должен существовать либо objсам по себе, либо где-то в своей цепочке прототипов.

В случае, если toUpperCaseэтот метод происходит String.prototype.toUpperCase- было бы довольно глупо иметь отдельную копию метода для каждой существующей строки.

ThiefMaster
источник
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']();
Nisk
источник
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.

так, по встроенной ул, вы могли бы иметь ниже

"a"["toUpperCase"]()
Shuping
источник
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', а затем вызываете его ().

SteveP
источник
4

Но значит ли это, что указанный метод уже будет неявным членом указанного объекта?

Кто-то может передать объект, который

  1. не имеет названного свойства toUpperCase; или
  2. имеет свойство с именем toUpperCase, которое не является функцией

В первом случае будет выдано сообщение об ошибке, поскольку доступ к несуществующему свойству вернется undefined, и мы не сможем вызвать его undefinedкак функцию.

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

Помните, что JavaScript - это язык со слабой типизацией. Мало или нет проверки типа происходит, если и пока это не должно. Код, который вы показали, работает в определенных случаях, потому что в этих случаях переданный объект имеет свойство с именем toUpperCase, то есть функция.

Тот факт, что objаргумент не гарантирует правильные типы свойств, вообще говоря, не беспокоит JavaScript. Он принимает отношение «жди и смотри» и не выдает ошибку, пока не возникнет реальная проблема во время выполнения.

LarsH
источник
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; // расстояние между нижним набором и верхним набором

это так просто, я на самом деле не удосужился найти правильные значения, однако это в основном переводит все строчные буквы в прописные.

Даниэль Аро
источник
Как это связано с вопросом?
Quasdunk
2
@glh, он спросил, как и почему 'a'['toUpperCase']()работает. Но недоразумение понятно, если кто-то не прочитал вопрос.
LarsH
Ответ gr8, так как не text-transform: uppercaseбыло добавлено ни одного стиля , я был в курсе того, как JS удалось изменить случай, очистить сомнение и узнать одну или две вещи. большое спасибо.
dkjain