Bài viết này bao gồm một số khái niệm cơ bản trong JavaScript. Mọi lập trình viên JavaScript nên biết và thành thạo những điều này.
JavaScript Prototype
Có các cách tạo đối tượng khác nhau trong JavaScript. Một trong những cách để tạo các đối tượng trong JavaScript là constructor. Hãy xem xét hàm constructor dưới đây:
1 2 3 4 5 6 7 8 9 10 11 12 | function Bike(model,color){ this.model = model, this.color = color, this.getDetails = function(){ return this.model+' bike is '+this.color; } } var bikeObj1 = new Bike('BMW','BLACK'); var bikeObj2 = new Bike('BMW','WHITE'); console.log(bikeObj1.getDetails()); //output: BMW bike is BLACK console.log(bikeObj2.getDetails()); //output: BMW bike is WHITE |
Trong ví dụ trên chúng ta đã tạo ra 2 objects: bikeObj1, bikeObj2 dùng 1 constructure. Trong JavaScript, mọi đối tượng đều có các phương thức và thuộc tính riêng. Trong ví dụ trên 2 đối tượng là 2 instances của constructor function getDetails()
Thay vì sử dụng một bản sao của các instances, chúng ta sẽ sử dụng prototype của constructor function.
Prototype
Khi một đối tượng được tạo bằng JavaScript, JavaScript engine sẽ thêm một thuộc tính __proto__
vào đối tượng mới được tạo được gọi là dunder proto
. dunder proto
hoặc __proto__
trỏ đến đối tượng nguyên mẫu của constructor function.
1 2 3 4 5 6 7 8 9 10 | function Bike(model,color){ this.model = model, this.color = color } Bike.prototype.getDetails = function(){ return this.model+" bike is "+this.color; } var bikeObj1 = new Bike(‘BMW’,’Black’); console.log(bikeObj1.getDetails()); |
Đối tượng bikeObj1
được tạo ra bằng Bike constructor function, có dunder proto
hoặc __proto__
trỏ đến đối tượng prototype của constructor function Bike
Trong mã dưới đây, thuộc tính dunder proto
hoặc __proto__
của đối tượng bikeObj1
và thuộc tính Bike.prototype
là bằng nhau, hãy kiểm tra bằng toán tử ===
1 2 3 | console.log(bikeObj1.__proto__ === Bike.prototype ); //output : true |
Vì vậy dùng thuộc tính prototypec thì có bao nhiêu đối tượng được tạo các hàm chỉ được tải vào bộ nhớ một lần và chúng ta có thể ghi đè các hàm nếu cần
JavaScript(ES6) Class
JavaScript classes được giới thiệu trong ES6, chủ yếu là dễ chịu cú pháp trên JavaScript hơn thừa kế dựa trên prototype hiện có. Cú pháp class không giới thiệu một mô hình kế thừa hướng đối tượng mới cho JavaScript. Đầu ES5 sử dụng biểu thức hàm
Kế thừa dựa trên nguyên mẫu hiện có:
1 2 3 4 5 6 7 8 9 | function Bike(model,color) { this.model = model; this.color = color; } Bike.prototype.getInfo = function() { return this.color + ' ' + this.model+ ' bike'; }; |
Classes trên thưc tế là “special functions” và giống như bạn định nghĩa function expressions và function declarations, cú pháp class gồm có 2 thành phần: class expressions và class declarations
ES6 class:
1 2 3 4 5 6 7 | class Bike{ constructor(color, model) { this.color= color; this.model= model; } } |
Lợi ích của việc sử dụng class
- Thuận tiện, cú pháp khép kín.
- Một cách duy nhất, hợp quy để mô tả các class trong JavaScript. Trước ES6, đã có một số triển khai cạnh tranh trong các thư viện phổ biến.
- Quen thuộc hơn với mọi người từ một nền tảng ngôn ngữ dựa trên class.
IIFE
IIFE trong JavaScript là gì?
IIFE (Immediately Invoked Function Expression) là một hàm JavaScript chạy ngay khi được định nghĩa.
1 2 3 4 | (function () {// logic here }) (); |
Lần đâu nghe có vẻ khá khó hiểu nhưng thực sự, pattern rất đơn giản. Các pattern được gọi ngay function expression.
JavaScript functions có thể được tạo ra thông qua một function declaration hoặc function expression. Function declaration là cách tạo ra một hàm bình thường.
Hàm được tạo trong ngữ cảnh của biểu thức cũng là function expression. Điều quan trọng về biểu thức JavaScript là chúng trả về các giá trị.
Trong cả hai trường hợp trên giá trị trả về của biểu thức là hàm. Điều đó có nghĩa là nếu chúng ta muốn gọi biểu thức hàm ngay lập tức, chúng ta chỉ cần giải quyết một vài dấu ngoặc đơn ở cuối. Điều này đưa chúng ta trở lại với đoạn mã đầu tiên mà chúng ta đã xem xét:
1 2 3 4 5 6 7 | (function () { var foo = “hello”; console.log(foo); }) (); console.log(foo); //Error: foo is not defined |
Lý do chính để sử dụng IIFE là để có được quyền riêng tư dữ liệu. Vì JavaScript phạm vi biến var với hàm chứa của chúng, bất kỳ biến nào được khai báo trong IIFE không thể được truy cập bởi bên ngoài.
Understanding Scope:
Một định nghĩa đơn giản cho scope trong JavaScript: là khả năng truy cập của các biến, hàm và đối tượng trong một số phần cụ thể của mã của bạn trong thời gian chạy. Nói cách khác, phạm vi xác định mức độ hiển thị của các biến và các tài nguyên khác trong các khu vực mã của bạn.
Scope được xác định theo hai cách chính:
- Global Scope
- Local Scope
1 2 3 4 5 | var greeting='Welcome to blog'; (function(){ console.log(greeting); //Output: Welcome to blog })(); |
xem xét biến greeting ở mã ở trên là Global Scope, nó có thể truy cập bên trong hàm.
1 2 3 4 5 6 | (function(){ var greeting = 'Welcome to blog'; console.log(greeting); //Output: Welcome to blog })(); console.log(greeting); //Output:Reference-Error greeting not defined |
Trong đoạn mã trên cho local scope
Trong các biến mức phạm vi trong JavaScript ES6 đã cập nhật biến let, var, const type để tìm hiểu scope, bạn có thể tham khải tại đây.
JavaScript Closures
Closures là gì?
là sự kết hợp của một hàm và lexical environment trong đó hàm đó được khai báo. Closures có ba chuỗi phạm vi: nó có quyền truy cập vào phạm vi riêng của nó, nó có quyền truy cập vào các biến ngoài function, và nó có quyền truy cập vào các biến toàn cục. ví dụ :
1 2 3 4 5 6 7 8 9 10 | function User(name){ var displayName = function(greeting){ console.log(greeting+' '+name); } return displayName; } var myFunc = User('Raj'); myFunc('Welcome '); //Output: Welcome Raj myFunc('Hello '); //output: Hello Raj |
Trong mã này, Chúng tôi có một hàm User()
bên ngoài trả về một hàm bên trong là displayName()
. Hàm bên trong sẽ có quyền truy cập vào các biến trong phạm vi hàm ngoài, ngay cả sau khi hàm ngoài đã trả về
The Module Pattern
Trong JavaScript, một module là một đơn vị nhỏ độc lập, có thể tái sử dụng. Các Module là nền tảng của nhiều mẫu thiết kế JavaScript và cực kỳ cần thiết khi xây dựng bất kỳ ứng dụng dựa trên JavaScript nào.
JavaScript module xuất dưới dạng giá trị thay vì xác định loại, vì Module JavaScript có thể xuất một đối tượng, Các Module xuất một chuỗi chứa mẫu HTML hoặc biểu định kiểu CSS cũng rất phổ biến. JavaScript không có từ khóa private nhưng chúng ta có thể đạt được các phương thức và thuộc tính private nhờ closures.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | var myModule = (function() { 'use strict'; var _privateProperty = 'Hello World'; function _privateMethod() { console.log(_privateProperty); } return { publicMethod: function() { _privateMethod(); } }; }()); myModule.publicMethod(); // outputs 'Hello World' console.log(myModule._privateProperty); // is undefined protected by the module closure myModule._privateMethod(); // is TypeError protected by the module closure |
Các module này có thể đã xuất sang các tệp JS khác bằng cách sử dụng từ khóa export
1 2 3 | //myMOdule.js file export default myModule; |
các module có thể nhập vào tệp JS khác
1 2 3 | //second.js file import myModule from ‘./myModule’; |
Tại sao chúng ta cần sử dụng một Module?
Có rất nhiều lợi ích khi sử dụng các module như:
- maintainability
- reusability
- Namespacing
Hoisting
Trong JavaScript, ý nghĩa của là một cơ chế JavaScript trong đó các biến và khai báo hàm được di chuyển lên đầu phạm vi của chúng trước khi thực thi mã.
Một giải thích đơn giản với code sau:
1 2 3 4 | console.log(Hoist); var Hoist = ’The variable Has been hoisted’; //output : undefined// |
Trên thực tế, JavaScript đã kéo khai báo biến lên. Đây là những gì đoạn mã trên trông giống như trình thông dịch
1 2 3 4 | var Hoist; console.log(Hoist); Hoist = ’The variable Has been hoisted’; |
JavaScript chỉ kéo các khai báo lên, không khởi tạo. điều này có nghĩa là bất kể các hàm và biến được khai báo ở đâu, chúng đều được chuyển lên đầu phạm vi của chúng bất kể phạm vi của chúng là toàn cục hay cục bộ. Điều tiếp theo bạn cần biết về Hoisting
- let, var, const keyword in JavaScript (ES6)
- Hoisting Functions
- Hoisting classes
Để biết thêm tham khảo tại đây
Currying
Currying là một kỹ thuật đánh giá hàm với nhiều đối số, thành một chuỗi hàm với một đối số duy nhất. Nói cách khác, khi một hàm, thay vì lấy tất cả các đối số cùng một lúc, sẽ lấy hàm thứ nhất và trả về hàm mới lấy hàm thứ hai và trả về hàm mới lấy hàm thứ ba, và tiếp tục cho đến khi tất cả các đối số đã được hoàn thành. xem xét mã ví dụ dưới đây:
1 2 3 4 5 6 7 8 9 10 | var add = function (a){ return function(b){ return function(c){ return a+b+c; } } } console.log(add(2)(3)(4)); //output 9 console.log(add(3)(4)(5)); //output 12 |
Tại sao currying lại hữu ích
Chủ yếu nó giúp tạo ra một function bậc cao. Nó là rất hữu ích trong việc xử lý sự kiện
Làm thế nào để chuyển đổi một chức năng hiện có thành một phiên bản curried?
Curry function không tồn tại trong JavaScript gốc. Nhưng một thư viện như lodash giúp dễ dàng chuyển đổi một hàm thành một hàm curried