Tại sao! {} [True] đánh giá là đúng trong JavaScript?


131

{}[true][true]![true]nên false.

Vậy tại sao !{}[true]đánh giá true?


30
var o = {}; o[true] === undefined.
azz

2
Lời giải thích ở đây có thể sẽ rất giống với những điều kỳ lạ được thảo luận trong câu hỏi trước đây
IMSoP

45
"Bởi vì Javascript là ngớ ngẩn" có lẽ không phải là câu trả lời bạn đang tìm kiếm.
Georgia

2
Như đã đề cập, nếu bạn đang nhận được {}[true] === [true]từ một bảng điều khiển, đó là bởi vì nó được coi {}là một khối mã trống chứ không phải một đối tượng.
azz

3
nếu nó có thể giúp, hãy thử so sánh {}({})trong bảng điều khiển của bạn (hoặc {}[true]({})[true]). Ngoài ra, như không ai đề cập đến nó, đối tượng [true] được ước tính cho đối tượng ["true"].
BiAiB

Câu trả lời:


172

Tôi tin rằng vì đơn giản {}[true]được phân tách như một khối tuyên bố rỗng (không phải là một đối tượng theo nghĩa đen) tiếp theo là một mảng chứa true, đó là true.

Mặt khác, việc áp dụng các !nhà điều hành làm cho các phân tích cú pháp diễn giải {}như một literal đối tượng, vì vậy sau đây {}[true]sẽ trở thành một thành viên truy cập mà lợi nhuận undefined, và !{}[true]thực sự là true(như !undefinedtrue).


25
Thực tế là, không xác định là đúng, mặt khác, vẫn không thể tha thứ được.
evilcandybag

87
@evilcandybag: Hoàn toàn không. undefinedlà giả mạo (một cái gì đó mà chúng ta thường dựa vào - if (obj.maybeExists) ...), vì vậy nó có ý nghĩa logic hoàn hảo !undefinedlà đúng.
josh3736

8
@Josh, tôi nghĩ evilcandybag sẽ thích một hành vi gần giống với nullmột số ngôn ngữ, !undefinedtương đương với undefined. Tuy nhiên, đó không phải là trường hợp trong Javascript.
Frédéric Hamidi

6
@evilcandybag: chỉ có ý nghĩa logic khi nói rằng một cái gì đó là not undefined( !undefined) do đó phải được xác định. Nếu một cái gì đó được định nghĩa thì nó thường được hiểu là true.
OozeMeister

7
@Cruncher Nếu a không xác định và b không xác định, làm sao chúng ta có thể biết rằng a! = B? Đặc biệt khi đặc điểm duy nhất được biết của hai biến là hoàn toàn giống nhau.
LJ2

44

Bởi vì {}[true]không trả về true, nhưng undefined, và undefinedđược đánh giá là false:

http://jsfiddle.net/67GEu/

'use strict';
var b = {}[true];
alert(b); // undefined
b = !{}[true];
alert(b); // true

21
Nếu bạn đánh giá {}[true]trong một giao diện điều khiển, bạn sẽ nhận được [true], bởi vì {}nó được hiểu là một khối mã trống, không phải là một đối tượng. Đó là tất cả về bối cảnh và sự mơ hồ của {}.
IMSoP

1
@IMSoP nhưng tại sao {key:"value"}[1,2,3];cũng đánh giá [1,2,3]?
t.niese

3
@ t.niese, vì nó được phân tích cú pháp dưới dạng khối câu lệnh chứa nhãn ( key:) và chuỗi ký tự ( "value"), theo sau là một mảng. Trình phân tích cú pháp vẫn không thấy một đối tượng bằng chữ.
Frédéric Hamidi

1
@ Frédéric Hamidi ah vâng, đúng vậy. Tôi đã kìm nén các nhãn ^^
t.niese

1
@dooxe Đọc các câu trả lời khác; đó là tất cả về bối cảnh mà nó được giải thích. Nếu bạn bọc nó trong alert()hoặc console.log(), hoặc gán nó cho một biến, bạn đang thay đổi bối cảnh, đó là lý do tại sao nó không hoạt động giống như cách tự gõ trong bảng điều khiển.
IMSoP

27

Bởi vì

{}[true]

đánh giá undefined, và !undefinedtrue.

Từ @schlingel:

trueđược sử dụng làm chìa khóa và {}làm bản đồ băm. Không tồn tại một tài sản với khóa trueđể nó trả về undefined. Không phải undefinedtrue, như mong đợi.

Phiên bảng điều khiển ( Node.js [0.10.17] ):

> {}[true]
undefined
> !{}[true]
true
> [true]
[ true ]
> ![true]
false
>

Tuy nhiên, trong bảng điều khiển Google Chrome :

> !{}[true]
true

Vì vậy, không có sự mâu thuẫn. Có lẽ bạn đang sử dụng phiên bản cũ của JavaScript VM. Đối với những người cần thêm bằng chứng:

Nhập mô tả hình ảnh ở đây

CẬP NHẬT

Với Firefox , nó cũng đánh giá để true:

Nhập mô tả hình ảnh ở đây


Không nếu bạn làm eval('{}[true]')hoặc gõ nó vào bàn điều khiển. Sau đó, ví dụ als {}"test"testhoặc thậm chí {key:"value"}"test"test.
t.niese

Thật thú vị, bạn đã kiểm tra động cơ js nào?
t.niese

@ t.niese Tôi vừa gõ nó vào bảng điều khiển nút của mình, và đây là những gì tôi nhận được.
Trò chơi Brainiac

Chỉ vì tò mò. Có {}[true];(với ;) trả lại [true]cho bạn, bởi vì ở đây nó không?
t.niese

2
Lý do cho downvote guys? Có một câu trả lời gần như giống hệt với câu hỏi này với 8 phiếu, và tôi có nhận được downvote không? Tôi đã làm gì sai?
Trò chơi Brainiac

23

Lý do cho sự nhầm lẫn là do sự hiểu lầm về khẳng định đầu tiên của bạn:

{}[true][true]

Những gì bạn nhìn thấy khi bạn chạy nó là kết quả của sự mơ hồ. Javascript có một bộ quy tắc được xác định là làm thế nào để xử lý sự mơ hồ như thế này và trong trường hợp này, nó phá vỡ những gì bạn thấy dưới dạng một câu lệnh đăng nhập thành hai câu lệnh riêng biệt.

Vì vậy, Javascript xem mã trên là hai câu lệnh riêng biệt: Thứ nhất, có một {}, và sau đó có một đoạn hoàn toàn riêng biệt [true]. Tuyên bố thứ hai là những gì mang lại cho bạn kết quả [true]. Các tuyên bố đầu tiên {}là hoàn toàn bỏ qua.

Bạn có thể chứng minh điều này bằng cách thử như sau:

({}[true])

tức là gói toàn bộ trong ngoặc để buộc người phiên dịch đọc nó dưới dạng một câu lệnh.

Bây giờ bạn sẽ thấy rằng giá trị thực tế của tuyên bố của bạn là undefined . (điều này cũng sẽ giúp chúng ta sau này để hiểu phần tiếp theo)

Bây giờ chúng tôi biết rằng phần ban đầu của câu hỏi của bạn là cá trích đỏ, vì vậy hãy chuyển sang phần cuối cùng của câu hỏi:

Vậy tại sao! {} [True] đánh giá là đúng?

Ở đây, chúng ta có cùng một tuyên bố, nhưng với một phần được !thêm vào phía trước của nó.

Trong trường hợp này, các quy tắc của Javascript bảo nó đánh giá toàn bộ sự việc dưới dạng một câu lệnh.

Xem lại những gì đã xảy ra khi chúng tôi gói câu lệnh trước đó trong ngoặc; chúng tôi đã nhận undefined. Lần này, chúng tôi thực sự đang làm điều tương tự, nhưng đặt một !phía trước nó. Vì vậy, mã của bạn có thể được đơn giản hóa !undefined, đó là true.

Hy vọng rằng điều đó giải thích một chút.

Đây là một con thú phức tạp, nhưng bài học cần tìm hiểu ở đây là sử dụng dấu ngoặc quanh các câu lệnh của bạn khi đánh giá chúng trong bảng điều khiển, để tránh các kết quả giả như thế này.


2
Tôi không nghĩ {}[true]không chính xác, chỉ là mơ hồ . Nó có thể được hiểu là "khối mã rỗng theo sau là mảng bằng chữ" hoặc "đối tượng bằng chữ không có thuộc tính, trong đó một thuộc tính đang được truy cập". Tôi không biết liệu đầu tiên về mặt kỹ thuật có phải là trường hợp của ASI hay không (nhiều ngôn ngữ sẽ không đặt dấu chấm phẩy ở đó) nhưng đó là cách giải thích theo ngữ cảnh là cốt lõi của vấn đề.
IMSoP

@IMSoP - Tôi đã chỉnh sửa câu trả lời trước khi bạn đăng bình luận. :)
Spudley

1
Nó vẫn nói "{} [true] hoàn toàn không hợp lệ" ngay khi bắt đầu câu trả lời.
IMSoP

Ngoài ra, OP không nói " {}[true]true" họ nói " {}[true][true]", đây là một trong hai cách hiểu hợp lệ của tuyên bố mơ hồ.
IMSoP

14

{}[true]undefined. Để tìm thấy điều đó viết này:

a = {};
a[true] === undefined // true

hoặc đơn giản:

({})[true] === undefined // true

Chúng tôi biết đó !undefinedtrue.


Từ câu trả lời của @Benjamin Gruenbaum :

Các công cụ dveloper của Chrome thực hiện như sau :

  try {
      if (injectCommandLineAPI && inspectedWindow.console) {
          inspectedWindow.console._commandLineAPI = new CommandLineAPI(this._commandLineAPIImpl, isEvalOnCallFrame ? object : null);
          expression = "with ((window && window.console && window.console._commandLineAPI) || {}) {\n" + expression + "\n}";
      }
      var result = evalFunction.call(object, expression);
      if (objectGroup === "console")
          this._lastResult = result;
      return result;
  } 
  finally {
      if (injectCommandLineAPI && inspectedWindow.console)
          delete inspectedWindow.console._commandLineAPI;
  }

Về cơ bản, nó thực hiện một calltrên đối tượng với biểu thức. Biểu thức là:

with ((window && window.console && window.console._commandLineAPI) || {}) {
    {}+{};// <-- This is your code
}

Vì vậy, như bạn có thể thấy, biểu thức đang được đánh giá trực tiếp, không có dấu ngoặc đơn.

Thêm thông tin có thể được tìm thấy trong câu hỏi này .


10

Các câu trả lời ở đây là tốt, đây là một sự cố trong mã giả:

  • {}['whatever'] = khối trống, NewArray ('anything') = NewArray ('anything')
  • {}[true] = khối trống, NewArray (true) = NewArray (true)
  • !{}['whatever'] = LogicalNOT (convertToBool (NewObject.whthing)) = LogicalNOT (convertToBool (không xác định)) = LogicalNOT (false) = true
  • ({}['whatever']) = Nhóm (NewObject.whthing) = Nhóm (không xác định) = không xác định

8

Điều này xảy ra bởi vì {}theo nghĩa của bạn không phải là trình bày theo nghĩa đen Object, mà là phạm vi trống (hoặc khối mã trống):

{ var a = 1 }[true] // [true] (do the same thing)

Nó chỉ đánh giá mã bên trong phạm vi và sau đó hiển thị cho bạn mảng của bạn.

Và từ của bạn

!{}[true]

Chỉ cần chuyển đổi sang int phạm vi này và trả về cùng một mảng đúng. Không có kiểm tra bool trong mã này.

Và nếu bạn cố gắng kiểm tra kết quả từ {}[true]bạn sẽ nhận được false:

{}[true] -> [true] -> ![true] -> false

Như không có bất kỳ phạm vi.

Vì vậy, !trong câu hỏi của bạn làm tương tự như:

!function() {
   //...
}

Điều này dễ thấy hơn nếu bạn làm var x = {}; x[true].
Chris Hayes

1
Tôi không chắc ý của bạn là gì khi "chuyển đổi sang int phạm vi này"; Tôi nghĩ với sự dẫn đầu, !được hiểu là một đối tượng trống rỗng, không phải phạm vi và đây là sự khác biệt.
IMSoP

6
  • {} là một đối tượng không có thuộc tính.
  • []ngay lập tức theo sau một đối tượng, nó có nghĩa là "Truy cập một thuộc tính của tên này" chứ không phải "Tạo một mảng"
  • truelà một boolean, nhưng đang được sử dụng làm tên thuộc tính để nó được chuyển thành một chuỗi ( "true")
  • Các đối tượng không có một tài sản được gọi là true(vì nó không có tài sản) nên {}['true']undefined
  • !undefinedphôi undefinedđể một boolean ( false)
  • Toán tử không biến falsethành true.

2
Trong trường hợp {}[true](không có bối cảnh khác), {}không một đối tượng không có thuộc tính, nó là một khối mã sản phẩm nào.
IMSoP


4

Hãy chơi nhiều hơn một chút!

Đầu tiên, chúng ta hãy vui vẻ!:

//----------#01#-----------
{}[true]; //[true]

//----------#02#-----------
var a = {}[true]; 
      console.log(a); //undefined

//----------#03#-----------
{ b: 12345 }[true]; //[true]

//----------#04#-----------
{ b: 12345 }["b"]; //evaluates to ["b"] ?!?

//----------#05#-----------
{ b: 12345 }.b; // "Unexpected token ."

//----------#06#-----------
({ b: 12345 }).b; //12345

//----------#07#-----------
var c = { b: 12345 }.b; 
      console.log(c); //12345

//----------#08#-----------
var c = { b: 12345 }["b"];
      console.log(c); //12345

//----------#09#-----------
{ true: 54321 }[true]; // "SyntaxError: Unexpected token : "

//----------#10#-----------
var d = { true: 54321 }[true]; //No error here ¬¬
      console.log(d); //54321

//----------#11#-----------
!{}[true]; // true

Ok, chúng ta hãy cố gắng hiểu những hành vi điên rồ này, từng cái một:

1) Ở đây, {}được phân tích cú pháp dưới dạng một khối mã trống. Không có sự gán, phủ định, nhóm (có dấu ngoặc đơn) hoặc bất kỳ cú pháp nào chỉ ra cho trình phân tích cú pháp rằng đây {}là một đối tượng theo nghĩa đen, giả định mặc định là nghĩ rằng nó chỉ đơn giản là một khối trống vô dụng.

Đây là một bằng chứng về hành vi này:

{ alert(123) }[true]

Mã ở trên sẽ hiển thị cảnh báo bình thường và sẽ được đánh giá là [true], theo cách tương tự {}[true].

Khối báo cáo không có dấu chấm phẩy

Một câu lệnh kiểu khối không cần dấu chấm phẩy sau nó.

Ví dụ:

for(var i=0; i < 1; i++){}function a(){};alert("Passed here!");if(true){}alert("Passed here too!")

Cả hai cảnh báo được hiển thị.

Vì vậy, chúng ta có thể thấy rằng một tuyên bố khối trống, không có dấu chấm phẩy, là hợp lệ và đơn giản là không làm gì cả. Theo cách này, khi bạn nhập vào {}[true]Bảng điều khiển Công cụ dành cho nhà phát triển (hoặc Fireorms), giá trị được đánh giá sẽ là giá trị của câu lệnh biểu thức cuối cùng . Trong trường hợp này, câu lệnh biểu thức cuối cùng là[true] .

2) Trong ngữ cảnh gán, trình phân tích cú pháp sẽ đảm bảo rằng đó {}là một đối tượng bằng chữ. Khi bạn thực hiện var a = {}[true], bạn loại bỏ bất kỳ sự mơ hồ nào và loại bỏ trình phân tích cú pháp {}không phải là một câu lệnh chặn.
Vì vậy, ở đây, bạn đang cố gắng để có được một giá trị với một khóa"true" từ một đối tượng trống. Rõ ràng, không có cặp khóa-giá trị với tên khóa này. Bằng cách này, một biến là không xác định.

Các từ dành riêng như các khóa đối tượng

ECMAScript 5 cho phép các khóa đối tượng là các từ dành riêng. Vì vậy, các khóa sau là hợp pháp:

var obj = {if: 111, for: 222, switch: 333, function: 444, true: 555}

3) Giải thích tương tự ví dụ 1 . Nhưng ... Nếu { b: 12345 }phần đó được coi là một câu lệnh chặn thì loại b: 12345câu lệnh đó là gì ??

... (?????)

Đó là một tuyên bố nhãn , bạn đã thấy nó trước đây ... Nó được sử dụng trong các vòng lặp và trong switch. Dưới đây là một vài liên kết thú vị về các tuyên bố nhãn: 1 , (2) [ Cách tốt nhất để thoát khỏi các vòng lặp lồng nhau trong Javascript?, (3) [ Làm thế nào để phá vỡ các vòng lặp lồng nhau trong javascript? .

GHI CHÚ: Chỉ cần cố gắng đánh giá điều này:

{a: 1, b: 2} //=>>>SyntaxError: Unexpected token :

Báo cáo nhãn không thể được phân tách bởi phân tách bằng toán tử dấu phẩy , bạn sẽ cần tách chúng bằng dấu chấm phẩy. Vì vậy, điều này là hợp lệ:{a: 1; b: 2}

4) Xem giải thích cho các ví dụ 1 3 ...

5) Một lần nữa, chúng ta { b: 12345 }bị coi là một khối mã và bạn đang cố truy cập vào một thuộc tính của khối mã bằng cách sử dụng ký hiệu dấu chấm , và rõ ràng, điều này không được phép và trình phân tích cú pháp ném một"Unexpected token :" ngoại lệ.

6) Mã gần giống với ví dụ trên, nhưng bằng cách bao quanh { b: 12345 }câu lệnh với toán tử nhóm biểu thức , trình phân tích cú pháp sẽ biết đó là một đối tượng. Bằng cách này, bạn sẽ có thể truy cập vào"b" tài sản bình thường.

7) Hãy nhớ ví dụ 2 , chúng ta có một bài tập ở đây, trình phân tích cú pháp biết rằng{ b: 12345 } là một đối tượng.

8) Giống hệt với ví dụ trên, nhưng thay vì ký hiệu dấu chấm, ở đây chúng tôi đang sử dụng ký hiệu dấu ngoặc .

9) Tôi đã nói rằng "identifier: value"cú pháp này trong một câu lệnh khối là một nhãn. Nhưng, bạn cũng phải biết rằng tên nhãn không thể là từ khóa dành riêng (ngược lại với tên thuộc tính đối tượng). Khi chúng tôi cố gắng xác định một nhãn được gọi "true", chúng tôi đã nhận được một SyntaxError.

10) Một lần nữa, chúng ta đang làm việc với một đối tượng. Không có vấn đề sử dụng các từ dành riêng ở đây. =)

11) Cuối cùng, chúng ta có điều này:!{}[true]

Chúng ta hãy tách những thứ ở đây:

a) Bằng cách phủ nhận, chúng tôi thông báo cho trình phân tích cú pháp rằng đó {}một đối tượng .

b) Như trong ví dụ 2 , một {}đối tượng không có thuộc tính được gọi true, vì vậy biểu thức này sẽ ước tính undefined.

c) Kết quả cuối cùng là sự phủ định undefinedgiá trị. Javascript thực hiện chuyển đổi loại ẩnundefinedgiá trị là giả .

d) Vì vậy, sự phủ định falselà ... true!

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.