Hướng dẫn học thuật toán javascript

Hôm nay mình sẽ giới thiệu với các bạn một số những tips và tricks để giúp bạn code Javascript một cách hiệu quả cũng như "ngầu hơn" giúp tăng năng suất và hiệu quả công việc. :D Bắt đầu nhé.

Nhắc đến Front-end chắc chắn không thể bỏ qua được Javascript, một ngôn ngữ mà được đánh giá là khó và cực kì "hack não" nhất là đối với các anh em mới vào nghề. Tuy vậy, không thể phủ nhận được sức mạnh của Javascript đối với Front-end được. Và đây là các thủ thuật hỗ trợ đắc lực cho các lập trình viên khi sử dụng ngôn ngữ này.

1. Loại bỏ các phần tử trùng lặp trong mảng

Các bạn có 1 mảng gồm nhiều phần tử nhưng có 1 vài phần tử bị trùng lặp, bạn không muốn sự xuất hiện của các phần tử trùng lặp đó? Thuật toán bình thường và dễ nhất đó là tạo 1 mảng mới rồi duyệt lần lượt mảng cũ, tại mỗi phần tử kiểm tra xem có trong mảng mới chưa, nếu chưa có thì push vào, có rồi thì bỏ qua. Hmm, cũng khá là đơn giản nhỉ, nhưng chúng ta có 1 cách khác còn nhanh hơn rất nhiều. Bằng cách sử dụng Set (được giới thiệu từ ES6) và toán tử ..., mọi thứ trở nên cực kì dễ dàng như sau: 

const arr = [1, 1, 1, 2, 2, 2, 3, 5, 3, 2];
const newArr = [...new Set(arr)];
console.log(newArr); // [ 1, 2, 3, 5 ]

Set là cấu trúc dữ liệu mà các phần tử trong Set lúc nào cũng là duy nhất, bằng việc convert mảng ban đầu sang Set chúng ta sẽ loại bỏ được hết các phần tử trùng lặp, việc còn lại chỉ còn là convert ngược lại Set này về mảng mà thôi

Chú ý là cách này chỉ áp dụng cho mảng gồm các kiểu dữ liệu nguyên thủy (Number, String, Null, Undefined, Symbol) . Nếu là 1 mảng các object thì các bạn có thể tham khảo code snippet số 12 tại đây đây

2. Loại bỏ các falsy values trong 1  mảng. 

Falsy values trong Javascript bao gồm có 0, false, NaN, undefined, null, '' (xâu rỗng).

Ngược lại với nó thì ta có khái niệm truthy values. Trong JavaScript, truthy là giá trị được hiểu là true trong ngữ cảnh Boolean. Tất cả mọi giá trị đều là truthy, trừ các falsy values kể trên. 

Giả sử ta có 1 mảng ban đầu gồm có các falsy valuestruthy values, ta muốn loại bỏ toàn bộ các falsy values chỉ giữ lại các truthy values thôi. Có rất nhiều cách để thực hiện điều này, hôm nay mình muốn giới thiệu đến các bạn 1 cách làm đơn giản mà hiệu quả nhất đó là sử dụng filter kết hợp với Boolean.

const arr = [
	0,
	false,
	NaN,
	undefined,
	"",
	null,
	1,
	true,
	"Hello World",
	{ name: "value" },
];
const filteredArr = arr.filter(Boolean);
console.log(filteredArr); // [ 1, true, 'Hello World', { name: 'value' } ]

3. Viết biểu thức điều kiện một cách ngắn gọn và mạnh mẽ hơn.

Ví dụ 1 đoạn code như sau: 

const a = 1;
const b = 1;
if (a === b) console.log("a = b");
else console.log("a != b");

Nhưng thay vì dùng if, các bạn có thể sử dụng ternary operator để viết 1 cách ngắn gọn hơn như sau:

a === b ? console.log("a = b") : console.log("a != b");

Hoặc: 

console.log(`${a === b ? "a = b" : "a != b"}`);

Có 1 cách khác đó là sử dụng toán tử &&||

Giải thích về 2 toán tử &&||

Với 1 chuỗi các lựa chọn thì && sẽ trả về falsy value đầu tiên trong danh sách, nếu tất cả các toán hạng đều true thì sẽ trả về toán hạng cuối cùng.

Ví dụ:

const a = 1;
const b = 2;

console.log(a && b); // 2
console.log(0 && 1); // 0

Ngược lại với &&, toán tử || sẽ trả về truthy value đầu tiên trong danh sách, nếu tất cả các toán hạng đều là false thì sẽ trả về toán hạng cuối cùng

Ví dụ: 

const a = 1;
const b = 2;

console.log(a || b); // 1
console.log(0 || 1); // 1

Có thể viết lại biểu thức ở ví dụ đầu tiên như sau: 

console.log(`${(a == b && "a = b") || "a != b"}`);

Trông có vẻ cũng không khác nhau mấy nhỉ. Vậy thì hãy xét các ví dụ khác xem sao. 

Ví dụ: 

const data = await fetch([api]);
if (data) {
    console.log(data.length);
} else {
    console.log(0);
}​

Ta có thể viết lại 1 cách ngắn gọn hơn như sau: 

console.log(data.length || 0);

Nếu data.lengthtruthy value thì sẽ lấy giá trị của data.length, nếu không thì sẽ lấy giá trị 0

Sử dụng kết hợp &&||

ví dụ: 

const response = {
	message: "",
	success: true,
	result: {
		name: "response-list",
		list: [1, 2, 3, 4],
	},
};

// Lấy giá trị của result
const result = (response && response.result) || "Default result";
// Lấy giá trị của 1 thuộc tính không tồn tại trong object không sử dụng ||
const unknownProperty =
	response && response.result && response.result.unknownProperty;
// Lấy giá trị của 1 thuộc tính không tồn tại trong object sử dụng ||
const unknownPropertyWithDefaultValue =
	(response && response.result && response.result.unknownProperty) ||
	"Default Value";
console.log(result); // { name: 'response-list', list: [ 1, 2, 3, 4 ] }
console.log(unknownProperty); // undefined
console.log(unknownPropertyWithDefaultValue); // "Default Value"

với cách sử dụng như trên, bất cứ khi nào 1 trong số các toán tử ở phía trước dấu || trả về falsy value thì dữ liệu sẽ được gán = []. Đây cũng là 1 điều rất được khuyến khích trong khi làm các sản phẩm thực tế tránh việc ứng dụng của chúng ta gặp các lỗi phát sinh vì dữ liệu trả về không chính xác hoặc không lấy được dữ liệu. 

Ngoài ra có 1 cách viết ngắn gọn hơn cho cách trên đó là sử dụng Optional Chaning. Optional Chaning sử dụng kí tự ? để lấy thuộc tính khi object không phải là null. 

Viết lại ví dụ trên sử dụng Optional Chaing

const response = {
	message: "",
	success: true,
	result: {
		name: "response-list",
		list: [1, 2, 3, 4],
	},
};

// Lấy giá trị của result
const result = response?.result ?? "Default result";
// Lấy giá trị của 1 thuộc tính không tồn tại trong object không sử dụng ||
const unknownProperty = response?.result?.unknownProperty;
// Lấy giá trị của 1 thuộc tính không tồn tại trong object sử dụng ||
const unknownPropertyWithDefaultValue =
	response?.result?.unknownProperty ?? "Default Value";
console.log(result); // { name: 'response-list', list: [ 1, 2, 3, 4 ] }
console.log(unknownProperty); // undefined
console.log(unknownPropertyWithDefaultValue); // "Default Value"

Có thể nhiều bạn sẽ thắc mắc về toán tử ?? , về cơ bản thì toán tử ?? khá giống với ||. Ngoại trừ việc nếu || trả về giá trị truthy đầu tiên thì ?? trả về giá trị defined đầu tiên. 

1 ví dụ cụ thể cho thấy sự khác nhau giữa || và ??. 

let height = 0; 

alert(height || 100); // 100 
alert(height ?? 100); // 0

Ở đây thì 0 là 1 falsy value. Khi sử dụng || thì kết quả sẽ trả về là 100 do 100 là giá trị truthy đầu tiên. Còn khi sử dụng ?? thì ta thấy kết quả trả về là 0, bởi vì như mình nói ở trên, ?? lấy giá trị defined đầu tiên và 0 tuy là 1 falsy value nhưng vẫn là 1 defined value nên ta có kết quả như trên. 

Các bạn có thể tìm hiểu rõ hơn về ?? tại đây

4. Chuyển xâu về số

Chuyển 1 xâu về số có thể được thực hiện một cách nhanh chóng bằng việc sử dụng toán tử cộng

const strInt = "10";
const number = +strInt;
console.log("number", number); // 10
console.log("typeof number", typeof number); // number

Ngoài ra nó cũng có thể được sử dụng thể chuyển giá trị boolean về number

console.log(+true);  // Return: 1
console.log(+false); // Return: 0​

Có 1 cách khác nữa là sử dụng ~~

Về bản chất thì nếu 1 dấu ~ thường được biết đến như "bitwsise NOT operator", ~n = - n - 1

Ví dụ: ~15 = -16

Khi sử dụng 2 dấu ~ thì ta sẽ có ~~n = -(-n-1)-1 = n.

const strInt = "10";
const number = ~~strInt;
console.log("number", number); // 10
console.log("typeof number", typeof number); // number

Ngoài ra có 1 điều khá thú vị đó là: ~true = -2~false = -1

Cá nhân mình thì thấy sử dụng ~~ có 1 số điểm mạnh hơn so với sử dụng +

Vì toán tử + chỉ có thể chuyển đổi các xâu "có dạng số" ví dụ như "11", "12", .... Nếu giá trị truyền vào k phải là 1 xâu kiểu số hoặc 1 biểu thức ví dụ như: "Hello", x=10 thì việc sử dụng toán tử + sẽ trả về giá trị là NaN trong khi ~~ sẽ trả về là 0

Nhưng có điểm yếu là việc với xâu dạng số, + sẽ trả vế chính xác giá trị của xâu đó trong khi ~~ trả về giá trị tương đương với Math.floor()

const strArr = ["10", "Str", (x = 23), "false", "10.6"];

strArr.forEach((item) => console.log(+item)); // 10, NaN, NaN, NaN,10.6
strArr.forEach((item) => console.log(~~item)); // 10, 0, 23, 0,10

5. Xáo trộn mảng 

const initialArr = [1, 2, 3, 4, 5];
const shuffledArr = initialArr.sort(() => 0.5 - Math.random());
console.log(shuffledArr); //  [ 1, 5, 2, 3, 4 ]

6. Sử dụng biến làm thuộc tính của object

Ví dụ: 

const propertyName = "Rikikudo";

const object = {
	[propertyName]: "Value",
};

console.log("object", object); // object { Rikikudo: 'Value' }

Với cách này, các bạn có thể thay đổi tên thuộc tính của object 1 cách tùy ý dựa theo 1 biến bên ngoài. 

7. Object To Array

const arr = [1, 2, 3, 4, 5, 6];
const obj = { ...arr };
console.log(obj); // { '0': 1, '1': 2, '2': 3, '3': 4, '4': 5, '5': 6 }

8. Reset Array

Bạn muốn xóa toàn bộ dữ liệu của 1 mảng 1 cách nhanh chóng và dễ dàng? Cực kì đơn giản, chỉ cần 1 câu lệnh sau: arr.length = 0;

var array = [11, 12, 13, 14, 15];  
console.log(array.length); // 5  

array.length = 0;  
console.log(array.length); // 0  
console.log(array); // []

9. Tráo đổi giá trị của 2 biến.

let a = 1,
	b = 2;
let c = "c",
	d = "d";

[a, b] = [b, a];
[c, d] = [d, c];

console.log(`a: ${a}, b: ${b}, c: ${c}, d: ${d}`); //a: 2, b: 1, c: d, d: c

10. Hợp nhất nhiều objects vào 1 object duy nhất

const obj1 = {
	a: 1,
	b: 2,
};
const obj2 = {
	c: 3,
	d: 4,
};
const obj3 = {
	e: 5,
	f: 6,
};
const obj4 = {
	g: 7,
	h: 8,
};
const obj5 = {
	i: 9,
	j: 10,
};
const mergedObj = {
	...obj1,
	...obj2,
	...obj3,
	...obj4,
	...obj5,
};

console.log(mergedObj); //{ a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10 }

Chú ý là khi hợp nhất các objects, nếu trong trường hợp có 2 (hoặc nhiều) objects có cùng thuộc tính thì giá trị của thuộc tính đó sẽ là giá trị gán với thuộc tính của object được hợp nhất sau cùng. 

Ví dụ: 

const obj1 = {
	a: 1,
	b: 2,
};
const obj2 = {
	c: 3,
	d: 4,
};
const obj3 = {
	a: 5,
	f: 6,
};
const obj4 = {
	g: 7,
	h: 8,
};
const obj5 = {
	a: 9,
	j: 10,
};
const mergedObj = {
	...obj1,
	...obj2,
	...obj3,
	...obj4,
	...obj5,
};

console.log(mergedObj); // { a: 9, b: 2, c: 3, d: 4, f: 6, g: 7, h: 8, j: 10 }

Lời kết

Trên đây mình đã giới thiệu với các bạn 1 số tips và tricks trong Javascript mà mình thấy rất hữu ích. Dù có nhiều ý kiến trái chiều về việc có nên sử dụng 1 số tips và tricks trong bài hay không tuy nhiên cá nhân mình thấy chúng vẫn là những thủ thuật rất hay và có thể được áp dụng vào thực tế đương nhiên là phải biết vận dụng chính xác. :D Mong nhận được ý kiến phản hồi và góp ý từ các bạn.

Nguồn tham khảo: 

Stackoverflow.com / Dev.to / Medium.com / Javascript.info