+4

JS - Những điều kỳ lạ với Array

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ạng boolean == integer, các toán hạng sẽ được chuyển đổi thành integer

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:

  1. Xem xét biểu thức []
  2. Chuyển đổi sang boolean, chẳng hạn như ToBoolean([]). Và theo định nghĩa thì [] có giá trị booleantrue
  3. Kết quả false sau khi thực hiện phép phủ định ! của true

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"number", sẽ thực hiện cả 2 phương thức .valueOf().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 "end là undefined" thì "end là độ 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

https://hackernoon.com/oh-my-goodnejs-arrays-f9e9e4f03c97


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí