2012-02-21 38 views
43

Một người bạn của tôi đã khám phá ra một số hành vi thú vị trong một số mã Javascript, mà tôi quyết định điều tra thêm.Một hàm lớn hơn một mảng?

Việc so sánh

(function (x) {return x*x;}) > [1,2,3] 

lợi nhuận true trong hầu hết các trình duyệt phổ biến (Firefox, Chrome, Opera và Safari) và false trong IE9. Với tôi, không có kết quả logic nào so sánh này ngoài undefined vì không có cách nào để nói rằng một hàm lớn hơn một mảng.

Đọc về điều này trong tiêu chuẩn ECMA-script, nó nói rằng các đối số thực tế của > khi nó được sử dụng trên các đối tượng là kết quả của việc gọi hoạt động nội bộ ToNumber trên các đối số. Một số thử nghiệm và đọc thêm cho tôi biết rằng điều này không giống như việc áp dụng chuyển đổi loại như (Number) arg. Đọc đặc điểm kỹ thuật, tôi có một thời gian khó khăn để tìm ra những gì đang xảy ra ở đây.

Mọi người có thể điền vào tôi những gì đang thực sự xảy ra ở đây không?

+5

tất nhiên chức năng lớn hơn mảng, nó có thể tạo ra các mảng heckuvalotta ;-) –

+5

Bạn chưa đọc đủ của spec được nêu ra. Toán tử '>' cũng có thể so sánh các chuỗi. – Pointy

+1

@Pointy, thực sự. 'f' lớn hơn '1' (có nghĩa ẩn không?). –

Trả lời

54

Các toán hạng tới > không nhất thiết phải được chuyển đổi thành số. Các cuộc gọi abstract relational comparison algorithm gọi ToPrimitive với gợi ý Number, nhưng ToPrimitive vẫn có thể trả về một chuỗi (và trong trường hợp cả chức năng lẫn mảng).

Vì vậy, bạn sẽ chỉ so sánh hai chuỗi. Kết quả của việc gọi toString on function objects không được xác định bởi spec, mặc dù hầu hết các công cụ chính trả lại mã nguồn của hàm (hoặc một số dạng của nó và định dạng khác nhau). Kết quả của việc gọi số toString on arrays cũng giống như số join.

Vì vậy, tỷ lệ cược được rằng bạn sẽ kết thúc về cơ bản làm điều này:

"function (x) {return x*x;}" > "1,2,3" 

Kể từ khi hình thức chính xác của chuỗi cho hàm có thể thay đổi từ trình duyệt đến trình duyệt (và lưu ý Esailija's investigations   — vẻ như IE9 giữ bên ngoài (), Chrome không), không quá ngạc nhiên khi kết quả có thể khác nhau.

+0

Tôi nghĩ rằng mảng biến thành chuỗi thông qua '.join()' ... – Pointy

+1

Cũng có thể là 'hàm() {...' và '1,2,3' (tôi nghĩ đó là những gì Chrome sẽ tạo ra). –

+0

@Pointy: Yeah và 'toString' trên các hàm trả về nguồn (thường, không được bảo đảm). Tôi đã nghĩ về 'Object.prototype.toString.call (func)' - doh! –

60

Trong IE < 9, .toString ing (function (x) {return x*x;}) cho

"(function (x) {return x*x;})" 

Trong thời gian ở chrome nó mang lại:

"function (x) {return x*x;}" 

Nếu bạn so sánh:

"function (x) {return x*x;}" > "1,2,3" // true 
"(function (x) {return x*x;})" > "1,2,3" // false 

Đó là một cách hiệu quả tương tự như so sánh:

"f" > "1" 
"(" > "1" 

Mà là giống như so sánh:

102 > 49 
40 > 49 

Vì vậy, đó là cách chúng tôi nhận được từ một hàm và mảng so với một so sánh số đơn giản :)

+0

Phân tích thú vị về tính không tương thích rõ ràng, được cho phép bởi thông số kỹ thuật. – Abel

+0

Không có trong IE9 của tôi: http://i.imgur.com/1zhLa.png. Tôi chỉ thấy các parens ở chế độ IE8/IE7. Trình duyệt của bạn có ở chế độ tương thích không? – gilly3

+0

@ gilly3 true Tôi đã có chế độ IE7, OP phải có cùng một sai lầm khi nó trả về true cho so sánh (giống như các trình duyệt hiện đại) – Esailija

2

Cả hai trình duyệt IE và các trình duyệt khác sẽ sử dụng so sánh chuỗi giống nhau cho cả hai đối tượng.Lý do cho sự khác biệt là IE sẽ chuyển đổi chức năng thành chuỗi chữ như đã nhập:

(function (x) {return x*x;}) 

Các trình duyệt khác (thử nghiệm trên Firefox) sẽ ra nó giải thích biên soạn riêng của hàm:

function (x) { 
    return x * x; 
} 

Kể từ ký tự đầu tiên của biểu diễn hàm của IE là (, cao hơn 1, nó sẽ trả về false. Vì số f thấp hơn 1, các trình duyệt khác sẽ trả về giá trị true.

5

Hãy đi sâu vào Đặc tả ECMA. Tôi đã bao gồm các số phần để bạn có thể tham khảo.

11.8.2 Các Greater hơn Operator (>)

Các RelationalExpression sản xuất: RelationalExpression> ShiftExpression được đánh giá như sau:

  1. Hãy lref là kết quả của việc đánh giá RelationalExpression .
  2. Cho phép nhận tiền là GetValue (lref).
  3. Cho rref là kết quả của việc đánh giá ShiftExpression.
  4. Hãy để rval là GetValue (rref).
  5. Cho r là kết quả của việc thực hiện so sánh quan hệ trừu tượng rval < lval với LeftFirst bằng false. (xem 11.8.5).

Phần quan trọng trong đó là Tóm tắt Relational So sánh. Được định nghĩa:

11.8.5 Tóm tắt Relational So sánh thuật toán

Chức năng toPrimitive đầu tiên sẽ được gọi trên đối tượng. Mặc dù điều này là thiên vị để trả về Numbers nếu nó có thể, Strings cũng có thể được bắt nguồn. Khi điều này xảy ra, những điều sau đây sẽ được kiểm tra:

a. Nếu py là tiền tố của px, trả về false. (Giá trị chuỗi p là tiền tố của giá trị chuỗi q nếu q có thể là kết quả của kết nối p và một số chuỗi khác. Lưu ý rằng bất kỳ chuỗi là tiền tố của nó, vì r có thể là Chuỗi rỗng.)

b. Nếu px là tiền tố của py, trả về true.

c. Gọi k là số nguyên không âm nhỏ nhất sao cho ký tự ở vị trí k trong px khác với ký tự tại vị trí k trong py. (Phải có k như vậy, vì không phải String là tiền tố của giá trị kia.)

d. Gọi m là số nguyên là giá trị đơn vị mã cho ký tự tại vị trí k trong px. e. Gọi n là số nguyên là giá trị đơn vị mã cho ký tự tại vị trí k trong py. f. Nếu m < n, trả về true. Nếu không, trả về false.

Điều này có nghĩa là ký tự đầu tiên trong chuỗi khác với chuỗi kia sẽ được kiểm tra. Vì nó đã được chỉ ra bởi Esailija, chức năng toString() của IE trả về một chuỗi hơi khác với chuỗi của các trình duyệt khác, dẫn đến việc so sánh khác nhau diễn ra.

Sự khác biệt này giữa các trình duyệt dường như là hợp lệ như được nêu ở đây:

15.2.4.4 Object.prototype.valueOf()

Khi phương pháp valueOf được gọi, các bước sau đây được lấy:

  1. Cho O là kết quả của việc gọi ToObject chuyển giá trị này làm đối số.
  2. Nếu O là kết quả của việc gọi hàm tạo đối tượng với đối tượng lưu trữ (15.2.2.1), thì a. Trả về giá trị O hoặc giá trị khác như đối tượng lưu trữ ban đầu được truyền cho hàm tạo. Kết quả cụ thể của được trả về là định nghĩa thực hiện.
  3. Return O.
Các vấn đề liên quan