Câu hỏi Tại sao ++ [[]] [+ []] + [+ []] trả về chuỗi “10”?


Điều này là hợp lệ và trả về chuỗi "10" trong JavaScript (thêm ví dụ ở đây):

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

Tại sao? Chuyện gì đang xảy ra ở đây?


1440
2017-08-26 08:56


gốc


Bắt đầu bằng cách hiểu rằng +[] tạo một mảng trống cho 0... sau đó lãng phí một buổi chiều ...;) - deceze♦
Liên quan stackoverflow.com/questions/4170978/explain-why-this-works . - Juho Vepsäläinen
typeof ++ [[]] [+ []] + [+ []] trả về "số 0" trong Bảng điều khiển của Chrome. - dgo
"Có ++ [[]] [+ []] + [+ []] loại người trong thế giới này ..." áo sơ mi sắp xảy ra. - dmanexe
Bạn có thể chuyển đổi mã js thành mã này bằng cách sử dụng jsfuck.com - Oss


Các câu trả lời:


Nếu chúng ta chia nó ra, mớ hỗn độn là bằng:

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

Trong JavaScript, đúng là +[] === 0. + chuyển đổi một cái gì đó thành một số, và trong trường hợp này nó sẽ đi xuống +"" hoặc là 0 (xem chi tiết đặc điểm kỹ thuật bên dưới).

Do đó, chúng tôi có thể đơn giản hóa nó (++ có quyền ưu tiên hơn +):

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

Bởi vì [[]][0] có nghĩa là: lấy yếu tố đầu tiên từ [[]], đúng là:

  • [[]][0] trả về mảng bên trong ([]). Do tham chiếu là sai khi nói [[]][0] === [], nhưng hãy gọi mảng bên trong A để tránh ký hiệu sai.
  • ++[[]][0] == A + 1, kể từ đó ++ có nghĩa là 'tăng thêm một'.
  • ++[[]][0] === +(A + 1); nói cách khác, nó sẽ luôn là một con số (+1 không nhất thiết phải trả lại một số, trong khi ++ luôn luôn - cảm ơn Tim Down vì đã chỉ ra điều này).

Một lần nữa, chúng ta có thể đơn giản hóa các mess vào một cái gì đó dễ đọc hơn. Hãy thay thế [] lại cho A:

+([] + 1)
+
[0]

Trong JavaScript, điều này cũng đúng: [] + 1 === "1", bởi vì [] == "" (tham gia một mảng trống), vì vậy:

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

Hãy đơn giản hóa nó nhiều hơn:

1
+
[0]

Ngoài ra, điều này đúng trong JavaScript: [0] == "0", bởi vì nó tham gia một mảng với một phần tử. Tham gia sẽ ghép các phần tử được phân cách bằng ,. Với một phần tử, bạn có thể suy ra rằng logic này sẽ dẫn đến chính phần tử đầu tiên.

Vì vậy, cuối cùng chúng ta có được (number + string = string):

1
+
"0"

=== "10" // Yay!

Chi tiết đặc điểm kỹ thuật cho +[]:

Đây là một mê cung, nhưng để làm +[], đầu tiên nó đang được chuyển đổi thành một chuỗi bởi vì đó là những gì + nói:

11.4.6 Nhà điều hành + đơn nhất

Toán tử + đơn nguyên chuyển đổi toán hạng của nó thành Kiểu số.

UnaryExpression sản xuất: + UnaryExpression được đánh giá như sau:

  1. Hãy để expr là kết quả của việc đánh giá UnaryExpression.

  2. Trả về ToNumber (GetValue (expr)).

ToNumber() nói:

Vật

Áp dụng các bước sau:

  1. Hãy để primValue là ToPrimitive (đối số đầu vào, chuỗi gợi ý).

  2. Trả về ToString (primValue).

ToPrimitive() nói:

Vật

Trả về một giá trị mặc định cho đối tượng. Giá trị mặc định của một đối tượng được lấy ra bằng cách gọi phương thức bên trong [[DefaultValue]] của đối tượng, chuyển qua gợi ý tùy chọn PreferredType. Hành vi của phương pháp bên trong [[DefaultValue]] được xác định bởi đặc tả này cho tất cả các đối tượng ECMAScript gốc trong 8.12.8.

[[DefaultValue]] nói:

8.12.8 [[DefaultValue]] (gợi ý)

Khi phương thức bên trong [[DefaultValue]] của O được gọi bằng chuỗi gợi ý, các bước sau được thực hiện:

  1. Gọi toString là kết quả của việc gọi phương thức bên trong [[Get]] của đối tượng O với đối số "toString".

  2. Nếu IsCallable (toString) là đúng thì,

a. Cho str là kết quả của việc gọi phương thức bên trong [[Call]] của toString, với O là giá trị này và một danh sách đối số rỗng.

b. Nếu str là một giá trị nguyên thủy, trả về str.

Các .toString của một mảng cho biết:

15.4.4.2 Array.prototype.toString ()

Khi phương thức toString được gọi, thực hiện các bước sau:

  1. Hãy để mảng là kết quả của việc gọi ToObject trên giá trị này.

  2. Gọi func là kết quả của việc gọi phương thức nội bộ [[Get]] của mảng với tham số "join".

  3. Nếu IsCallable (func) là false, thì hãy cho func là phương thức tích hợp chuẩn Object.prototype.toString (15.2.4.2).

  4. Trả về kết quả của việc gọi phương thức bên trong [[Call]] của func cung cấp mảng làm giá trị này và một danh sách đối số rỗng.

Vì thế +[] đi xuống +"", bởi vì [].join() === "".

Một lần nữa, + được định nghĩa là:

11.4.6 Nhà điều hành + đơn nhất

Toán tử + đơn nguyên chuyển đổi toán hạng của nó thành Kiểu số.

UnaryExpression sản xuất: + UnaryExpression được đánh giá như sau:

  1. Hãy để expr là kết quả của việc đánh giá UnaryExpression.

  2. Trả về ToNumber (GetValue (expr)).

ToNumber được định nghĩa cho "" như:

MV của StringNumericLiteral ::: [trống] là 0.

Vì thế +"" === 0, và như vậy +[] === 0.


1876
2017-08-26 08:58



@harper: Đó là kiểm tra bình đẳng nghiêm ngặt, tức là nó chỉ trả về true nếu cả giá trị và loại đều giống nhau. 0 == "" trả về true (cùng một loại sau khi chuyển đổi), nhưng 0 === "" Là false (không cùng loại). - pimvdb
Một phần của điều này là không chính xác. Biểu hiện sôi xuống 1 + [0], không phải "1" + [0], bởi vì tiền tố (++) toán tử luôn trả về một số. Xem bclary.com/2004/11/07/#a-11.4.4 - Tim Down
@Tim Down: Bạn hoàn toàn chính xác. Tôi đang cố gắng sửa lỗi này, nhưng khi cố gắng làm như vậy tôi đã tìm thấy một thứ khác. Tôi không chắc làm thế nào điều này là có thể. ++[[]][0] trả về thực sự 1, nhưng ++[] ném một lỗi. Điều này là đáng chú ý vì nó trông giống như ++[[]][0] đun sôi xuống ++[]. Bạn có lẽ có bất kỳ ý tưởng tại sao không ++[] ném một lỗi trong khi ++[[]][0] không làm? - pimvdb
@pimvdb: Tôi khá chắc chắn vấn đề ở trong PutValue cuộc gọi (trong thuật ngữ ES3, 8.7.2) trong hoạt động tiền tố. PutValue yêu cầu tham chiếu trong khi [] như một biểu thức của riêng nó không tạo ra một tham chiếu. Biểu thức chứa tham chiếu biến (giả sử chúng ta đã định nghĩa trước đó var a = [] sau đó ++a hoạt động) hoặc quyền truy cập tài sản của một đối tượng (chẳng hạn như [[]][0]) tạo tham chiếu. Nói một cách đơn giản, toán tử tiền tố không chỉ tạo ra một giá trị, nó cũng cần một nơi nào đó để đặt giá trị đó. - Tim Down
@pimvdb: Vì vậy, sau khi thực hiện var a = []; ++a, a là 1. Sau khi thực hiện ++[[]][0], mảng được tạo bởi [[]] biểu thức hiện chỉ chứa số 1 ở chỉ mục 0. ++ yêu cầu một tài liệu tham khảo để làm điều này. - Tim Down


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

Sau đó, chúng tôi có một chuỗi nối

1+[0].toString() = 10

99
2017-09-14 13:54



Hy vọng tất cả các câu trả lời được thẳng đến điểm như thế này. - Adam


Sau đây được điều chỉnh từ một bài viết trên blog trả lời câu hỏi này mà tôi đã đăng trong khi câu hỏi này vẫn đóng. Các liên kết đến (bản sao HTML) của thông số ECMAScript 3, vẫn là đường cơ sở cho JavaScript trong các trình duyệt web thường được sử dụng ngày nay.

Đầu tiên, một nhận xét: loại biểu thức này sẽ không bao giờ xuất hiện trong bất kỳ môi trường sản xuất nào (sane) và chỉ sử dụng như một bài tập trong cách người đọc biết được các cạnh mờ của JavaScript. Nguyên tắc chung mà các toán tử JavaScript chuyển đổi hoàn toàn giữa các loại là hữu ích, cũng như một số chuyển đổi phổ biến, nhưng phần lớn chi tiết trong trường hợp này là không.

Cách diễn đạt ++[[]][+[]]+[+[]] ban đầu có thể trông khá áp đặt và tối nghĩa, nhưng thực sự là tương đối dễ dàng chia thành các biểu thức riêng biệt. Dưới đây tôi chỉ đơn giản là thêm dấu ngoặc đơn cho sự rõ ràng; Tôi có thể đảm bảo với bạn rằng họ không thay đổi gì cả, nhưng nếu bạn muốn xác minh điều đó thì hãy thoải mái đọc về toán tử nhóm. Vì vậy, biểu thức có thể được viết rõ ràng hơn

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

Phá vỡ điều này, chúng ta có thể đơn giản hóa bằng cách quan sát +[] đánh giá 0. Để thỏa mãn chính mình tại sao điều này là đúng, hãy xem toán tử đơn + và đi theo con đường hơi quanh co mà kết thúc với ToPrimitive chuyển đổi mảng trống thành một chuỗi rỗng, sau đó nó được chuyển đổi thành 0 bởi Đến số. Bây giờ chúng ta có thể thay thế 0 cho mỗi trường hợp +[]:

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

Đã có sẵn. Như cho ++[[]][0], đó là sự kết hợp của toán tử tăng thêm tiền tố (++), một mảng chữ định nghĩa một mảng với phần tử đơn lẻ là chính nó là một mảng trống ([[]]) và một truy cập tài sản ([0]) được gọi trên mảng được xác định bởi mảng chữ.

Vì vậy, chúng tôi có thể đơn giản hóa [[]][0] chỉ [] và chúng ta có ++[], đúng? Trong thực tế, đây không phải là trường hợp vì đánh giá ++[] ném một lỗi, có thể ban đầu có vẻ khó hiểu. Tuy nhiên, một chút suy nghĩ về bản chất của ++ làm rõ điều này: nó được sử dụng để tăng biến (ví dụ: ++i) hoặc thuộc tính đối tượng (ví dụ: ++obj.count). Nó không chỉ đánh giá một giá trị, nó cũng lưu trữ giá trị đó ở đâu đó. Trong trường hợp ++[], nó không có nơi nào để đặt giá trị mới (bất kể nó có thể là gì) vì không có tham chiếu đến thuộc tính hoặc biến đối tượng để cập nhật. Trong điều kiện cụ thể, điều này được bao phủ bởi nội bộ PutValue hoạt động, được gọi bởi toán tử tăng thêm tiền tố.

Vậy thì, cái gì ++[[]][0] làm gì? Vâng, bằng logic tương tự như +[], mảng bên trong được chuyển đổi thành 0 và giá trị này được tăng lên bởi 1 để cung cấp cho chúng tôi giá trị cuối cùng của 1. Giá trị của tài sản 0 trong mảng ngoài được cập nhật thành 1 và toàn bộ biểu thức đánh giá 1.

Điều này khiến chúng tôi

1 + [0]

... đó là một cách sử dụng đơn giản của toán tử bổ sung. Cả hai toán hạng là đầu tiên chuyển thành nguyên thủy và nếu một trong hai giá trị nguyên thủy là một chuỗi, chuỗi nối được thực hiện, nếu không thì việc bổ sung số được thực hiện. [0] chuyển thành "0", vì vậy nối chuỗi được sử dụng, sản xuất "10".

Cuối cùng, một cái gì đó có thể không rõ ràng ngay lập tức là ghi đè một trong hai toString() hoặc là valueOf() phương pháp của Array.prototype sẽ thay đổi kết quả của biểu thức, bởi vì cả hai đều được kiểm tra và sử dụng nếu có khi chuyển đổi một đối tượng thành một giá trị nguyên thủy. Ví dụ, sau đây

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

... sản xuất "NaNfoo". Tại sao điều này xảy ra còn lại như một bài tập cho người đọc ...


57
2017-12-30 15:41





Hãy làm cho nó đơn giản:

++[[]][+[]]+[+[]] = "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"

21
2017-12-28 23:13





Cái này đánh giá giống nhau nhưng nhỏ hơn một chút

+!![]+''+(+[])
  • [] - là một mảng được chuyển đổi được chuyển thành 0 khi bạn cộng hoặc trừ nó, do đó + [] = 0
  • ! [] - đánh giá sai, do đó !! [] đánh giá đúng
  • + !! [] - chuyển giá trị true thành giá trị số để đánh giá là đúng, vì vậy trong trường hợp này 1
  • + '' - nối thêm một chuỗi rỗng vào biểu thức làm cho số được chuyển thành chuỗi
  • + [] - đánh giá là 0

như vậy là đánh giá

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

Vì vậy, bây giờ bạn đã có điều đó, hãy thử cái này:

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

13
2017-08-26 08:58



whats câu trả lời? '010'? - ghostCoder
Cũng không có nó vẫn đánh giá đến "10". Tuy nhiên điều này đang làm nó theo một cách khác. Hãy thử đánh giá điều này trong trình kiểm tra javascript như chrome hoặc một cái gì đó. - Vlad Shlosberg
_ = $ = + [], ++ _ + '' + $ -> _ = $ = 0, ++ _ + '' + $ -> _ = 0, $ = 0, ++ _ + '' + $ -> ++ 0 + '' + 0 -> 1 + '' + 0 -> '10' // Yei: v - LeagueOfJava


+ [] đánh giá là 0 [...] sau đó tổng hợp (+ hoạt động) nó với bất cứ điều gì chuyển đổi nội dung mảng để đại diện chuỗi của nó bao gồm các yếu tố tham gia bằng dấu phẩy.

Bất cứ điều gì khác như lấy chỉ số của mảng (có ưu tiên vắt hơn + hoạt động) là thứ tự và không có gì thú vị.


7
2017-12-30 08:10





Có lẽ cách ngắn nhất có thể để đánh giá một biểu thức thành "10" không có chữ số là:

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

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

// ========== Giải thích ========== \\

+!+[] : +[] Chuyển thành 0. !0 chuyển thành true. +true chuyển thành 1. -~[] = -(-1) đó là 1

[+[]] : +[] Chuyển thành 0. [0] là một mảng với một phần tử đơn lẻ 0.

Sau đó, JS đánh giá 1 + [0], do đó Number + Array biểu hiện. Sau đó, đặc tả ECMA hoạt động: + toán tử chuyển đổi cả hai toán hạng thành một chuỗi bằng cách gọi toString()/valueOf() chức năng từ cơ sở Object nguyên mẫu. Nó hoạt động như một hàm phụ gia nếu cả hai toán hạng của một biểu thức chỉ là các số. Bí quyết là mảng dễ dàng chuyển đổi các phần tử của chúng thành một biểu diễn chuỗi được nối.

Vài ví dụ:

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

Có một ngoại lệ tốt đẹp là hai Objects kết quả bổ sung trong NaN:

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

4