++ [[]] [+ []] + [+ []] neden “10” dizesini döndürüyor?


1657

Bu geçerlidir ve dizeyi "10"JavaScript'te döndürür ( burada daha fazla örnek ):

console.log(++[[]][+[]]+[+[]])

Neden? Burada ne oluyor?


446
Başlamak +[]için boş bir dizi attığını anlayarak başlayın 0... sonra bir öğleden sonra israf ...;)
deceze


10
Wtfjs.com'a bir göz atın - explanatinos ile bunun gibi birkaç şey var.
ThiefMaster

3
@deceze, bu tür şeyleri nereden öğreniyorsun? Hangi kitaplar?
JS'yi MDN'den

6
@SiddharthThevaril Az önce yaptığınız gibi: birisi bu konuda bir yere gönderdi ve ben de okudum.
deceze

Yanıtlar:


2070

Eğer onu bölersek, karışıklık eşittir:

++[[]][+[]]
+
[+[]]

JavaScript'te bu doğrudur +[] === 0. +bir şeyi bir sayıya dönüştürür ve bu durumda +""ya da aşağıya iner 0(aşağıdaki özellik ayrıntılarına bakın).

Bu nedenle, basitleştirebiliriz ( ++önceliğe sahiptir +):

++[[]][0]
+
[0]

Çünkü şu [[]][0]anlama gelir: ilk elementi almak [[]], doğrudur:

[[]][0]iç diziyi ( []) döndürür . Referanslar nedeniyle söylemek yanlıştır [[]][0] === [], ancak Ayanlış gösterimi önlemek için iç diziyi arayalım .

++işleneninden önce “bir artış ve artan sonucu döndür” anlamına gelir. Yani (veya ) ++[[]][0]ile eşdeğerdir .Number(A) + 1+A + 1

Yine, karışıklığı daha okunaklı bir şey haline getirebiliriz. Hadi yerine []geri A:

(+[] + 1)
+
[0]

+[]Diziyi sayıya zorlamak için önce 0bir dizeye zorlanması gerekir, yani "", yine. Son olarak, 1eklenir, sonuçlanır 1.

  • (+[] + 1) === (+"" + 1)
  • (+"" + 1) === (0 + 1)
  • (0 + 1) === 1

Daha da basitleştirelim:

1
+
[0]

Ayrıca, JavaScript'te bu doğrudur: [0] == "0"çünkü bir öğeyi bir öğeyle birleştiriyor. Birleştirme, ile ayrılmış öğeleri birleştirir ,. Bir elemanla, bu mantığın ilk elemanın kendisiyle sonuçlanacağını çıkarabilirsiniz.

Bu durumda, +iki işlenen görür: bir sayı ve bir dizi. Şimdi ikisini aynı tipte zorlamaya çalışıyor. İlk olarak, dizi dizeye zorlanır "0", sonra sayı bir dizeye ( "1") zorlanır . Sayı +Dize ===Dize .

"1" + "0" === "10" // Yay!

İçin şartname detayları +[]:

Bu oldukça bir labirent, ama yapmak için +[]önce bir dizeye dönüştürülüyor çünkü +şöyle diyor:

11.4.6 Tekli + Operatör

Unary + operatörü işlenenini Sayı tipine dönüştürür.

UnaryExpression üretimi: + UnaryExpression şu şekilde değerlendirilir:

  1. İfadenin UnaryExpression değerlendirmesinin sonucu olmasına izin verin.

  2. DönüşNumarası (GetValue (ifade)).

ToNumber() diyor:

Nesne

Aşağıdaki adımları uygulayın:

  1. PrimValue'nun ToPrimitive olmasına izin verin (giriş argümanı, ipucu String).

  2. Eğime Dön (primValue).

ToPrimitive() diyor:

Nesne

Nesne için varsayılan bir değer döndürür. Bir nesnenin varsayılan değeri, isteğe bağlı ipucu PreferredType iletilerek nesnenin [[DefaultValue]] dahili yöntemi çağrılarak alınır. [[DefaultValue]] dahili yönteminin davranışı, 8.12.8'deki tüm yerel ECMAScript nesneleri için bu belirtim ile tanımlanır.

[[DefaultValue]] diyor:

8.12.8 [[Varsayılan Değer]] (ipucu)

İpucu String ile [[DefaultValue]] dahili O yöntemi çağrıldığında aşağıdaki adımlar uygulanır:

  1. ToString, "toString" argümanıyla O nesnesinin [[Get]] dahili yöntemini çağırmanın sonucu olsun.

  2. IsCallable (toString) true ise,

a. Str, [[Call]] toString iç yöntemini çağırmanın sonucu olsun, bu değer O ve boş bir argüman listesi.

b. Str ilkel bir değerse str.

.toStringBir dizinin diyor ki:

15.4.4.2 Array.prototype.toString ()

ToString yöntemi çağrıldığında aşağıdaki adımlar uygulanır:

  1. Dizi bu değerde ToObject çağrısının sonucu olsun.

  2. Func, "join" argümanıyla dizinin [[Get]] dahili yöntemini çağırmanın sonucu olsun.

  3. IsCallable (func) yanlışsa, func'un standart yerleşik Object.prototype.toString (15.2.4.2) yöntemi olmasına izin verin.

  4. Bu değer ve boş bir bağımsız değişkenler listesi olarak dizi sağlama işlevinin [[Call]] dahili yöntemini çağırmanın sonucunu döndürün.

Bu yüzden +[]aşağıya iner +"", çünkü [].join() === "".

Yine, şu +şekilde tanımlanır:

11.4.6 Tekli + Operatör

Unary + operatörü işlenenini Sayı tipine dönüştürür.

UnaryExpression üretimi: + UnaryExpression şu şekilde değerlendirilir:

  1. İfadenin UnaryExpression değerlendirmesinin sonucu olmasına izin verin.

  2. DönüşNumarası (GetValue (ifade)).

ToNumberşu şekilde tanımlanır "":

StringNumericLiteral ::: [empty] öğesinin MV'si 0'dır.

Yani +"" === 0, ve böylece +[] === 0.


8
@ harper: Bu katı bir eşitlik denetleyicisidir, yani yalnızca truedeğer ve tür aynı ise geri döner . 0 == ""döner true(tip dönüşüm sonra aynı), ancak 0 === ""bir false(değil aynı tipleri).
pimvdb

41
Bunun bir kısmı doğru değil. Prefix ( ) operatörü her zaman bir sayı döndürdüğünden 1 + [0], ifade değil değerine kadar kaynar . Bkz. Bclary.com/2004/11/07/#a-11.4.4"1" + [0]++
Tim Down

6
@Tim Down: Tamamen haklısın. Bunu düzeltmeye çalışıyorum ama bunu yapmaya çalışırken başka bir şey buldum. Bunun nasıl mümkün olduğundan emin değilim. ++[[]][0]gerçekten döner 1, ancak ++[]bir hata atar. Bu kayda değer çünkü ++[[]][0]kaynıyor gibi görünüyor ++[]. Belki de neden ++[]hata verirken bir fikriniz var ++[[]][0]mı?
pimvdb

11
@pimvdb: Sorunun önek işleminde PutValue(ES3 terminolojisinde, 8.7.2'de) olduğundan eminim . PutValueoysa bir başvuru gerektiren []bir başvuru üretmez kendi başına bir ifadesi olarak. Bir nesnenin (örneğin ) bir değişken referansı (daha önce tanımladığımızı var a = []sonra çalışırız ++a) veya özellik erişimini içeren bir ifade [[]][0]Referans üretir. Daha basit terimlerle, önek operatörü sadece bir değer üretmekle kalmaz, aynı zamanda bu değeri koymak için bir yere de ihtiyaç duyar.
Tim Down

13
@pimvdb: Yani yürüttükten sonra var a = []; ++a, ayürüttükten sonra 1'dir ++[[]][0]yarattığı dizi, [[]]ifade şimdi olduğu index 0 dan sadece sayı 1 içerir ++Bunu yapmak için bir başvuru gerektirir.
Tim Down

123
++[[]][+[]] => 1 // [+[]] = [0], ++0 = 1
[+[]] => [0]

Sonra bir dize birleşimimiz var

1+[0].toString() = 10

7
Yazmak ===yerine daha net olmaz mıydı =>?
Mateen Ulhaq

61

Aşağıdakiler, bu soru hala kapalıyken gönderdiğim soruyu cevaplayan bir blog gönderisinden uyarlanmıştır . ECMAScript 3 spesifikasyonunun bağlantıları (HTML kopyası), hala günümüzde yaygın olarak kullanılan web tarayıcılarında JavaScript'in temelini oluşturmaktadır.

İlk olarak, bir yorum: bu tür bir ifade hiçbir zaman (aklı başında) herhangi bir üretim ortamında ortaya çıkmayacak ve sadece okuyucunun JavaScript'in kirli kenarlarını ne kadar iyi bildiğine dair bir alıştırma olarak kullanılmayacaktır. JavaScript operatörlerinin türler arasında dolaylı olarak dönüştürme genel ilkesi, bazı yaygın dönüşümler gibi yararlıdır, ancak bu durumda ayrıntıların çoğu değildir.

İfade ++[[]][+[]]+[+[]]başlangıçta oldukça heybetli ve belirsiz görünebilir, ancak aslında ayrı ifadelere nispeten kolay ayrılır. Aşağıda netlik için parantez ekledim; Hiçbir şeyi değiştirmediklerinden emin olabilirim, ancak bunu doğrulamak istiyorsanız, gruplama operatörü hakkında okumaktan çekinmeyin . Yani, ifade daha açık bir şekilde yazılabilir

( ++[[]][+[]] ) + ( [+[]] )

Bunu parçalayarak, bunun +[]değerlendirildiğini gözlemleyerek basitleştirebiliriz 0. Bunun nedeni ile, kendini tatmin kontrol etmek tekli + operatörü ile biter hafifçe dolambaçlı izi takip ToPrimitive sonra nihayet dönüştürülür boş bir dize içine boş diziyi dönüştürerek 0tarafından ToNumber . Artık aşağıdakilerin 0her birinin yerine geçebiliriz +[]:

( ++[[]][0] ) + [0]

Çok daha basit. Gelince ++[[]][0], bu bir kombinasyonu önek değerli arttırma ( ++), bir Düz dizi kendisi boş bir dizi (olup tek bir eleman olan bir dizi tanımlama [[]]) ve bir özelliği erişimci ( [0]Düz Dizi ile tanımlanan dizi olarak da bilinir).

Yani, [[]][0]sadece basitleştirebiliriz []ve var ++[], değil mi? Aslında, durum böyle değildir çünkü değerlendirme ++[], başlangıçta kafa karıştırıcı görünebilecek bir hata atar. Bununla birlikte, bunun doğası hakkında biraz düşünmek ++bunu açıklığa kavuşturur: bir değişkeni (örneğin ++i) veya bir nesne özelliğini (örn ++obj.count. ) Arttırmak için kullanılır . Sadece bir değeri değerlendirmekle kalmaz, aynı zamanda bu değeri bir yerde saklar. Bu durumda, ++[]güncellenecek bir nesne özelliğine veya değişkene başvuru olmadığından, yeni değeri koymak için hiçbir yere sahip değildir (her ne olursa olsun). Spesifik olarak, bu, ön ek artış operatörü tarafından çağrılan dahili PutValue işlemi tarafından kapsanır .

O zaman ne yapar ++[[]][0]? +[]İç diziye benzer bir mantıkla dönüştürülür 0ve bu değer 1bize son değerini vermek için arttırılır 1. 0Dış dizideki özelliğin değeri olarak güncelleştirilir 1ve tüm ifade olarak değerlendirilir 1.

Bu bizi

1 + [0]

... ekleme operatörünün basit kullanımıdır . Her iki işlenen ilk olarak ilkellere dönüştürülür ve ilkel değerlerden biri dize ise dize birleştirme gerçekleştirilir, aksi takdirde sayısal ekleme yapılır. [0]dönüştürür "0", böylece dize birleştirme kullanılır, üreten "10".

Son olarak, hemen belirgin olmayabilecek bir şey, ya toString()da valueOf()yöntemlerinden birini geçersiz kılmanın Array.prototypeifadenin sonucunu değiştireceğidir, çünkü bir nesne ilkel bir değere dönüştürülürken her ikisi de kontrol edilir ve kullanılır. Örneğin, aşağıdakiler

Array.prototype.toString = function() {
  return "foo";
};
++[[]][+[]]+[+[]]

... üretir "NaNfoo". Bunun neden okuyucu için bir egzersiz olarak kaldığı ...


24

Basitleştirelim:

++[[]][+[]]+[+[]] = "10"

var a = [[]][+[]];
var b = [+[]];

// so a == [] and b == [0]

++a;

// then a == 1 and b is still that array [0]
// when you sum the var a and an array, it will sum b as a string just like that:

1 + "0" = "10"

13

Bu aynı ama biraz daha küçük olarak değerlendirilir

+!![]+''+(+[])
  • [] - bir toplama eklediğinizde veya çıkardığınızda 0'a dönüştürülen bir dizidir, dolayısıyla + [] = 0
  • ! [] - yanlış olarak değerlendirilir, dolayısıyla !! [] doğru olarak değerlendirir
  • + !! [] - true değerini true olarak değerlendiren sayısal bir değere dönüştürür, bu durumda bu durumda 1
  • + '' - ifadeye boş bir dize ekleyerek sayının dizeye dönüştürülmesine neden olur
  • + [] - 0 olarak değerlendirilir

değerlendirir

+(true) + '' + (0)
1 + '' + 0
"10"

Şimdi anladınız, bunu deneyin:

_=$=+[],++_+''+$

Hayır, hala "10" olarak değerlendiriliyor. Ancak bu farklı bir şekilde yapıyor. Bunu krom veya benzeri bir javascript denetçisinde değerlendirmeyi deneyin.
Vlad Shlosberg

_ = $ = + [], ++ _ + '' + $ -> _ = $ = 0, ++ _ + '' + $ -> _ = 0, $ = 0, ++ _ + '' + $ -> ++ 0 + '' + 0 -> 1 + '' + 0 -> '10' // Yei: v
LeagueOfJava

Bu aynı değerlendirir ama sizinkinden bile küçük:"10"
ADJenks

7

+ [], 0 [...] olarak değerlendirir ve ardından onu toplama (+ işlemi) ile dizi içeriğini virgülle birleştirilen öğelerden oluşan dize temsiline dönüştürür.

Dizinin dizinini almak gibi herhangi bir şey (+ işleminden rende önceliğe sahiptir) sıralıdır ve ilginç değildir.


4

Belki de bir ifadeyi basamak olmadan "10" olarak değerlendirmenin olası en kısa yolları şunlardır:

+!+[] + [+[]] // "10"

-~[] + [+[]] // "10"

// ========== Açıklama ==========

+!+[]: +[]0'a !0dönüştürür true. +true1'e dönüştürür. -~[]= -(-1)1 olan

[+[]]: +[]0'a dönüştürür. 0 [0]tek öğeli bir dizidir.

Daha sonra JS ifadesini 1 + [0], dolayısıyla Number + Arrayifadesini değerlendirir . Daha sonra ECMA özelliği çalışır: +operatör toString()/valueOf(), temel Objectprototipten işlevleri çağırarak her iki işleneni de bir dizeye dönüştürür . Bir ifadenin her iki işleneni de yalnızca sayılarsa, ek işlev olarak çalışır. İşin püf noktası, dizilerin öğelerini kolayca birleştirilmiş bir dize gösterimine dönüştürmesidir.

Bazı örnekler:

1 + {} //    "1[object Object]"
1 + [] //    "1"
1 + new Date() //    "1Wed Jun 19 2013 12:13:25 GMT+0400 (Caucasus Standard Time)"

İki Objectseklemenin bununla sonuçlandığı hoş bir istisna var NaN:

[] + []   //    ""
[1] + [2] //    "12"
{} + {}   //    NaN
{a:1} + {b:2}     //    NaN
[1, {}] + [2, {}] //    "1,[object Object]2,[object Object]"

1
  1. Unary artı verilen dize sayıya dönüştürür
  2. Verilen dize dönüştürür ve artışları verilen operatör 1
  3. [] == ''. Boş Dize
  4. + '' veya + [] 0 değerini değerlendirir.

    ++[[]][+[]]+[+[]] = 10 
    ++[''][0] + [0] : First part is gives zeroth element of the array which is empty string 
    1+0 
    10

1
Cevap karışık / kafa karıştırıcı, IOW yanlış. []ile eşdeğer değil"" . Önce eleman çıkarılır, sonra tarafından dönüştürülür ++.
PointedEars

1

Bunu adım adım, +bir sayıya çevirin ve boş bir diziye eklerseniz +[]... boş ve eşitse 0,

Oradan, şimdi kodunuza bakın, ++[[]][+[]]+[+[]]...

Ve aralarında artı var ++[[]][+[]]+[+[]]

Bu yüzden , diğer dizinin içine dönüştürülen boş bir diziye sahip oldukları için [+[]]geri dönecekler ...[0]0

Düşündüğünüz gibi, ilk değer, içinde bir dizi olan 2 boyutlu bir dizidir ... bu yüzden [[]][+[]]dönecek olana eşit [[]][0]olacaktır []...

Ve sonunda ++dönüştürmek ve artırmak 1...

Tahmin edebileceğiniz gibi, 1+ "0"olacak "10"...

Neden “10” dizesini döndürüyor?

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.