JS - Những điều kỳ lạ với Array
Bài đăng này đã không được cập nhật trong 7 năm
Intro
Bài viết được dịch từ JS WTF 🦄 with Arrays, viết những điều trong JS mà khi gặp phải thì những lập trình viên đều phải thốt là WTF 
[] == ![]
[] == ![]   // true
Điều này xảy ra do precedence và việc coercion
Precedence là việc xác định thứ tự mà toán tử được thực hiện. Mức độ ưu tiên cao hơn sẽ được thực hiện sớm hơn. Ví dụ
*có độ ưu tiên cao hơn toán tử+. Đối với coercion , đây là khả năng của JS để chuyển đổi một trong các toán hạng (của toán tử) thành giá trị "tương đương" của toán hạng khác. Điều này diễn ra khi kiểu toán hạng là khác nhau. Ví dụ những phép so sánh dạngboolean == integer, các toán hạng sẽ được chuyển đổi thànhinteger
Thứ tự độ ưu tiên trong phép so sánh [] == ![] sẽ là ![], tiếp theo là ==
Sau đây sẽ là từng bước thực hiện:
- Xem xét biểu thức []
- Chuyển đổi sang boolean, chẳng hạn nhưToBoolean([]). Và theo định nghĩa thì[]có giá trịbooleanlàtrue
- Kết quả falsesau khi thực hiện phép phủ định!củatrue
Ta được
[] == false
Bây giờ là đến việc coercion
Bởi vì toán hạng thứ hai của chúng ta là một boolean, biểu thức sẽ được dịch sang  là  [] == ToNumber(false). ToNumber của boolean sẽ trả về 1 khi true và là 0 khi false
Ta được
[] == 0
Theo đặc tả của ECMA thì [] == 0 được chuyển thành ToPrimitive([]) == 0, từ đó dẫn đến kết quả là OrdinaryToPrimitive([], "number") == 0
OrdinaryToPrimitive cho các tham số Object và "number", sẽ thực hiện cả 2 phương thức .valueOf() và .toString() của Object và trả về kết quả không phải là một object. Trong trường hợp này bởi vì [].valueOf() trả về một object ([]), vì vậy kết quả của [].toString() sẽ được trả về  thay thế ("")
Ta được
"" == 0
Nhiều bước biến đổi rồi nhưng vẫn chưa thấy kết quả cuối cùng là true như đã nói nhỉ. Mọi người hãy kiên nhẫn chùng ta đã gần đến rồi 
Bây giờ, một khi toán hạng đầu tiên là một chuỗi, biểu thức sẽ được chuyển thành ToNumber ("") == 0. Theo định nghĩa, một chuỗi rỗng sẽ được chuyển thành 0.
Ta được
0 == 0
Và thế là kết quả là true 
.apply()
Array.apply(null, Array(3))        // [undefined, undefined, undefined]
Array.apply(null, [,,,])           // [undefined, undefined, undefined]
Array.apply(null, {length : 3})    // [undefined, undefined, undefined]
Array.apply(null, (a,b,c) => {})   // [undefined, undefined, undefined]
Tính năng này được mang đến bởi ECMAScript 5
Kể từ ES5 , ta có thể thực hiện lời gọi Function.prototype.apply() với bất kỳ array-like object (một object giống array nhưng không phải là array). Điều đó có nghĩa là tham số thứ 2 của apply() cần phải có thuộc tính length dạng integer có giá trị nằm trong khoảng từ 0...length - 1.
Bởi vì tất cả 4 ví dụ là array-like object với thuộc tính length, thì apply() sẽ thực hiện Array với mỗi giá trị từ 0 ... length - 1 như là các đối số của nó.
Ví dụ {length: 3} nó có độ dài là 3 và apply () sẽ thực hiện Array như sau:
var example = { length: 3 } // Một array-like object
// Khi thực thi 'Array.apply(null, example)'
// Array sẽ được thực hiện với các đối số của nó là các thuộc tính có giá trị từ 0...length - 1
Array(example[0], example[1], example[2]) 
// Và cuối cùng sẽ thành
Array(undefined, undefined, undefined) // [undefined, undefined, undefined]
.sort()
[10, 9, 8, 3, 2, 1, 0].sort() // [0, 1, 10, 2, 3, 8, 9]
Thực ra đây là cách hoạt động của sort() trong js nhưng nó khá là khác sort của các ngôn ngữ khác
Theo đặc tả, sort() sắp xếp các phần tử của một mảng theo giá trị chuỗi unicode của chúng.
'0'.charCodeAt(0)       // 48
'2'.charCodeAt(0)       // 50
'10'.charCodeAt(0)      // 49
Bởi vì giá trị unicode của 10 nhỏ của 2 nên 10 sẽ đúng trước 2 khi "sắp xếp"
.slice()
[1,2,3].slice(0, null)         // []
[1,2,3].slice(0, undefined)    // [1,2,3]
Bạn có thể đã biết rằng slice() theo đặc tả  "lấy 2 đối số, start and end và trả về một mảng chứa các phần tử của mảng từ phần tử bắt đầu từ start đến end(không bao gồm end)"
Cú pháp:
array.slice(begin, end)
Trong cả 2 ví dụ begin = 0
Tuy nhiên, và mặc dù cả null và undefined đại diện cho sự vắng mặt của một giá trị, kết quả cho end = null lại là không có gì so với kết quả cho end = undefined.
Lý  do là trong đặc tả ECMAScript có nói:
- Nếu "endlà undefined" thì "endlà độ dài của mảng"
- Nếu ngược lại "toInteger(end)" sẽ được chuyển sang thành "toNumber(end)"
Theo định nghĩa, toNumber(null) là 0
Câu lệnh [1, 2, 3].slice (0, null) thực tế là [1, 2, 3].slice (0, 0) và kết quả sẽ là []
References
All rights reserved
 
  
 