+22

Những tính năng JavaScript bạn có thể chưa từng sử dụng

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 đó. expressionscope 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 letconst đượ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.

Tham khảo


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í