Mẹo với Javascript (ES6) và thủ thuật để làm cho code sạch hơn, ngắn hơn, và dễ đọc hơn (Phần 1)
JS Template literals Template literals là gì? Nó cho phép lập trình viên có thể nhúng biểu thức JavaScript (hoặc multi-line string, biến, hàm…) bên trong chuỗi string một cách dễ dàng. Thông thường cú pháp khai báo string là dấu ' hoặc " , đối với template literals thì ta khai ...
JS Template literals
Template literals là gì? Nó cho phép lập trình viên có thể nhúng biểu thức JavaScript (hoặc multi-line string, biến, hàm…) bên trong chuỗi string một cách dễ dàng. Thông thường cú pháp khai báo string là dấu ' hoặc " , đối với template literals thì ta khai báo bằng dấu ` gọi là dấu back-tick. Nó giúp chúng ta làm việc với string dễ dàng hơn trước, muốn sử dụng nó thì dùng ${bien} . Xem ví dụ bên dưới:
1 2 3 4 5 |
var fName = 'Peter', sName = 'Smith', age = 43, job= 'photographer'; var a = 'Hi, I'm ' + fName + ' ' + sName + ', I'm ' + age + ' and work as a ' + job + '.'; var b = `Hi, I'm ${ fName } ${ sName }, I'm ${ age } and work as a ${ job }.`; |
Điều này giúp code của bạn dễ đọc hơn. Bạn có thể đặt bất cứ điều gì bên trong của dấu ngoặc nhọn: biến, phương trình, hoặc các function call.
Block Scoping
Trong JS, scope đề cập đến ngữ cảnh hiện tại trong code của bạn. Scope có thể được xác định trên globally hoặc locally . Hiểu về scope trong js là chìa khóa để viết code chống lỗi và trở thành developer tốt hơn. Bạn sẽ hiểu được ở đâu variables/functions có thể được truy cập, có thể thay đổi scope của ngữ cảnh trong code của bạn, nói nôm na là có thể viết code nhanh hơn và dễ maintain hơn, cũng như debug dễ dàng hơn.
- Block Scoped là phạm vi trong một khối, nghĩa là chỉ hoạt động trong phạm vi được khai báo bời cặp {...}.
- ở Es6 người ta sử dụng biến let và const để khai báo cho biến trong cặp {}
Tìm hiểu sâu hơn về scope javascript.
Khai báo ‘Let’
Điều này cũng tương tự như var nhưng có một vài khác biệt đáng chú ý. Vì với phạm vi một khối, một biến mới có cùng tên có thể được khai báo mà không ảnh hưởng đến các biến bên ngoài.
1 2 3 4 5 6 7 8 |
var a = 'car' ; { let a = 5; console.log(a) // 5 } console.log(a) // car |
Vì nó bị ràng buộc thành một block scope, nó giải quyết câu hỏi này:
“Đầu ra là gì, và làm thế nào bạn sẽ làm cho nó hoạt động như chúng ta mong đợi?”
1 2 3 4 5 |
for (var i = 1; i < 5; i++){ setTimeout(() => { console.log(i); }, 1000); } |
Trong trường hợp này nó xuất ra “5 5 5 5 5” bởi vì biến i thay đổi trên mỗi lần lặp.
Nếu bạn chuyển var thành let sau đó tất cả mọi thứ thay đổi . Bây giờ, mỗi vòng lặp tạo ra một block scope mới với giá trị bị ràng buộc với vòng lặp đó. Đó là mặc dù bạn đã viết:
1 2 3 4 5 6 7 |
{let i = 1; setTimeout(() => { console.log(i) }, 1000)} {let i = 2; setTimeout(() => { console.log(i) }, 1000)} {let i = 3; setTimeout(() => { console.log(i) }, 1000)} {let i = 4; setTimeout(() => { console.log(i) }, 1000)} {let i = 5; setTimeout(() => { console.log(i) }, 1000)} |
Một khác biệt giữa var và let là let không bị “hoisted” như var .
Note: hoisted nghĩa là trong Javascript bạn có thể định nghĩa một biến sau khi sử dụng nó, hay nói cách khác một biến có thể được sử dụng trước và định nghĩa sau.
1 2 3 4 5 6 7 8 |
{ console.log(a); // undefined console.log(b); // ReferenceError var a = 'car'; let b = 5; } |
Bởi vì hành vi của nó chặt chẽ và dễ dự đoán hơn, một số người đã nói rằng bạn nên sử dụng let thay vì var, trừ khi bạn đặc biệt cần hoisting hoặc looser scoping mà var khai báo.
Const
Nếu bạn muốn khai báo một biến không đổi trong JavaScript trước đó, nó đã được quy ước để đặt tên cho biến trong block cap. Tuy nhiên, điều này sẽ không secure biến – nó chỉ cho phép các developer khác biết rằng đó là một hằng số và không nên thay đổi.
Bây giờ chúng ta khai báo biến const.
1 2 3 4 5 6 7 |
{ const c = "tree"; console.log(c); // tree c = 46; // TypeError! } |
const không làm cho biến không thể thay đổi, mà nó chỉ gán cứng giá trị cho biến. Nhưng nếu bạn có một công việc phức tạp (object hay array), thì giá trị vẫn có thể được sửa đổi.
1 2 3 4 5 6 7 8 9 10 |
{ const d = [1, 2, 3, 4]; const dave = { name: 'David Jones', age: 32}; d.push(5); dave.job = "salesman"; console.log(d); // [1, 2, 3, 4, 5] console.log(dave); // { age: 32, job: "salesman", name: 'David Jones'} } |
Vấn đề với block scoping function
Các khai báo Function bây giờ được chỉ định để ràng buộc block scoping.
1 2 3 4 5 6 7 |
{ bar(); // works function bar() { /* do something */ } } bar(); // doesn't work |
Vấn đề xảy ra khi bạn khai báo một function bên trong câu lệnh if.
Cùng xem xét điều này:
1 2 3 4 5 6 7 8 |
if ( something) { function baz() { console.log('I passed') } } else { function baz() { console.log('I didn't pass') } } baz(); |
Trước ES6, cả hai khai báo function đã được “hoisted” và kết quả sẽ là ‘I didn’t pass’. Bây giờ chúng ta nhận được ‘ReferenceError’, như baz luôn bị ràng buộc bởi block scope.
Spread
ES6 giới thiệu ... operator, được gọi là ‘spread operator’ (Spread Operator cho phép chuyển đổi một chuỗi thành nhiều argument (trong trường hợp gọi với hàm) hoặc thành nhiều phần tử (cho array). Thêm vào nữa nó cũng cho phép làm nhiệm vụ destructure. Operator này có syntax là 3 dấu chấm ...). Nó có hai cách sử dụng chính: truyền một array hay object vào một array hay object mới, và đưa nhiều tham số vào một array.
Trường hợp sử dụng đầu tiên là trường hợp bạn dễ gặp phải nhất, vì vậy chúng ta sẽ hãy xem xét nó đó đầu tiên.
1 2 3 4 5 |
let a = [3, 4, 5]; let b = [1, 2, ...a, 6]; console.log(b); // [1, 2, 3, 4, 5, 6] |
Điều này có thể rất hữu ích cho việc đẩy một tập các biến sang một function từ một array.
1 2 3 4 5 |
function foo(a, b, c) { console.log(`a=${a}, b=${b}, c=${c}`)} let data = [5, 15, 2]; foo( ...data); // a=5, b=15, c=2 |
Một object cũng có thể được truyền vào, nhập mỗi cặp giá trị quan trọng vào object mới.
1 2 3 4 5 |
let car = { type: 'vehicle ', wheels: 4}; let fordGt = { make: 'Ford', ...car, model: 'GT'}; console.log(fordGt); // {make: 'Ford', model: 'GT', type: 'vehicle', wheels: 4} |
Một tính năng khác của spread operator là nó tạo ra một array hoặc object mới. Ví dụ dưới đây tạo ra một array mới cho b, nhưng c chỉ refer đến cùng một array.
1 2 3 4 5 6 7 8 9 10 11 |
let a = [1, 2, 3]; let b = [ ...a ]; let c = a; b.push(4); console.log(a); // [1, 2, 3] console.log(b); // [1, 2, 3, 4] referencing different arrays c.push(5); console.log(a); // [1, 2, 3, 5] console.log(c); // [1, 2, 3, 5] referencing the same array |
Trường hợp còn lại là thu thập các biến với nhau thành một array. Điều này rất hữu ích khi bạn không biết có bao nhiêu biến được truyền vào một function.
1 2 3 4 5 6 |
function foo(...args) { console.log(args); } foo( 'car', 54, 'tree'); // [ 'car', 54, 'tree' ] |
Tham số mặc định
Các Function có thể được định nghĩa cùng các tham số mặc định. Thiếu hoặc không xác định giá trị được khởi tạo với giá trị mặc định. Hãy cẩn thận – bởi vì giá trị null và false bị ép buộc là 0.
1 2 3 4 5 6 7 8 9 10 |
function foo( a = 5, b = 10) { console.log( a + b); } foo(); // 15 foo( 7, 12 ); // 19 foo( undefined, 8 ); // 13 foo( 8 ); // 18 foo( null ); // 10 as null is coerced to 0 |
Giá trị mặc định có thể nhiều định dạng hơn – chúng cũng có thể là các biểu thức hoặc các hàm.
1 2 3 4 5 6 7 8 9 |
function foo( a ) { return a * 4; } function bar( x = 2, y = x + 4, z = foo(x)) { console.log([ x, y, z ]); } bar(); // [ 2, 6, 8 ] bar( 1, 2, 3 ); //[ 1, 2, 3 ] bar( 10, undefined, 3 ); // [ 10, 14, 3 ] |
TopDev via Freecodecamp