JavaScriptの 'a' ['toUpperCase']()はどのように、そしてなぜ機能するのですか?


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

    これは、ANYオブジェクトに対してANYメソッドを呼び出す、汎用的な関数です。しかし、それは、指定されたメソッドが既に指定されたオブジェクトの暗黙のメンバーであることを意味しますか?

JavaScript関数の基本的な概念についての真剣な理解が欠けていると私は確信しています。これを理解してください。


24
オブジェクトのプロパティにアクセスするには、ドット表記とブラケット表記の2つの方法があります。やや関連:stackoverflow.com/a/11922384/218196を。ブラケット要素については、配列要素にアクセスするときに常に使用するため、すでに知っていますarr[5]。有効な識別子の名前の番号の場合、ドット表記を使用できますarr.5
Felix Kling 2013年

2
と同じ5['toString']()です。
0x499602D2 2013年


関連資料:1)継承とプロトタイプチェーン:developer.mozilla.org/en-US/docs/JavaScript/Guide/…2)JavaScriptプリミティブの秘密の生活:javascriptweblog.wordpress.com/2010/09/27/…
Theraot 2013年

21
最初に読んだとき、タイトルは「JavaScriptがどのように、そしてなぜ機能するのか」と思いました。まぁ。
問題のある

回答:


293

それを壊すために。

  • .toUpperCase() の方法です String.prototype
  • 'a'プリミティブ値ですが、そのオブジェクト表現に変換されます
  • オブジェクトのプロパティ/メソッドにアクセスするには、ドットとブラケット表記の2つの表記法があります。

そう

'a'['toUpperCase'];

介してアクセスされ、ブラケット表記プロパティにはtoUpperCase、からString.prototype。このプロパティはmethodを参照するため、アタッチすることで呼び出すことができます()

'a'['toUpperCase']();

これは陽気なインタビューの質問になります
FluffyBeing

78

foo.barfoo['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はすべてをオブジェクトとして扱います。むしろ、すべてのオブジェクトを辞書/連想配列として表示できます。そして、関数/メソッドは、この連想配列のエントリとして、オブジェクトに対してまったく同じ方法で定義されます。

つまり、基本的に、'a'オブジェクト(この場合は文字列型)の 'toUpperCase'プロパティを参照/呼び出し( ' () 'に注意)しています。

これが私の頭のてっぺんのコードです。

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の「Working with Objects」を参照してください。

toUpperCase方法は、タイプStringに取り付けられています。ここ'a'でプリミティブ値に対して関数を呼び出すと、それは自動的にオブジェクト(ここではString)に昇格されます。

メソッドがプリミティブ文字列で呼び出されるか、プロパティルックアップが発生するコンテキストでは、JavaScriptは自動的に文字列プリミティブをラップし、メソッドを呼び出すか、プロパティルックアップを実行します。

ロギングにより、関数が存在することがわかりString.prototype.toUpperCaseます。


9

だから、JavaScriptで、objectsありますobjects。つまり、この性質のもの{}です。オブジェクトプロパティは、a.greeting = 'hello';またはのいずれかを使用して設定できますa['greeting'] = 'hello';。どちらの方法でも機能します。

取得は同じように機能します。a.greeting(引用符なし)is 'hello'a['greeting']is 'hello'です。例外:プロパティが数値の場合、ブラケットメソッドのみが機能します。ドット方式にはありません。

したがって'a''toUpperCase'実際には関数であるプロパティを持つオブジェクトも同様です。あなたは機能を取得し、その後、いずれかの方法でそれを呼び出すことができます'a'.toUpperCase()'a'['toUpperCase']()

しかし、マップ関数を書くためのより良い方法

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

は、誰がcallMethod関数を必要とするのでしょうか?


美しく説明された:-)
Elisha Senoo

6

すべてのJavaScriptオブジェクトはハッシュテーブルであるため、キーを指定することでそのメンバーにアクセスできます。たとえば、変数が文字列の場合、toUpperCase関数が必要です。だから、あなたはそれを呼び出すことができます

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

したがって、インラインstrを使用すると、以下のようになります

"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」は、「a」文字列オブジェクトの「toUppercase」プロパティを参照し、それを呼び出すことを示しています()。


4

しかし、それは、指定されたメソッドが既に指定されたオブジェクトの暗黙のメンバーであることを意味しますか?

いいえ、誰かがオブジェクトを渡すことができます

  1. というプロパティはありませんtoUpperCase。または
  2. toUpperCase関数ではないという名前のプロパティがあります

最初のケースではundefined、存在しないプロパティにアクセスするとが返さundefinedれ、関数として呼び出すことができないため、エラーがスローされます。

2番目のケースでは、関数として非関数を呼び出すことができないため、エラーがスローされます。

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オブジェクトのすべてにアクセスするには、ブラケット表記とドット表記の2つの方法があります。

質問の最初の部分に答える2つの表記について簡単に説明し、次に2番目の部分に進みます。

ブラケット表記

このモードは、他のプログラミング言語でのハッシュマップと配列へのアクセスに似ています。この構文を使用して、任意のコンポーネント(データ(他のオブジェクトを含む)または関数)にアクセスできます。

これはまさにあなたがあなたの例でやっていることです。あなたは持っている'a'(と文字列である、ではない、それは、そのようなC ++などの言語になりますように、文字リテラル)。

ブラケット表記を使用して、そのtoUpperCaseメソッドにアクセスします。しかし、それにアクセスするだけではまだ十分ではありません。alertたとえば、JavaScriptで単にと入力しても、メソッドは呼び出されません。これは単純なステートメントです。関数を呼び出すには、括弧を追加する必要があります。パラメーターを受け取っていないため、をalert()含む単純なダイアログボックスが表示さundefinedれます。これで、この知識を使用してコードを解読できます。コードは次のようになります。

alert('a'.toUpperCase());

はるかに読みやすいです。

実際、これを少しよく理解するには、次のJavascriptを実行するのがよいでしょう。

alert(alert)

これは、2番目のアラートも実行せずalertに、関数オブジェクトを渡すことで呼び出しますalert。表示されるのは(少なくともChrome 26では)次のとおりです。

function alert() { [native code] } 

呼び出し:

alert(alert())

は、を含む2つの連続したメッセージボックスを示していますundefined。これは簡単に説明できます。内部alert()が最初に実行され、表示されundefined(パラメータがないため)、何も返されません。外側のアラートは、内側のアラートの戻り値を受け取りundefinedます。これは何も起こらず、メッセージボックスにも表示されます。

jsFiddleですべてのケースを試してください!

ドット表記

これはより標準的な方法であり、ドット(.)演算子を使用してオブジェクトのメンバーにアクセスできます。これはあなたのコードがドット表記でどのように見えるかです:

alert('a'.toUpperCase())

はるかに読みやすいです。では、ドット表記を使用する必要がある場合と、ブラケット表記を使用する必要がある場合はどうでしょうか。

比較

2つの方法の主な違いはセマンティックです。他にもいくつかの詳細がありますが、それらについてはすぐに説明します。最も重要なことは、実際に何をしたいかです-経験則として、オブジェクトが持つ確立されたフィールドとメソッドにはドット表記を使用し、実際にオブジェクトをハッシュマップとして使用している場合にはブラケット表記を使用します。

このルールが非常に重要である理由の優れた例は、例に示すことができます。コードでは、ドット表記がより適切である場所でブラケット表記を使用しているため、コードが読みにくくなっています。そして、それは悪いことです。なぜなら、コードは書かれたよりも何度も読み込まれるからです

場合によっては、ドット表記を使用する方が賢明であっても、ブラケット表記を使用する必要があります。

  • オブジェクトのメンバーの名前に1つ以上のスペースまたはその他の特殊文字が含まれている場合、ドット表記は使用できません。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' 

これは、ANYオブジェクトに対してANYメソッドを呼び出す、ちょっとした汎用的な関数です。しかし、それは、指定されたメソッドが既に指定されたオブジェクトの暗黙のメンバーであることを意味しますか?

はい、あなたの場合はそうでなければなりません。そうしないと、上記のエラーが発生します。しかし、あなたはしていない持って使用することreturn obj[method]();callMethod()機能。map関数で使用される独自の機能を追加できます。すべての文字を大文字に変換するハードコードされたメソッドは次のとおりです。

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 2013

2
@glh、彼はどのようにそしてなぜ'a'['toUpperCase']()働くか尋ねました。しかし、誰かが質問を読まなければ、誤解は理解できます。
LarsH

gr8回答、スタイルtext-transform: uppercaseが追加されていないので、JSがどのようにしてケースを変更し、疑いを解消し、1つまたは2つのことを学んだかについて、私は途方に暮れました。どうもありがとう。
dkjain
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.