JavaScript >>>演算子とは何ですか、またどのように使用しますか?


150

Arrayにフィルターメソッドを追加するMozillaのコードを見ていましたが、混乱を招くコード行がありました。

var len = this.length >>> 0;

私は>>>がJavaScriptで使用されるのを見たことがありません。
それは何ですか、何をしますか?


@CMS確かに、このコード/質問はそれらから来ています。ただし、ここでの応答は以前の応答よりも具体的で価値があります。
ジャスティンジョンソン

2
それともバグか、Mozillaの担当者がthis.lengthを-1にすることを想定しています。>>>は符号なしシフト演算子なので、var lenは常に0以上になります。
user347594 2010年

1
JS(ダグクロックフォード)の実装の領主を覆す-アッシュサールはそれのために使用見つかっArray.prototype.push/ Array.prototype.pop- hexmen.com/blog/2006/12/push-and-popを(彼はテストをしたものの、笑)。
Dan Beam

回答:


211

非数値を数値に変換するだけでなく、32ビットの符号なし整数として表現できる数値に変換します。

JavaScriptの数字は、倍精度浮動小数点数(*)であるが、ビット単位の演算子(<<>>&|および~)は、32ビット整数の操作で定義されています。ビット単位の演算を行うと、数値が32ビットの符号付きintに変換され、計算を実行してからNumberに変換する前に、小数部と32より上位のビットが失われます。

そのため、0ビットの右シフトのように、実際に影響を与えずにビット単位の演算を行う>>0ことは、数値を丸めて32ビットのint範囲にあることを確認する簡単な方法です。さらに、トリプル>>>演算子は、符号なし演算を実行した後、その計算結果を、他の演算子が行う符号付き整数ではなく、符号なし整数としてNumberに変換するため、負の値を32ビットの2の補数に変換するために使用できます。大きな数値としてのバージョン。を使用>>>0すると、0〜0xFFFFFFFFの整数が得られます。

この場合、ECMAScriptは32ビットの符号なし整数で配列インデックスを定義するため、これは便利です。したがってarray.filter、ECMAScriptの第5版の標準の内容とまったく同じ方法で実装しようとしている場合は、このように数値を32ビットのunsigned intにキャストします。

(実際にはうまくいけば、人々が設定されようとしていないとして、このために少し実用的な必要性がありますarray.length0.5-11e21または'LEMONS'あなたが知っていることはありませんので、。しかし、これは私たちが話しているJavaScriptの作者であるが...)

概要:

1>>>0            === 1
-1>>>0           === 0xFFFFFFFF          -1>>0    === -1
1.7>>>0          === 1
0x100000002>>>0  === 2
1e21>>>0         === 0xDEA00000          1e21>>0  === -0x21600000
Infinity>>>0     === 0
NaN>>>0          === 0
null>>>0         === 0
'1'>>>0          === 1
'x'>>>0          === 0
Object>>>0       === 0

(*:まあ、フロートのように振る舞うように定義されています。パフォーマンス上の理由から、JavaScriptエンジンが実際にintを使用したとしても驚くことではありません。しかし、それは実装の詳細ではありません。の利点。)


2
詳細な説明と表の+ 2、-1。array.lengthはそれ自体を検証し、整数または0以外の値に任意に設定できないため(-1は、FFがこのエラーをスローします:)RangeError: invalid array length
ジャスティンジョンソン

4
ただし、仕様では意図的に多くの配列関数を非配列(例:を介してArray.prototype.filter.call)で呼び出すことができるため、array実際にはそうではないArray可能性があります。それは、他のユーザー定義クラスである可能性があります。(残念ながら、それが確実にNodeListであるとは限りません。これは、本当にそれを実行したい場合です。これは、ホストオブジェクトであるためです。これにより、arguments疑似配列として現実的に実行できる唯一の場所が
残り

素晴らしい説明と素晴らしい例!残念ながら、これはJavaScriptのもう1つの非常識な側面です。間違った型を受け取ったときにエラーをスローすることの何がそんなに恐ろしいのか、私には理解できません。偶発的なミスをすべて行って型キャストを作成することなく、動的型付けを許可することができます。:(
マイク・ウィリアムソン

">>> 0を使用すると、0〜0xFFFFFFFFの整数が得られます。" 何だろうifステートメント評価の左側はint型ではなかったことを識別しようとすると、このためのように見えますか?'lemons'>>>0 === 0 && 0 >>>0 === 0真と評価しますか?レモンは明らかに言葉ですが..?
Zze

58

符号なし右シフト演算子は、Mozilla すべての配列extraのメソッド実装で使用され、lengthプロパティが符号なし32ビット整数であることを確認します。

length配列オブジェクトのプロパティは、仕様では次のように説明されています。

すべてのArrayオブジェクトにはlengthプロパティがあり、その値は常に2 32未満の負でない整数です。

この演算子はそれを実現するための最短の方法です。内部的に配列メソッドがToUint32操作を使用しますが、そのメソッドにはアクセスできず、実装目的で仕様に存在します。

Mozilla 配列エクストラの実装はECMAScript 5に準拠しようとしています。Array.prototype.indexOfメソッドの説明(15.4.4.14)をご覧ください。

1. thisを渡してToObjectを呼び出した結果をOとする 
   引数として。
2. lenValueをOの[[Get]]内部メソッドを呼び出した結果とする 
   引数「長さ」。
3. lenをToUint32(lenValue)にします。
....

ご覧のToUint32ように、ES3実装のES5仕様に準拠するようにメソッドの動作を再現したいだけで、前に述べたように、符号なし右シフト演算子が最も簡単な方法です。


リンクされた配列のエクストラの実装は正しい(または正しいに近い)かもしれませんが、コードはまだ悪いコード例です。おそらく、意図を明確にするコメントでさえ、この状況を解決するでしょう。
fmark

2
配列の長さが整数でない可能性はありますか?想像もできないので、こういうのToUint32はちょっと不必要に思えます。
マルセルコーペル

7
@Marcel:ほとんどのArray.prototypeメソッドは意図的にジェネリックであり配列のようなオブジェクトで使用できることに注意してArray.prototype.indexOf.call({0:'foo', 1:'bar', length: 2}, 'bar') == 1;ください。argumentsオブジェクトは、良い例です。以下のために、純粋な配列オブジェクトは、タイプ変更することは不可能だlength、彼らは特別を実装するので、プロパティを[Put ]]内部メソッド、および割り当てをした場合lengthプロパティ、再び変換されるToUint32と他の動作は上記のインデックスを削除するように、取得され新しい長さ...
CMS

32

これは、符号なし右ビットシフト演算子です。このとの間の差の符号付き右ビットシフト演算は、ということである符号なし右ビットシフト演算子(>>>は)左からゼロで埋め、そして署名された右ビットシフト演算子(>>従って、符号ビットを有する)塗りつぶしシフト時に数値の符号を保持します。


イヴァン、それはそれを0桁シフトするでしょう。そのステートメントは何も変更しません。
Dean J

3
@Ivanは、通常、値を0桁シフトしても、まったく意味がありません。しかし、これはJavascriptなので、その背後にある意味があるかもしれません。私はJavaScriptの第一人者ではありませんが、値がタイプレスのJavasacript言語で実際に整数であることを確認する方法である可能性があります。
driis 2009年

2
@イヴァン、以下のジャスティンの答えを見てください。実際には、len変数に数値が含まれていることを確認する方法です。
driis 2009年

1
さらに、>>>単項で+は行われない整数に変換します。
再帰的

this.length >>> 0は、符号付き整数を符号なし整数に変換します。個人的には、署名されていないintが含まれているバイナリファイルを読み込むときに、これが便利であることがわかりました。
Matt Parkins、2013

29

Driisは、オペレーターとは何か、オペレーターが何をするかについて十分に説明しています。その背後にある意味/使用された理由は次のとおりです。

doによって任意の方向にシフトする0と、元の数が返され、にキャストさnull0ます。あなたが見ているサンプルコードは、定義されていなくても数値であるthis.length >>> 0ことを確認するために使用しているようです。 lenthis.length

多くの人にとって、ビットごとの操作は不明確です(そして、ダグラス・クロックフォード/ jslintはそのようなものの使用を勧めていません)。それが間違っているという意味ではありませんが、コードを読みやすくするためのより好都合で使い慣れた方法が存在します。ことを確認するために、より明確な方法lenでは、0次の2つの方法のいずれかです。

// Cast this.length to a number
var len = +this.length;

または

// Cast this.length to a number, or use 0 if this.length is
// NaN/undefined (evaluates to false)
var len = +this.length || 0; 

1
、あなたの第二の溶液は時々に評価しますがNaN.. Egが+{}:...それは、2つ組み合わせることがおそらく最善です+length||0
ジェームズ・

1
this.lengthは配列オブジェクトのコンテキスト内にあり、これは負ではない整数(少なくともFFでは)以外にはなり得ないため、ここでは可能ではありません。また、{} || 1は{}を返すため、this.lengthがオブジェクトである場合は、これ以上の利点はありません。最初のメソッドでthis.lengthを単項キャストする利点は、this.lengthがNaNであるケースを処理できることです。それを反映するように編集された応答。
ジャスティンジョンソン

jslintは、var len = + this.lengthについても「紛らわしいプラス」として文句を言うでしょう。ダグラス、あなたはとてもうるさい!
Bayard Randel、2010

ダグラスはうるさいです。そして、彼の議論は賢明であり、通常は根拠があるものですが、彼の言うことは絶対的でも福音でもありません。
ジャスティンジョンソン

15

>>>ある符号なし右シフト演算は、JavaScriptの1.5仕様の、P。76を参照のとは対照的に)>>署名された右シフト演算子。

>>>シフト時に符号ビットを保持しないため、負の数をシフトした結果を変更します。この結果は、例によって、インタプリタから理解できます。

$ 1 >> 0
1
$ 0 >> 0
0
$ -1 >> 0
-1
$ 1 >>> 0
1
$ 0 >>> 0
0
$ -1 >>> 0
4294967295
$(-1 >>> 0).toString(16)
"ffffffff"
$ "cabbage" >>> 0
0

したがって、おそらくここで行うことを意図しているのは、長さを取得すること、または"cabbage"上記の例のように、長さが定義されていないか整数でない場合は0を取得することです。この場合、それはthis.length決して起こらないと仮定しても安全だと思います< 0。それにもかかわらず、この例は 2つの理由で厄介なハックあると主張します。

  1. <<<負の数を使用する場合の動作、上記の例ではおそらく意図しない(または発生する可能性が高い)副作用。

  2. この質問の存在が確認するように、コードの意図は明白はありません

パフォーマンスが絶対に重要でない限り、ベストプラクティスはおそらくもっと読みやすいものを使用することです。

isNaN(parseInt(foo)) ? 0 : parseInt(foo)

すごい... @ johncatfishは正しいですか?this.lengthが負でないことを確認するためですか?
Anthony

4
このようなケースが-1 >>> 0発生する可能性はありますか?このようにすると、ループが必要以上に数回実行されます。
だます

@deceze:実装を見ずthis.lengthに知ることは不可能です。「健全な」実装では、文字列の長さが負になることはありませんが、「健全な」環境ではthis.length常に整数を返すプロパティの存在を想定できると主張する人もいます。
fmark

>>>は符号ビットを保持しないと言います..わかりました。したがって、負の数を処理するときは、>>>または>>変換の前に、それらが2秒の適合であるかどうか尋ねる必要があります形式、またはそれらは符号付き整数形式であり、どのように知ることができますか?ちなみに、2sが、私は多分...それが署名した表記法に代わるものだが、整数の符号を決定することが可能である符号ビットを持っていると言わないと思う補完
barlop

10

2つの理由:

  1. >>>の結果は「積分」です

  2. undefined >>> 0 = 0(JSはLFSを数値コンテキストに変換しようとするため、これは "foo" >>> 0などでも機能します)

JSの数値はdoubleの内部表現を持つことに注意してください。これは、長さの基本的な入力健全性の「迅速な」方法にすぎません。

ただし、-1 >>> 0(おっと、おそらく望ましい長さではありません!)


0

以下のサンプルJavaコードはよく説明しています:

int x = 64;

System.out.println("x >>> 3 = "  + (x >>> 3));
System.out.println("x >> 3 = "  + (x >> 3));
System.out.println(Integer.toBinaryString(x >>> 3));
System.out.println(Integer.toBinaryString(x >> 3));

出力は次のとおりです。

x >>> 3 = 536870904
x >> 3 = -8
11111111111111111111111111000
11111111111111111111111111111000
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.