Sự khác biệt giữa (NaN! = NaN) và (NaN! == NaN) là gì?


148

Trước hết tôi muốn đề cập rằng tôi biết làm thế nào isNaN()Number.isNaN()làm việc. Tôi đang đọc Hướng dẫn xác định của David Flanagan và ông đưa ra một ví dụ về cách kiểm tra xem giá trị có phải là NaN:

x !== x

Điều này sẽ dẫn đến kết quả truenếu và chỉ khi xNaN.

Nhưng bây giờ tôi có một câu hỏi: tại sao anh ta sử dụng so sánh nghiêm ngặt? Bởi vì dường như

x != x

cư xử theo cùng một cách. Là nó an toàn để sử dụng cả hai phiên bản, hay tôi thiếu một số giá trị (s) trong JavaScript rằng sẽ trở lại truecho x !== xfalsecho x != x?


10
Có thể là Flanagan chỉ thích !==kiểm tra hơn !=séc. Theo như tôi biết thì không có giá trị nào khác x != x. Nhưng có hai nhóm riêng biệt của các nhà phát triển JavaScript: những người thích !=và những người thích !==, có thể là cho tốc độ, rõ ràng, biểu cảm, vv
Steve Klosters

30
Tại sao sử dụng so sánh lỏng lẻo khi so sánh nghiêm ngặt hành xử theo cùng một cách?
Ry-

3
@Raulucco: NaNkhông phải là một loại duy nhất, nó là một con số. Đó là một giá trị duy nhất không bằng chính nó.
TJ Crowder

8
Tiêu đề dường như là gây hiểu lầm cho mọi người. Tôi khuyên bạn nên thay đổi nó thành một cái gì đó như "Is x! = X có khác với x! == x không?"
TJ Crowder

6
@femmestem: Giorgi nói "trong trường hợp này" đó là vấn đề về phong cách. Và anh ấy đúng trong đó. Nó không phải là kiểu khi các kiểu toán hạng khác nhau, nhưng nó kiểu khi chúng giống nhau. Một cách riêng biệt: Flanagan đang thực hiện những so sánh đó ===với NaN để đưa ra quan điểm rằng NaN không bằng chính nó. Anh ấy không "sai", anh ấy làm nó như một bài tập giảng dạy, chứng tỏ rằng nó không hoạt động.
TJ Crowder

Câu trả lời:


128

Đầu tiên, hãy để tôi chỉ ra rằng NaN là một giá trị rất đặc biệt: Theo định nghĩa, nó không bằng chính nó. Điều đó xuất phát từ tiêu chuẩn IEEE-754 mà số JavaScript rút ra. Giá trị "không phải là số" không bao giờ bằng chính nó, ngay cả khi các bit là khớp chính xác. (Điều mà chúng không nhất thiết phải có trong IEEE-754, nó cho phép nhiều giá trị "không phải là số" khác nhau.) Đó là lý do tại sao điều này thậm chí xuất hiện; tất cả các giá trị khác trong JavaScript đều bằng chính chúng, NaNchỉ là đặc biệt.

... Tôi có thiếu một số giá trị trong JavaScript sẽ trả về true cho x! == x và false cho x! = x?

Không, bạn không phải. Sự khác biệt duy nhất giữa !==!=là cái sau sẽ thực hiện cưỡng chế kiểu nếu cần thiết để có được các loại toán hạng giống nhau. Trong x != x, các loại toán hạng là như nhau, và do đó, nó chính xác giống như x !== x.

Điều này rõ ràng từ đầu định nghĩa của Hoạt động bình đẳng trừu tượng :

  1. Trả về IfAbrupt (x).
  2. Trả về IfAbrupt (y).
  3. Nếu Loại (x) giống với Loại (y), thì

    Trả về kết quả thực hiện So sánh bình đẳng nghiêm ngặt x === y.

  4. ...

Hai bước đầu tiên là hệ thống ống nước cơ bản. Vì vậy, trong thực tế, bước đầu tiên ==là xem các loại có giống nhau không và nếu có thì phải làm gì ===. !=!==chỉ là phiên bản phủ định của điều đó.

Vì vậy, nếu Flanagan đúng mà chỉ NaNđúng x !== x, chúng ta có thể chắc chắn rằng điều đó cũng đúng mà chỉ NaNđúngx != x .

Nhiều lập trình viên JavaScript mặc định sử dụng ===!==để tránh một số cạm bẫy xung quanh kiểu ép buộc mà các nhà khai thác lỏng lẻo làm, nhưng không có gì để đọc về việc Flanagan sử dụng toán tử nghiêm ngặt so với lỏng trong trường hợp này.


Tôi có 4.9.1 - Equality and Inequality Operatorsphần đọc lại và đây dường như là câu trả lời. Điểm quan trọng để ===so sánh là : If the two values have the same type, test them for strict equality as described above. If they are strictly equal, they are equal. If they are not strictly equal, they are not equal.
Giorgi Nakeuri

@GiorgiNakeuri: Tôi không chắc 4.9.1 bạn đang đề cập đến điều gì, có lẽ là cuốn sách của Flanagan? Nhưng về cơ bản là nói những gì trích dẫn từ thông số trên nói, vâng.
TJ Crowder

2
Tôi chấp nhận điều này bởi vì điều này trả lời câu hỏi của tôi trong thời trang chính thức và chính xác. Cảm ơn bạn đã giải thích!
Giorgi Nakeuri

1
@Moshe: Ý của bạn là "ràng buộc sống" là gì? (Thuật ngữ này không xuất hiện trong spec.) Bạn có nghĩa là một cái gì đó giống như ví dụ GOTO 0 của nơi athực sự là một chức năng và không trả về giá trị tương tự hai lần? Đó không phải là điều tương tự như một giá trị!==sẽ là sự thật, đó là những gì OP được hỏi về. Nó chỉ là một hàm trả về các giá trị khác nhau. foo() !== foo()cũng không hẳn đúng, vì foocó thể trả về các giá trị khác nhau trên mỗi cuộc gọi.
TJ Crowder

1
@Moshe Vâng, đó là một cách cực kỳ khó chịu để gây rối với các thuộc tính và getters. Nhưng nó dường như khá giống với ví dụ của GOTO 0, chỉ với một lớp bổ sung thêm.
JAB

37

Đối với mục đích của NaN, !=!==làm điều tương tự.

Tuy nhiên, nhiều lập trình viên tránh ==hoặc !=bằng JavaScript. Ví dụ: Douglas Crockford coi chúng là một trong những " phần xấu " của ngôn ngữ JavaScript vì chúng hành xử theo những cách bất ngờ và khó hiểu:

JavaScript có hai bộ toán tử đẳng thức: ===!==, cặp song sinh xấu xa của chúng ==!=. Những người tốt làm việc theo cách bạn mong đợi.

... Lời khuyên của tôi là đừng bao giờ sử dụng cặp song sinh độc ác. Thay vào đó, luôn luôn sử dụng ===!==.


2
Câu hỏi không phải là về NaN (mặc dù tiêu đề). Câu hỏi là "tôi có thiếu một số giá trị trong JavaScript sẽ trả về true cho x! == x và false cho x! = X?"
TJ Crowder

@TJCrowder Hai câu hỏi, thực sự. Câu hỏi đầu tiên là "Có an toàn khi sử dụng cả hai phiên bản không" và câu trả lời là cả hai phiên bản đều tương đương nhau. Tôi thực sự thích câu trả lời "dưới mui xe" của bạn giải thích mọi thứ một cách chi tiết.
jkdev

22

Để cho vui, hãy để tôi chỉ cho bạn một ví dụ nhân tạo xkhông có NaNnhưng các toán tử cư xử khác đi. Xác định đầu tiên:

Object.defineProperty(
  self,
  'x',
  { get: function() { return self.y = self.y ? 0 : '0'; } }
);

Sau đó chúng tôi có

x != x // false

nhưng

x !== x // true

9
Hà! :-) Nhưng đó là cách hiệu quả foo() != foo()khi foo trả về 1 rồi 2. Ví dụ: các giá trị không giống nhau, nó chỉ so sánh các giá trị khác nhau.
TJ Crowder

2

Tôi chỉ muốn chỉ ra NaNkhông phải là thứ duy nhất tạo ra x !== xmà không sử dụng đối tượng toàn cầu. Có rất nhiều cách thông minh để kích hoạt hành vi này. Đây là một cách sử dụng getters:

var i = 0, obj = { get x() { return i++; }};
with(obj) // force dynamic context, this is evil. 
console.log(x === x); // false

Như các câu trả lời khác chỉ ra, ==thực hiện coersion loại, nhưng như trong các ngôn ngữ khác và ngang bằng với tiêu chuẩn - NaN chỉ ra lỗi tính toán và vì lý do chính đáng không bằng chính nó.

Vì một số lý do ngoài tôi, mọi người cho rằng đây là một vấn đề với JS, nhưng hầu hết các ngôn ngữ có gấp đôi (cụ thể là C, Java, C ++, C #, Python và các ngôn ngữ khác) thể hiện hành vi chính xác này và mọi người đều ổn với nó.


2
Vâng, đó chính xác là những gì @TJCrowder đã đề cập trong bình luận cho câu trả lời của GOTO_0, phải không?
Giorgi Nakeuri

Bạn có thể làm rõ làm thế nào để có được sự ép buộc kiểu mơ hồ trong các ngôn ngữ khác này không?
chicocvenancio

0

Đôi khi, hình ảnh tốt hơn từ ngữ, hãy kiểm tra bảng này (đó là lý do để tôi đưa ra câu trả lời thay vì nhận xét là vì nó có khả năng hiển thị tốt hơn).

Ở đó bạn có thể thấy rằng so sánh bình đẳng nghiêm ngặt (===) chỉ trả về đúng nếu loại và nội dung khớp, vì vậy

var f = "-1" === -1; //false

Trong khi so sánh đẳng thức trừu tượng (==) chỉ kiểm tra nội dung * bằng cách chuyển đổi các loại và sau đó so sánh nghiêm ngặt chúng:

var t = "-1" == -1; //true

Mặc dù không rõ ràng, nhưng không tham khảo ECMA , những gì JavaScript xem xét khi so sánh, theo cách mà mã dưới đây đánh giá là đúng.

 var howAmISupposedToKnowThat = [] == false; //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.