javascript - チルダが式の前に置かれるとどうなりますか?
var attr = ~'input,textarea'.indexOf( target.tagName.toLowerCase() )
? 'value'
: 'innerHTML'
それをある答えで見ましたが、今まで見たことがありません。
それはどういう意味ですか?
回答 #1
~ は、オペランド内のすべてのビットを反転させるビット単位の演算子です。
例えば、もしあなたの数字が1であれば、IEEE 754 フロート(JavaScriptが数値を扱う方法)の二進表現は…
0011 1111 1111 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
だから ~ はそのオペランドを 32 ビット整数に変換します(JavaScript のビット演算子はそうします)...
0000 0000 0000 0000 0000 0000 0000 0001
もしそれが負の数であれば、2の補数で保存されます:すべてのビットを反転させて1を加えます。
...そしてすべてのビットを反転させる...
1111 1111 1111 1111 1111 1111 1111 1110
それでは、それは何の役に立つのですか?一体いつそれを使うことがあるのでしょうか?
それにはかなり多くの用途があります。低レベルのことを書く場合には便利です。もしアプリケーションをプロファイリングしてボトルネックを見つけた場合、ビット演算のトリックを使うことでパフォーマンスを向上させることができるかもしれません(はるかに大きなツールセットの中のひとつの可能な手段として)。
これはまた、indexOf() の found の返り値を truthy に変え(not found を falsy にする)、さらに数字を 32 ビットに切り捨て(小数点を切り捨てて 2 倍することで、正の数に対しては Math.floor() と実質的に同じになる)という副作用のためによく使われる、(一般的に)あいまいな trick でもあります。
私は不明確と言います、なぜならそれが何に使われているのかがすぐには明らかではないからです。一般的に、あなたのコードはそれを読む他の人々に明確に伝わることが望ましいです。~を使うことはかっこよく見えるかもしれませんが、一般的にはあまりにも巧妙すぎます。:)
JavaScriptにArray.prototype.includes()やString.prototype.includes()がある今では、これはあまり重要ではありません。これらはブール値を返します。もし対象のプラットフォームがこれをサポートしているなら、文字列や配列の中に値が存在するかをテストする場合、これを使うほうが望ましいです。
回答 #2
indexOf() 式の前にそれを使うと、直接返される数値インデックスの代わりに、真偽値の結果が得られます。
もし戻り値が-1なら、-1はすべてのビットが1の文字列なので、~-1は0になります。0以上の値はどれも非ゼロの結果を返します。したがって、
if (~someString.indexOf(something)) {
}
"something" が "someString" に含まれている場合、if のコードが実行されます。.indexOf() を直接ブール値として使おうとすると、文字列の先頭に "something" がある場合に 0 を返すことがあるため、うまく動作しません。
もちろん、これも使えます:
if (someString.indexOf(something) >= 0) {
}
そして、それはかなり謎めいていません。
時々、これも目にすることがあります。
var i = ~~something;
そのように ~ 演算子を2回使うことは、文字列を32ビット整数に変換する簡単な方法です。最初の ~ が変換を行い、2回目の ~ がビットを元に戻します。もちろん、演算子が数値に変換できないものに適用されると、結果として NaN が得られます。(編集 — 実際には最初に適用されるのは2回目の ~ ですが、意味は分かると思います。)
回答 #3
~ はビット単位のNOT演算子で、~x はおおよそ -(x+1) と同じです。少しわかりやすく言うと、こんな感じです:
~2; // -(2+1) ==> -3
-(x+1)を考えてください。-1はその操作を行い、0を生成できます。
言い換えると、~ は数値の範囲で使用されると、入力値が -1 の場合のみ偽の値(0 から false に強制変換される)を生成し、それ以外の値では他の真の値を生成します。
ご存知のように、-1 は一般的に センチネル値 と呼ばれます。C言語では、多くの関数で 成功 の場合に >= 0 の値を返し、失敗 の場合に -1 を返すために使用されます。これは、JavaScript の indexOf() の返り値の規則と同じです。
このようにして、ある文字列の中に部分文字列が存在するかどうかを確認することは一般的です
var a = "Hello Baby";
if (a.indexOf("Ba") >= 0) {
// found it
}
if (a.indexOf("Ba") != -1) {
// found it
}
if (a.indexOf("aB") < 0) {
// not found
}
if (a.indexOf( "aB" ) == -1) {
// not found
}
しかし、以下のように ~ を通じて行う方が簡単でしょう
var a = "Hello Baby";
~a.indexOf("Ba"); // -7 -> truthy
if (~a.indexOf("Ba")) { // true
// found it
}
~a.indexOf("aB"); // 0 -> falsy
!~a.indexOf("aB"); // true
if (!~a.indexOf( "aB" )) { // true
// not found
}
あなたはJSを知らない: タイプと文法 カイル・シンプソン著
回答 #4
~indexOf(item) はよく出てきますし、ここでの回答も素晴らしいですが、ただ使い方を知っていて理論を「飛ばしたい」人もいるかもしれません:
if (~list.indexOf(item)) {
// item in list
} else {
// item *not* in list
}
回答 #5
indexOfの結果からtruthyな値を作るためにチルダのトリックを使おうと考えている人にとって、代わりにStringのincludesメソッドを使う方が、より明示的で魔法が少ないです。
'hello world'.includes('hello') //=> true
'hello world'.includes('kittens') //=> false
これは ES 2015 の新しい標準的な方法であるため、古いブラウザでは動作しないことに注意してください。それが問題になる場合は、String.prototype.includes のポリフィルを使用することを検討してください。
この機能は、同じ構文を使って配列でも利用可能です。
['apples', 'oranges', 'cherries'].includes('apples') //=> true
['apples', 'oranges', 'cherries'].includes('unicorns') //=> false
もし古いブラウザのサポートが必要な場合、こちらが Array.prototype.includes のポリフィルです。
