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

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

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

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

В любом случае,

  • Это работает, потому что toUpperCase является членом 'a'? Или что-то происходит за кулисами?
  • code Я читал функцию следующим образом:

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

Ответ 1

Чтобы сломать его.

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

Итак,

'a'['toUpperCase'];

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

'a'['toUpperCase']();

Ответ 2

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

alert('a'.toUpperCase())

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


Посмотрим на callMethod:

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

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

Ответ 3

Вы можете либо получить доступ к членам любого объекта с обозначением .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
    }
}

Ответ 4

В основном 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']();

Ответ 5

anyObject['anyPropertyName'] совпадает с anyObject.anyPropertyName, когда anyPropertyName не имеет проблемных символов.

Смотрите Работа с объектами из MDN.

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

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

Вы можете видеть, что функция существует путем ведения журнала String.prototype.toUpperCase.

Ответ 6

Итак, в 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, тогда?

Ответ 7

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

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

поэтому, по строке str, вы можете иметь ниже

"a"["toUpperCase"]()

Ответ 8

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", а затем вызываете его().

Ответ 9

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

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

  • не имеет свойства с именем toUpperCase; или
  • имеет свойство с именем toUpperCase, которое не является функцией

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

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

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

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

Ответ 10

Почти все в javascript можно рассматривать как объект. В вашем случае сам алфавит действует как строковый объект, а toUpperCase может быть вызван как его метод. Квадратные скобки - это просто альтернативный способ доступа к свойствам объекта, а так как toUpperCase - это метод, то рядом с ['toUpperCase'] требуется ['toUpperCase'], образуя ['toUpperCase']().

'a'['toUpperCase']() эквивалентен 'a'.toUpperCase()

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

Ответ 11

Важно отметить, что, поскольку Javascript является dynamic, каждый объект, по сути, просто прославленный hash-map (за несколькими исключениями). И все, что есть в объекте Javascript, можно получить двумя способами: обозначение и точка ноты.

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

Обозначение кронштейна

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

Это именно то, что вы делаете в своем примере. У вас есть 'a', который является строкой (и не символьным литералом, например, на таком языке, как С++).

Используя нотацию скобки, вы получаете доступ к ее методу 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"]() does;

  • если вам нужно динамически обращаться к членам объекта, вы также застреваете с помощью скобок,

Пример:

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;
}

Ответ 12

Если вы спрашиваете, как это работает, как я его читаю. Хорошо, это простая математическая функция. Чтобы понять это, вам нужно посмотреть на таблицу ascii. Каждому письму присваивается числовое значение. Чтобы скрыть это, соперник просто использует логическое утверждение для скрытого, например, Если (ChcrValue > 80 & charValue < 106)//Это набор строчных букв затем charValue = charValue - 38;//расстояние между нижним и верхним наборами

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