JavaScript >>> operatörü nedir ve onu nasıl kullanıyorsunuz?


154

Array'e filtre yöntemi ekleyen Mozilla koduna bakıyordum ve kafamı karıştıran bir kod satırı vardı.

var len = this.length >>> 0;

Daha önce JavaScript'te >>> kullanıldığını hiç görmemiştim.
Nedir ve ne işe yarar?


@CMS Doğru, bu kod / soru onlardan geliyor; ancak, buradaki yanıt öncekilerden daha spesifik ve değerlidir.
Justin Johnson

2
Veya bu bir hata veya Mozilla adamları bunu varsayıyor. Uzunluk -1 olabilir. >>> işaretsiz kaydırma operatörüdür, bu nedenle değişken len her zaman 0 veya daha büyük olacaktır.
user347594

1
Ash Searle bunun için bir kullanım buldu - JS efendisinin (Doug Crockford) uygulamasını Array.prototype.push/ Array.prototype.pop- hexmen.com/blog/2006/12/push-and-pop'a çevirerek (testleri o yapmış olsa da, haha).
Dan Beam

Yanıtlar:


216

Yalnızca Sayı olmayanları Sayıya dönüştürmekle kalmaz, aynı zamanda onları 32 bitlik işaretsiz tams olarak ifade edilebilen Sayılara dönüştürür.

JavaScript'in Sayılar çift duyarlıklı yüzer (*), bit operatörleri olmasına rağmen ( <<, >>, &, |ve ~) 32 bit tamsayılar üzerinde operasyonlar açısından tanımlanır. Bitsel işlem yapmak, sayıyı 32 bitlik işaretli int'e dönüştürür, hesaplamayı yapmadan ve ardından tekrar Sayı'ya dönüştürmeden önce tüm kesirleri ve 32'den daha yüksek basamaklı bitleri kaybeder.

Bu nedenle, 0 bitlik sağa kaydırma gibi gerçek bir etkisi olmayan bit düzeyinde bir işlem yapmak, >>0bir sayıyı yuvarlamanın ve 32 bitlik aralıkta olmasını sağlamanın hızlı bir yoludur. Ek olarak, üçlü >>>operatör, işaretsiz işlemini yaptıktan sonra, hesaplamasının sonuçlarını, diğerlerinin yaptığı işaretli tamsayı yerine işaretsiz bir tam sayı olarak Sayı'ya dönüştürür, böylece negatifleri 32-bit-ikinin-tamamlayıcısına dönüştürmek için kullanılabilir. büyük bir Sayı olarak sürüm. Kullanmak >>>0, 0 ile 0xFFFFFFFF arasında bir tam sayıya sahip olmanızı sağlar.

Bu durumda bu yararlıdır çünkü ECMAScript, Array dizinlerini 32 bitlik işaretsiz ints cinsinden tanımlar. Dolayısıyla array.filter, ECMAScript Fifth Edition standardının söylediği şeyi tam olarak kopyalayacak şekilde uygulamaya çalışıyorsanız , sayıyı 32-bit işaretsiz int'e bu şekilde çevirirsiniz.

(Gerçekte küçük pratik şuna gerek yoktur umarım insanlar olmamız gidiş değildir kadar array.lengthiçin 0.5, -1, 1e21veya 'LEMONS'. Ama hiç belli olmaz yani bu, Bahsettiğimiz JavaScript yazarlar ise ...)

Özet:

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

(*: yüzer gibi davranıyor olarak tanımlanıyorlar. Bazı JavaScript motorlarının, yapabildiğinde performans nedenlerinden ötürü gerçekten ints kullanması beni şaşırtmaz. Avantajı.)


2
Derinlemesine açıklama ve tablo +2, -1 çünkü dizi.length kendini doğrular ve bir tamsayı veya 0 olmayan herhangi bir şeye keyfi olarak ayarlanamaz (FF bu hatayı atar :) RangeError: invalid array length.
Justin Johnson

4
Bununla birlikte, özellik kasıtlı olarak birçok Array işlevinin Array olmayan (örneğin aracılığıyla Array.prototype.filter.call) çağrılmasına izin verir , bu nedenle arrayaslında gerçek Arrayolmayabilir: başka bir kullanıcı tanımlı sınıf olabilir. (Ne yazık ki, güvenilir bir şekilde bir NodeList olamaz, ki bunu gerçekten yapmak isteyeceğiniz zaman, çünkü bu bir ana bilgisayar nesnesi. Bunu gerçekçi bir şekilde argumentssözde Array olarak yapabileceğiniz tek yer bırakıyor .)
bobince

Harika açıklama ve harika örnekler! Maalesef bu Javascript'in başka bir çılgın yönü. Yanlış tip aldığında hata yapmanın neyin korkunç olduğunu anlamıyorum. Her yanlışlıkla yapılan hatanın bir tür ataması oluşturmasına izin vermeden dinamik yazmaya izin vermek mümkündür. :(
Mike Williamson

">>> 0 kullanmak, 0 ile 0xFFFFFFFF arasında bir tam sayıya sahip olmanızı sağlar." Ne olur ifaçıklamada değerlendirmenin sol tarafı bir int olmadığını belirlemeye çalışırken bunun için böyle görünür? 'lemons'>>>0 === 0 && 0 >>>0 === 0doğru olarak değerlendiriliyor? limon açıkça bir kelime olsa bile ..?
Zze

58

İşaretsiz sağa kaydırma operatörü, özelliğin işaretsiz 32 bitlik bir tamsayı olmasını sağlamak için, Mozilla dizisinin tüm ekstra yöntem uygulamalarında kullanılır .length

lengthDizi nesnelerin özelliği olduğu genel olarak tarif edilen tarifnamede olarak:

Her Array nesnesinin, değeri her zaman 2 32'den küçük negatif olmayan bir tam sayı olan bir length özelliği vardır .

Bu operatör, bunu başarmanın en kısa yoludur, dahili olarak dizi yöntemleri işlemi kullanır ToUint32, ancak bu yöntem erişilebilir değildir ve uygulama amaçları için belirtimde mevcuttur.

Mozilla dizi ekstraları uygulamaları ECMAScript 5 uyumlu olmaya çalışır , Array.prototype.indexOfyöntemin açıklamasına bakın (§ 15.4.4.14):

1. ToObject çağrısının bu değeri geçmesinin sonucu O olsun 
   argüman olarak.
2. lenValue, O'nun [[Get]] dahili yöntemini çağırmanın sonucu olsun. 
   "uzunluk" argümanı.
3. len ToUint32 (lenValue) olsun .
....

Gördüğünüz gibi, ToUint32ES3 uygulamasında ES5 spesifikasyonuna uymak için yöntemin davranışını yeniden oluşturmak istiyorlar ve daha önce de söylediğim gibi, imzasız sağa kaydırma operatörü en kolay yoldur.


Bağlı dizi ekstraları uygulaması doğru (veya düzeltmeye yakın) olsa da, kod yine de kötü bir kod örneğidir. Belki niyeti açıklığa kavuşturacak bir yorum bile bu durumu çözebilir.
fmark

2
Bir dizinin uzunluğunun bir tamsayı olmaması mümkün müdür ? Bunu hayal edemiyorum, bu yüzden bu tür ToUint32bana biraz gereksiz görünüyor.
Marcel Korpel

7
@Marcel: Array.prototypeYöntemlerin çoğunun kasıtlı olarak jenerik olduğunu , örneğin dizi benzeri nesnelerde kullanılabileceğini unutmayın Array.prototype.indexOf.call({0:'foo', 1:'bar', length: 2}, 'bar') == 1;. argumentsNesne aynı zamanda iyi bir örnektir. İçin saf dizi nesneler, bu türünü değiştirmek imkansız lengthonlar special uygulamak çünkü, mülkiyet [[Put bir atama yapılır]] dahili yöntem ve lengthmülkiyet, yeniden dönüştürülmüş olan ToUint32ve diğer eylemler yukarıdaki indeksler silme gibi alınır yeni uzunluk ...
Christian C. Salvadó

33

Bu, işaretsiz sağ bit kaydırma operatörüdür. Bununla işaretli sağ bit kaydırma operatörü arasındaki fark , işaretsiz sağ bit kaydırma operatörünün ( >>> ) soldan sıfırlarla doldurması ve işaretli sağ bit kaydırma operatörünün ( >> ) işaret biti ile doldurmasıdır. kaydırıldığında sayısal değerin işaretini korumak.


Ivan, bu onu 0 sıra değiştirirdi; bu ifade hiçbir şeyi değiştirmez.
Dean J

3
@Ivan, normalde, bir değeri sıfır basamak kaydırmanın kesinlikle bir anlam ifade etmediğini söyleyebilirim. Ama bu Javascript, bu yüzden arkasında bir anlam olabilir. Ben bir Javascript uzmanı değilim, ancak değerin aslında yazısız Javasacript dilinde bir tamsayı olmasını sağlamanın bir yolu olabilir.
driis

2
@Ivan, aşağıdaki Justin'in cevabına bakın. Aslında bu, len değişkeninin bir sayı içermesini sağlamanın bir yoludur.
driis

1
Ayrıca, >>>tek terimli +olmayan bir tam sayıya dönüştürür .
özyinelemeli

this.length >>> 0, işaretli bir tamsayıyı işaretsiz bir tamsayıyı dönüştürür. Şahsen ben bunu, içinde imzalanmamış girişler içeren bir ikili dosya yüklerken faydalı buldum.
Matt Parkins

29

Driis , operatörün ne olduğunu ve ne yaptığını yeterince açıkladı. İşte arkasındaki anlam / neden kullanıldığı:

Tarafından herhangi bir yöne kaydırmak 0döndürür orijinal sayı yapar ve döküm olacak nullkadar 0. Görünüşe göre baktığınız örnek kod, tanımlanmamış olsa bile bunun sayısal this.length >>> 0olmasını sağlamak için kullanıyor . lenthis.length

Pek çok insan için bitsel işlemler belirsizdir (ve Douglas Crockford / jslint bu tür şeylerin kullanılmamasını önerir). Yapmanın yanlış olduğu anlamına gelmez, ancak kodu daha okunaklı hale getirmek için daha uygun ve tanıdık yöntemler vardır. Sağlamak için daha net bir yol lenolduğunu 0aşağıdaki iki yöntemden birini olduğunu.

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

veya

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

1
Yine de, ikinci çözümünüz bazen şu şekilde değerlendirilebilir: NaNÖrneğin +{}... İkisini birleştirmek muhtemelen en iyisidir:+length||0
James

1
this.length, negatif olmayan bir tam sayıdan başka bir şey olamayan (en azından FF'de) dizi nesnesi bağlamındadır, bu nedenle burada bir olasılık değildir. Ayrıca, {} || 1, {} değerini döndürür, böylece this.length bir nesneyse daha iyi durumda olmazsınız. Birinci yöntemde this.length öğesinin tekli dökümünün yararı, this.length değerinin NaN olduğu durumları ele almasıdır. Bunu yansıtmak için düzenlenmiş yanıt.
Justin Johnson

jslint, var len = + this.length hakkında da "kafa karıştırıcı artılar" olarak şikayet ederdi. Douglas, çok seçicisin!
Bayard Randel

Douglas seçici. Ve argümanları bilge ve tipik olarak sağlam temellendirilse de, söyledikleri mutlak veya müjde değildir.
Justin Johnson

15

>>>olan işaretsiz sağa kaydırma operatörü ( bkz. JavaScript 1.5 şartnamenin 76 ), karşıt olarak >>, imza sağ shift operatör.

>>>negatif sayıları kaydırmanın sonuçlarını değiştirir çünkü kaydırma sırasında işaret bitini korumaz . Bunun sonuçları, bir tercümandan örnekle anlaşılabilir:

$ 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

Dolayısıyla, muhtemelen burada yapılması amaçlanan, "cabbage"yukarıdaki örnekte olduğu gibi uzunluğu veya uzunluk tanımsızsa veya bir tam sayı değilse 0'ı elde etmektir . Sanırım bu durumda bunun this.lengthasla olmayacağını varsaymak güvenli < 0. Yine de, bu örneğin iki nedenden ötürü çirkin bir hack olduğunu iddia ediyorum :

  1. <<<Negatif sayılar kullanıldığında, yukarıdaki örnekte muhtemelen amaçlanmayan (veya meydana gelmesi muhtemel) bir yan etkinin davranışı .

  2. Bu sorunun varlığının da doğruladığı gibi , kodun amacı açık değildir .

En iyi uygulama, performans kesinlikle kritik olmadığı sürece muhtemelen daha okunabilir bir şey kullanmaktır:

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

Sooo ... @johncatfish doğru mu? Bu uzunluğun negatif olmamasını sağlamak için mi?
Anthony

4
Böyle bir durum -1 >>> 0olabilir mi ve eğer öyleyse, 4294967295'e kaydırmak gerçekten arzu edilir mi? Görünüşe göre bu, döngünün gerekenden birkaç kez daha fazla çalışmasına neden olacak.
cehenneme

@deceze: Uygulamasını görmeden this.lengthbilmek imkansız. Herhangi bir "mantıklı" uygulama için, bir dizenin uzunluğu asla negatif olmamalıdır, ancak o zaman biri "mantıklı" bir ortamda this.lengthher zaman bir integral sayı döndüren bir özelliğin varlığını varsayabileceğimizi iddia edebilir .
fmark

>>> işaret bitini korumaz diyorsunuz .. tamam .. Yani, negatif sayılarla uğraşırken sormam gerekir .. herhangi bir >>> veya >> dönüşümden önce, bunlar 2s uyumluluğunda mı formda mı yoksa imzalı tamsayı formunda mı ve nasıl bilebiliriz? Bu arada, sanırım 2s tamamlayıcı bir işaret biti olduğu söylenmiyor ... bu, işaretli gösterime bir alternatif, ancak bir tamsayının işaretini belirlemek mümkün
barlop

10

İki sebep:

  1. >>> sonucu bir "integral" dir

  2. tanımsız >>> 0 = 0 (JS, LFS'yi sayısal bağlama zorlayacağından, bu "foo" >>> 0 vb. için de çalışacaktır)

JS'deki sayıların çiftin dahili temsiline sahip olduğunu unutmayın. Bu, uzunluk için temel girdi mantığının "hızlı" bir yoludur.

Bununla birlikte , -1 >>> 0 (oops, muhtemelen istenen uzunluk değil!)


0

Aşağıdaki örnek Java Kodu iyi açıklıyor:

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

Çıktı aşağıdaki gibidir:

x >>> 3 = 536870904
x >> 3 = -8
11111111111111111111111111000
11111111111111111111111111111000
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.