❗QUAN TRỌNG: Có một số tính năng được nhắc đến trong bài viết này đã không còn được dùng nữa, EcmaScript đã cấm sử dụng chúng. Nhưng vẫn có thể tìm thấy chúng trong nhiều thư viện khác, do vậy, vẫn rất đáng để chúng ta tìm hiểu.
Tagged Template Literals
Nếu đã từng sử dụng styled-components
trong React, thì chính xác bạn đã sử dụng các ký tự mẫu được gắn thẻ (tagged template literals
).
Chúng cho phép ta kiểm soát việc parse
các ký tự thông qua một function
một cách hiệu quả hơn.
taggingThis`Hey!!, I'm ${name} !!`
Ở trên, taggingThis
là một function có tham số đầu tiên là một mảng các strings, tham số còn lại liên quan đến các biểu thức (expressions).
Bên trong hàm, chúng ta có thể thao tác với strings
để trả về kết quả mong muốn.
Trong ví dụ trên, tham số strings
tương ứng sẽ là [ "Hey!!, I'm ", '"!!" ]
, và phần còn lại của các tham số sẽ được truyền vào mảng, vals["Alex"]
.
Toán tử , (dấu phẩy)
,
là một toán tử phân tách các biểu thức và trả về biểu thức cuối cùng trong chuỗi.
Ví dụ, khi muốn viết hàm lambda ngắn hơn
Hàm test()
thực hiện 2 việc, đầu tiên push kết quả a*b
vào mảng array, và thực hiện tính toán a*b
.
Khi gọi hàm, kết quả trả về là kết quả của a*b
.
with ⚠
with
bị cấm hoàn toàn trong strict mode
nên nó không còn được khuyến khích sử dụng nữa. Ngoài ra, khi sử dụng block
với with
có thể gặp phải một số vấn đề về hiệu năng và bảo mật.
with
là một keyword
trong JS, được dùng để mở rộng phạm vi chuỗi các lệnh:
with(expression) statement
hoặc
with(expression) { statement statement ...
}
Đánh giá expression
và tạo ra một scope
quanh expression
đó. expression
và scope cha
của with
là available bên trong {}.
with
gói exp
bên trong chuỗi scope. exp
và các biến khác được khai báo bên ngoài block vẫn có thể truy cập được từ bên trong block.
Nhưng với các biến let
và const
được khai báo bên trong block with
thì chỉ truy cập được trong block đó, nó không thể sử dụng được ở bên ngoài. Khi cố gắng truy cập, sẽ tạo ra một ReferenceError
.
in
Chúng ta đã quá quen thuộc với vòng lặp for..in
, nhưng chắc hẳn nhiều người không nhận ra in
là một keyword
trong JS :v.
Nó được sử dụng để kiểm tra sự tồn tại của một thuộc tính trong một đối tượng, trả về true
nếu đối tượng có thuộc tính đó và ngược lại.
Ví dụ,
Array constructor
Thứ tự của các tham số được truyền vào constructor sẽ tương ứng là index của chúng trong mảng. Tương tự như sử dụng array literal:
const array = [1, 2, 3]
- Tạo mảng với new Array() thường áp dụng khi tham số lớn hơn hoặc bằng 2.
const array = new Array(1, 2, 3)
- JS sẽ engine phân bố không gian cho mảng với kích thước bằng với giá trị tham số truyền vào.
sẽ tạo ra một mảng với 4 phần tử và length là 4. Tương tự như
const array1 = [, , , , ]
Function constructor
const mul = new Function("a", "b", "return a * b")
tương tự với
const mul = (a, b) => a * b
hay
function mul(a, b) { return a * b
}
hoặc
const mul = function(a, b) { return a * b
}
Các biến truyền cho Function()
tạo thành các tham số đầu vào và phần thân của hàm.
Biến mul
là tên hàm, biến cuối cùng là phần thân hàm, và các biến phía trước là tham số của hàm.
Theo MDN:
Gọi trực tiếp constructor có thể tạo các hàm động, nhưng nó sẽ gặp phải các vấn đề liên quan tới bảo mật và hiệu năng tương tự như eval. Tuy nhiên, không giống eval, Function constructor tạo ra các hàm chỉ thực thi trong phạm vi toàn cục.
Destructuring Array
Chúng ta có thể hủy cấu trúc các phần tử trong một mảng bằng cách sử dụng index
của chúng.
Về bản chất, Array cũng là đối tượng. Do vậy, chúng ta có thể hủy cấu trúc của Array tương tự như khi thực hiện trên object.
Trên object,
Tương tự, cho Array
Sử dụng index
để trích xuất các phần tử. Index
là thuộc tính xác định vị trí của các phần tử trong mảng. Vì vậy,
const array1b = ['a', 'b', 'c']
tương đương với:
const array1b = { 0: 'a', 1: 'b', 2: 'c', length: 3
}
Ngoài ra, cũng có một cú pháp hủy cấu trúc mảng đặc biệt:
tránh việc phải biết thông tin vị trí cụ thể các phần tử trong mảng.
Thay đổi Array bằng cách sử dụng thuộc tính length
Thuộc tính length
trong một mảng cho biết số phần tử trong mảng.
const arr = [1, 2, 3]
arr.length // 3
Thay đổi length
sẽ làm cho JS engine thay đổi các phần tử mảng (từ bên phải) sao cho số lượng các phần tử bằng với giá trị length
.
const arr = [1, 2, 3]
arr.length = 1
arr
// [1]
Ngược lại, nếu tăng length
, JS engine sẽ thêm các phần tử (undefined
) để làm cho số lượng các phần tử trong mảng tăng đến giá trị bằng length.
Arguments
Chúng ta có thể sử dụng object arguments
để lấy các tham số được truyền vào một hàm mà không cần xác định rõ ràng các biến tham số trong hàm:
Object arguments
tương tự một array-indexed
với các key là index tương ứng.
arguments object
được khởi tạo từ Arguments class
nên có một số thuộc tính khá thú vị.
arguments.callee.name
: tham chiếu tới name của function đang được gọi.
function myFunc () { console.log(arguments.callee.name) // myFunc
}
myFunc(1, 2)
arguments.callee.caller.name
: tham chiếu tới name của function chứa function đang được thực thi.
function myFunc() { console.log(arguments.callee.name) // myFunc console.log(arguments.callee.caller.name) // myFunc1
} (function myFunc1() { myFunc(1, 2)
})()
Bỏ qua dấu ngoặc ()
Bạn có biết rằng chúng ta có thể bỏ qua dấu ngoặc ()
khi khởi tạo một đối tượng không?
class Test { hello() { console.log("Hi!") }
}
(new Test()).hello() // Hi!
(new Test).hello()
// Hi!
Các dấu ngoặc là tùy chọn, ngay cả trong các class có sẵn:
(new Date).getDay()
(new Date).getMonth()
(new Date).getYear()
Toán tử void
void
là một keyword trong JS dùng để đánh giá một câu lệnh và trả về undefined
.
class Test { hello() { return "Hi!" }
}
const test = new Test
console.log(void test.hello()) // undefined
Hãy xem, hàm hello()
sẽ trả về "Hi!"
, nhưng thay vào đó void
sẽ bỏ qua nó và trả về undefined
.
undefined
có thể được gán một giá trị khác trước đó, và điều này đã làm sai lệch ngữ nghĩa của nó. Vì vậy, void
được sử dụng để đảm bảo chúng ta sẽ có một undefined thực sự
.
Thuộc tính hàm
Chúng ta có thể đặt thuộc tính trong các hàm như sau:
function func() { func.prop1 = "a"
}
func.prop2 = "b"
- Các hàm cũng là các đối tượng. Thuộc tính hàm sẽ là một thuộc tính tĩnh cho tất cả các instance của hàm khi được sử dụng như một object.
function func() { func.prop1 = "a"
}
func.prop2 = "b" const ins1 = new func()
const ins2 = new func()
console.log(ins1.prop1) // a
console.log(ins2.prop1) // a
ins1.prop1 = "c"
console.log(ins2.prop1) // c
- Là một thuộc tính toàn cục khi được sử dụng như một hàm.
function func() { func.prop1 === undefined ? func.prop1 = "yes" : null if(func.prop1 === "yes") console.log("Prop with Yes") if (func.prop1 === "no") console.log("Prop with No")
}
func() // Prop with Yes
func.prop1 = "no"
func() // Prop with No
Toán tử một ngôi +
Toán tử một ngôi +
sẽ chuyển đổi toán hạng của nó thành kiểu Number.
Hữu ích khi muốn chuyển đổi nhanh các biến thành Number.
Toán tử một ngôi -
Toán tử một ngôi -
chuyển đổi toán hạng của nó thành kiểu Number và phủ định nó.
Toán tử này đảo ngược kết quả của toán tử một ngôi +
. Đầu tiên, nó chuyển đổi toán hạng thành giá trị Number, sau đó phủ định giá trị.
-"12" // -12
Điều xảy ra ở đây là, string “12”
sẽ được chuyển đổi thành Number của nó là 12
. Sau đó, số dương này sẽ được chuyển đổi thành dạng âm là -12
.
Nếu kết quả của chuyển đổi là NaN
, thì phủ định sẽ không được áp dụng.
Phủ định +0
tạo ra -0
và phủ định -0
tạo ra +0
.
- +0 // -0
- -0 // 0
Toán tử lũy thừa **
Toán tử này được sử dụng để tìm số mũ của một số. Giống như việc tìm lũy thừa của một số được nâng lên một mức độ chính xác.
Trong Toán học, nếu chúng ta nâng 2 lên lũy thừa 3 (tức là tìm số mũ của 2), nghĩa là nhân 2 lên ba lần:
2 * 2 * 2
Chúng ta có thể làm tương tự trong JS bằng cách sử dụng toán tử **
:
2 ** 3 // 8
9 ** 3 // 729
Kế thừa qua __proto__
__proto__
là một cách để kế thừa các thuộc tính từ một đối tượng trong JavaScript. Nó là một thuộc tính của Object.prototype
.
__proto__
sẽ set tất cả các thuộc tính của đối tượng được đặt trong [[Prototype]] của nó thành đối tượng đích.
Ví dụ:
const obj = { method: function() { console.log("method in obj") }
}
const obj2 = {}
obj2.__proto__ = obj
obj2.method()
Chúng ta có hai đối tượng: obj và obj2. obj có thuộc tính method, method. obj2 là một đối tượng rỗng, nghĩa là nó không có thuộc tính.
Truy cập __proto__
của obj2 và đặt nó thành obj. Điều này sẽ sao chép tất cả các thuộc tính của obj có thể truy cập thông qua Object.prototype
sang obj2. Đó là lý do tại sao chúng ta có thể gọi method()
trên obj2 mà không bị lỗi mặc dù nó không được định nghĩa.
obj2 đã kế thừa các thuộc tính của obj, do đó, thuộc tính hàm method sẽ có sẵn trong các thuộc tính của nó.
__proto__
được sử dụng trên các đối tượng như object literal, Object, Array, Function, Date, RegEx, Number, Boolean, String.