JS - Những điều kỳ lạ với Array
Bài đăng này đã không được cập nhật trong 6 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ịboolean
làtrue
- Kết quả
false
sau 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 "
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
All rights reserved