Bu geçerlidir ve dizeyi "10"
JavaScript'te döndürür ( burada daha fazla örnek ):
console.log(++[[]][+[]]+[+[]])
Neden? Burada ne oluyor?
Bu geçerlidir ve dizeyi "10"
JavaScript'te döndürür ( burada daha fazla örnek ):
console.log(++[[]][+[]]+[+[]])
Neden? Burada ne oluyor?
Yanıtlar:
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 A
yanlış 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 0
bir dizeye zorlanması gerekir, yani ""
, yine. Son olarak, 1
eklenir, 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:
İfadenin UnaryExpression değerlendirmesinin sonucu olmasına izin verin.
DönüşNumarası (GetValue (ifade)).
ToNumber()
diyor:
Nesne
Aşağıdaki adımları uygulayın:
PrimValue'nun ToPrimitive olmasına izin verin (giriş argümanı, ipucu String).
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:
ToString, "toString" argümanıyla O nesnesinin [[Get]] dahili yöntemini çağırmanın sonucu olsun.
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.
.toString
Bir dizinin diyor ki:
15.4.4.2 Array.prototype.toString ()
ToString yöntemi çağrıldığında aşağıdaki adımlar uygulanır:
Dizi bu değerde ToObject çağrısının sonucu olsun.
Func, "join" argümanıyla dizinin [[Get]] dahili yöntemini çağırmanın sonucu olsun.
IsCallable (func) yanlışsa, func'un standart yerleşik Object.prototype.toString (15.2.4.2) yöntemi olmasına izin verin.
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:
İfadenin UnaryExpression değerlendirmesinin sonucu olmasına izin verin.
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
.
true
değ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).
1 + [0]
, ifade değil değerine kadar kaynar . Bkz. Bclary.com/2004/11/07/#a-11.4.4"1" + [0]
++
++[[]][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ı?
PutValue
(ES3 terminolojisinde, 8.7.2'de) olduğundan eminim . PutValue
oysa 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.
var a = []; ++a
, a
yü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.
++[[]][+[]] => 1 // [+[]] = [0], ++0 = 1
[+[]] => [0]
Sonra bir dize birleşimimiz var
1+[0].toString() = 10
===
yerine daha net olmaz mıydı =>
?
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 0
tarafından ToNumber . Artık aşağıdakilerin 0
her 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 0
ve bu değer 1
bize son değerini vermek için arttırılır 1
. 0
Dış dizideki özelliğin değeri olarak güncelleştirilir 1
ve 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.prototype
ifadenin 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ığı ...
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"
Bu aynı ama biraz daha küçük olarak değerlendirilir
+!![]+''+(+[])
değerlendirir
+(true) + '' + (0)
1 + '' + 0
"10"
Şimdi anladınız, bunu deneyin:
_=$=+[],++_+''+$
"10"
Belki de bir ifadeyi basamak olmadan "10" olarak değerlendirmenin olası en kısa yolları şunlardır:
+!+[] + [+[]]
// "10"
-~[] + [+[]]
// "10"
// ========== Açıklama ==========
+!+[]
: +[]
0'a !0
dönüştürür true
. +true
1'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 + Array
ifadesini değerlendirir . Daha sonra ECMA özelliği çalışır: +
operatör toString()/valueOf()
, temel Object
prototipten 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 Objects
eklemenin 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]"
+ '' 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
[]
ile eşdeğer değil""
. Önce eleman çıkarılır, sonra tarafından dönüştürülür ++
.
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"
...
+[]
için boş bir dizi attığını anlayarak başlayın0
... sonra bir öğleden sonra israf ...;)