# Javascript cơ bản Javascript (sau đây gọi là `Js`) là một ngôn ngữ phổ biến để tạo website và ứng dụng web hiện đại ngày nay. Ứng dụng của Js không chỉ là phía client side (trên trình duyệt máy khách) mà còn ở cả server side (máy chủ). Hiện tại với những bạn đang theo frontend thì có thể dùng Js để làm website ứng dụng với sự hỗ trợ của nhiều framework, thư viện như Reactjs, Angular, Vue... viết bằng Js và đang rất phổ biến. Mỗi thư viện, framework đều có ưu điểm, nhược điểm riêng mà tùy vào quy mô, tính chất yêu cầu của dự án hay chỉ đơn giản là phù hợp nhất cho team thì sẽ được chọn để phát triển website, ứng dụng web của bạn. Nhưng những thư viện này đều có một điểm chung là được viết bằng ngôn ngữ Js. Do đó việc chúng ta hiểu rõ, nắm rõ về cú pháp, ngôn ngữ cách dùng của Js chính là một lợi thế vô cùng lớn để có thể "chấp 500 anh em thư viện, framework" :(fab fa-fly): {{< admonition tip >}} :(far fa-bookmark fa-fw): Bạn có thể Bookmark lại trang này để có gì sau này tham khảo khi đang viết markdown mà quên cú pháp {{< /admonition >}} ## 1. Set up cơ bản Học Js cũng không cần phải chuẩn bị gì phức tạp. Ta chỉ cần file `index.html ` có gắn file `app.js` như sau ```html !DOCTYPE html Javascript cơ bản ``` file `app.js` hiện tại không có gì. Vậy tạm thời là xong phần setup cơ bản rồi ## 2. Sử dụng console Sử dụng trình duyệt Google Chrome và mở file `index.html` lên hiện tại sẽ không có gì. Bấm chuột phải vào phần nội dung trang và chọn **Inspect** hoặc bạn có thể bấm tổ hợp phím tắt là `Ctrl + Shift + I` và một cửa sổ hiện lên có nhiều tab bạn chọn tab `console`. Chỗ này chính là nơi mà bạn sẽ làm việc chủ yếu sau này Bạn có thể viết code Js trong cửa sổ console này và bạn có thể viết nhiều dòng code với tổ hợp phím `shift + Enter` để xuống dòng trong console. Ta viết một số dòng Js đầu tiên trong file `app.js` như sau: ```js console.log('Hello from doidev.com') console.log('Học Js vững là chấp 500 anh em framework nha') console.warn('Lệnh này sẽ hiện lên dòng cảnh báo màu vàng') console.error('Lệnh này hiện lên lỗi nè') console.table({name:'đời Dev', weapon:'chém gió'}) ``` {{< admonition >}} Lệnh `console.table` giúp hiển thị nội dung dưới dạng bảng ở console log cho dễ xem {{< /admonition >}} ## 3. Variables (var, let, const) hoisting, scope Trong Js có sử dụng biến `variable` bạn tưởng tượng biến giống như hộp nhỏ để đựng đồ trong đó. Muốn sử dụng biến thì ta phải khai báo với từ khóa `var`, `let` hoặc `const`. Sau từ khóa sẽ là tên biến, sau khi khai báo xong có thể sử dụng phép `gán` bằng dấu bằng `=` trong Js để khai báo giá trị cho biến đó. Khi khai báo biến ta cũng không cần phải khai báo ngay giá trị hoặc kiểu định dạng. Ta cũng có thể gán lại giá trị của biến từ kiểu dữ liệu này sang kiểu dữ liệu khác :(far fa-grin-tears): **nguy hiểm vãi** > Quy tắc đặt tên biến là tên biến phải bắt đầu bằng chữ cái hoặc ký tự gạch dưới `_`. Không được bắt đầu bằng số >
Tên biến có thể đặt tên theo quy tắc camelCase, Pascal hoặc underscore (gạch dưới) Ví dụ:
var firstName = 'DEV' `// tên đặt theo quy tắc camelCase`
let Last Name = 'đời' `// tên đặt theo quy tắc Pascal`
const middle_name = 'chém gió'`// tên đặt theo quy tắc underscore` ### Cơ chế hoisting #### với biến var Cơ chế hoisting hiểu đơn giản là khi ta khai báo biến (và cả hàm nữa) bằng từ khóa `var` thì Js mặc định sẽ đưa biến đó lên đầu của scope (phạm vi) mà biến đó có thể được gọi. Lúc đó giá trị của biến luôn bị trả về là `undefined` cho dù lúc khai báo biến ta có gán giá trị cho biến đó rồi. ví dụ khi dòng code này ```js console.log(message) var message = "bị hoisting" ``` Khi đó với cơ chế hoisting thì đoạn code Js sẽ chạy theo trình tự như sau: ```js var message // chỗ này bị hoisting Js đưa biến này lên đầu phạm vi có thể gọi được biến console.log(message) // chỗ này bị undefined var message = "bị hoisting" ``` Đúng ra thì trong bình thường lập trình thì ngôn ngữ lập trình đó phải thông báo lỗi ở chỗ này để lập trình viên sửa code kịp thời. Còn như Js thì không báo lỗi mà vẫn cứ cho ra kết quả `undefined` thì làm sao mà mò lỗi trong hàng ngàn dòng code. Vì sao lại có chế độ hoisting này? Theo mình đoán thì có thể do Js cho phép khởi tạo biến mà không cần dùng từ khóa `var`.Ví dụ ```js message = "Khởi tạo biến mà không cần từ khóa var" console.log(message) // Kết quả trả về vần là `Khởi tạo biến mà không cần từ khóa var` ``` Vậy làm sao ta có thể phục chuyện này. May mắn là Js cũng cung cấp khắc phục bằng cách dùng `use strict`. Ví dụ ```js 'use strict' console.log(message) message = "bị hoisting" // Kết quả chỗ này sẽ bị bắt lỗi kiểu là message is not defined. ``` #### let, const Đây là những cách khai báo biến bắt đầu có từ những phiên bản Js cải tiến sau này và `let` và `const` đều không bị hoisting. Khác nhau cơ bản giữa chúng là `let` có thể bị gán lại giá trị còn `const` thì không bị gán lại giá trị toàn bộ biến. ```js let web = 'doidev.com' console.log(web) // doidev.com web = 'chemgio.com' console.log(web) // biến web bị gán lại giá trị là chemgio.com // trường hợp với const const site = 'doidev.com' console.log(site) site = 'chemgio.con' // báo lỗi không gán lại được giá trị ``` Nhưng với `const` thì ta có thể thay đổi lại giá trị bên trong của biến là `object` hoặc `array`. ```js const doidev = {name:'doidev'} doidev.name = 'chém gió' console.log(doidev) // {name: 'chém gió'} --> có nghĩa là giá trị bên trong object của biến const có thể bị thay đổi. const doidev = ['chém gió', 'ăn vạ'] doidev.push('chuẩn không phải chỉnh') console.log(doidev) // ['chém gió', 'ăn vạ', 'chuẩn không phải chỉnh'] ``` ### scope (phạm vi) Khi khai báo biến trong Js thì biến đó sẽ có một scope(phạm vi) mà trong phạm vi đó nó có thể được gọi, gán giá trị. Còn nếu ngoại phạm vi đó thì ta không thể gọi biến đó được. Có hai loại phạm vi trong Js là - Global scope: là biến đó có thể dùng bên ngoài hay bên trong một hàm. Ví dụ ```js var doidev = 'chém gió' // chỗ này ta có thể dùng biến trên được function anVa = { // chỗ này ta cũng có thể dùng biến được } ``` - Function scope: là biến chỉ được dùng trên trong một hàm. Ví dụ ```js function chemGio = { var dev = 'Chém gió cấp 1' // chỗ này có thể dùng biến } // chỗ này bên ngoài function và không có thể gọi biến ``` - Block scope: block ở đây là một cặp ngoặc nhọn `{}` ```js { var doidev = "chém gió" } // biến doidev vẫn có thể gọi được ở đây bên ngoài cặp ngoặc nhọn block scope ``` > Trước `ES2015` hay còn gọi là (ES6) Javascript không có block scope. Từ ES6 trở đi thì biến được khai báo > bằng `let` và `const` và có phạm vi được gọi trong block scope và sẽ không thể gọi bên ngoài block đó > ```js > { > let doidev = "chém gió" > const name = "Mãi chém gió" > } > // biến doidev & name sẽ không thể truy cập từ chỗ này vì nằm bên ngoài cặp ngoặc nhọn block scope > ``` ## 4. Data types (tham trị, tham chiếu) ### Data types Có 2 kiểu dữ liệu chính trong Js là nguyên thủy (Primitive) và object. #### Primitive - Number: 1,2,3, 4.. - String: 'doidev'.. - Boolean: true, false - Null - Undefined - Symbol (ES6) > `Null` và `undefined` bằng nhau về mặt giá trị. Nhưng khác nhau về kiểu > ```js > typeof undefined // undefined > typeof null // object > null == object // true > null === object // false > ``` ##### undefined `undefined` nghĩa là `Không có giá trị`. Undefined xuất hiện ở ```js // 1. Biến doidev chưa khởi tạo giá trị let doidev console.log (doidev) // undefined // 2. Thuộc tính không tồn tại trong object let web = {name:"doidev"} console.log(web.tag) // undefined vì tag không có trong object // 3. Thiếu param function chemGio (skill, tag) { console.log(tag) } chemGio(doidev)// log ra tag là undefined // 4. Function không trả về hoặc trả về undefined function anVa (){ alert('chí phèo') } console.log(anVa()) // undefined ``` ##### null `null` nghĩa là rỗng (khác với chuỗi rỗng `''`) và `null` thường dùng cho object mà không có giá trị như mong đợi Ta không thể truy cập thuộc tính của `null` và `undefined`. Ví dụ ```js function getName(value) { return value.name } // Lỗi getName(undefined) // TypeError getName(null) // TypeError ``` #### Kiểu object Tất cả những kiểu không thuộc kiểu dữ liệu nguyên thủy thì được coi là kiểu dữ liệu object. Ví dụ ```js // 1. Plain Object let web = {domain:'doidev.com', name:'chém gió chuyên nghiệp'} // 2. Array (mảng) let chemGio = ["web", "frontend", "html"] // 3. Regular Expression (biểu thức mẫu) let regex = /ab+c/ // 4. Function cũng coi là một object function sum(a,b) { return a + b } typeof sum ==='function' // true // Nhưng `null` không phải là object mặc dù typeof null ==='object' // true ``` ##### Khai báo - Contructor: là cách khai báo kiểu dữ liệu nguyên thủy - Wrapper object: là cách khai báo tạo ra object cho boolean, number, string với các **wrapper object** tương ứng là `Boolean`, `Number`, `String`. Ví dụ: ```js let str = new String ('doidev') console.log (typeof str) // object console.log(str) // String {"doidev"} ``` Để lấy giá trị nguyên thủy từ các object trên thì ta dùng hàm `valueOf()` ```js let str = new String ('doidev') console.log(str.valueOf()) // doidev ``` Chúng có thể đóng vai trò như một function để chuyển đổi về giá trị nguyên thủy. Ví dụ ```js String(2021) // '2021' ép kiểu về string Number('2021') // 2021 ép kiểu về number Boolean(0) // false ép kiểu về boolean ``` ### Tham trị, tham chiếu #### Tham trị Khi giá trị thuộc kiểu dữ liệu nguyên thủy, biến sẽ chứa giá trị của biến đó ```js // 1. So sánh bằng giá trị 'doidev' === 'doidev' // true 1 === 1 // true // 2. Luôn luôn bất biến (immutable) var year = 2021 // Khi ta thêm thuộc tính name thì sẽ không được year.name = 'Tân Sửu' console.log(year.name) // undefined ``` #### Tham chiếu Khi gán hoặc sao chép dữ liệu thuộc kiểu object thì biến đó chỉ lưu lại địa chỉ của giá trị đó trên vùng nhớ. Nó không lưu lại giá trị được gán ```js // So sánh bằng tham chiếu {} === {} // false var name1 = ["doidev"] var name2= ["doidev"] name1 === name2 // false // có thể thay đổi mutable var web1 = {name:'doidev'} var web2 = web1 web2.name = 'chemgio' console.log(web1) ``` ### Truthy và Falsy Truthy và Falsy là những giá trị mà Js ép về kiểu Boolean sẽ cho ra `true` hoặc `false`. Giống như dùng `Boolean(value)` để ép kiểu vậy. - Truthy: là chuỗi khác **rỗng**, số khác `0` và tất cả `object`. ```js Boolean([]) // true vì là object Boolean({})// true vì là object ``` - Falsy: là `undefined`, `null`, `false`, `0`, `NaN`, `''` ## 5. Operators (Toán tử) Có khoảng 10 loại toán tử khác nhau trong Js ví dụ như - Toán tử gán: Assignment operators - Toán tử so sánh: Comparison operators - Toán tử hạng: Arithmetic operators - Toán tử bitwise: Bitwise operators - Toán tử logic: Logical operators - Toán tử chuỗi: String operators - Toán tử ba ngôi: Conditional (terary) operator - Toán tử phẩy: Comma operator - Toán tử một ngôi: Unary operators - Toán tử quan hệ: Relation operators Ví dụ cho những toán tử hay dùng nhất trong Js như sau: ### Toán tử hạng ```js 1 + 2 // cộng 2 - 1 // trừ 3 * 2 // nhân 2 ** 4 // 2 lũy thừa 4 = 16 4 / 2 // chia 10 % 3 // kết quả là 1 nghĩa là chia lấy phần dư. i++ // tăng giá trị lên 1 và trả về giá trị mới ++i // tăng giá trị lên 1 và trả về giá trị ban đầu trước khi tăng // Ví dụ minh họa a=0, b=0; a++; // a = 1 ++b; // b = 1 b = ++a; // b = a = 2 a = b++; // a = 2, b = 3 // hết ví dụ i-- // giảm giá trị xuống 1 và trả về giá trị mới --i // giảm giá trị xuống 1 và trả về giá trị ban đầu trước khi tăng ``` ### Toán tử gán ```js let a = 1 a += 2 // 4 a -= 2 // 2 a *= 2 // 4 a **= 2 // 16 a /= 2 // 8 a %= 2 // 0 ``` ### Toán tử so sánh - So sánh bằng về giá trị ```js console.log('2' == 2) // true ``` - So sánh bằng về giá trị và kiểu dữ liệu ```js console.log('2' === 2) // false console.log(2 === 2) // true ``` - So sánh không bằng về giá trị ```js console.log('2' != 2) // true ``` - So sánh không bằng về giá trị và kiểu dữ liệu ```js console.log('2' !== 2) // false console.log(2 !== 2) // true ``` - So sánh lớn bé hơn ```js console.log(3 > 2) // true console.log(2 < 3)// true console.log( 3 >= 2)// true console.log(2<= 3)// true ``` - Toán tử ba ngôi ```js let diem = 5 let xet = diem >= 5 ? "Lên lớp" : "Ở lại lớp" console.log(xet) // Lên lớp ``` - Toán tử Logic 1. Logic là `và` ký hiệu `&&` và `hoặc` ký hiệu `||` ```js console.log(true && false) // false console.log(true && true) // true console.log(true || false)// true console.log(true || true)// true ``` 2. Logic phủ định ký hiệu dấu chấm than phía trước `!` nghĩa là phủ định lại nó ```js console.log(!false) // true ``` ## 6. Câu lệnh điều kiện (if-else) - Câu lệnh if ```js if(5 > 3) { console.log('Bạn đã trúng giải') } ``` - Câu lệnh if else ```js let check = true if(check === true) { console.log ('bạn đã trúng giải') } else { console.log ('về nhà ngủ') } ``` - Câu lệnh if else if ```js let diem = 6 if (diem < 5) { console.log('Học kém') } else if(diem < 7) { console.log ('Học trung bình') } else if(diem < 9) { console.log('Học Khá') } else { console.log('Học giỏi') } ``` - Switch case Ví dụ 1 ```js let chon = 1 switch (chon) { case 1: console.log('chém gió') break case 2: console.log('chém bão') break case 3: console.log('chém bão bão') break default: console.log('khỏi chém') } ``` Ví dụ 2 ```js let chon = 1 switch (chon) { case 1: case 2: case 3: console.log('chém gió' ) break case 4: console.log('chém bão' ) break default: console.log('khỏi chém') } ``` - Nâng cao - Toán tử ba ngôi ```js let note = null note ? console.log('Note có ghi chú') : console.log('Note không có giá trị') ``` - Toán tử logic ```js let ghiChu = 'yes' !ghiChu && console.log('Note có ghi chú') ``` ## 7. Loops (Vòng lặp) ### while Vòng lặp `while` kiểm tra điều kiện trước khi thực hiện code bên trong ```js let a = 1 let sum = 0 while (a <= 10) { sum += a a++ } console.log(sum) // 55 ``` ### do - while Vòng `do-while` thưc hiện code trước ít nhất một lần rồi mới kiểm tra điều kiện cho lần tiếp theo trong ```js let b = 2 let total = 0 do { total += b b++ } while (b <= 10) // 54 console.log(total) ``` Vòng `for` ```js for (i=0; i < 10; i++) { console.log(i) } ``` Vòng `for-in` dùng lặp các `key` của object ```js let web = {name:'doidev', skill: 'chém gió'} let text = '' for (let x in web) { text += web[x] + ' ' } console.log(text) ``` Vòng `for-of` dùng các cặp value của interable object điển hình như array hay string ```js let doidev = ['chém gió', 'ăn vạ', 'vật vờ' ] let name = '' for (let value of doidev) { name += value + ' ' } console.log(name) ``` ## 8. Function (hàm) Một function trong Js là một đoạn code được thiết kế để làm nhiệm vụ riêng biệt. Javascript function được thực khi gọi nó. ### Phân loại Hàm trong Js chia làm 2 loại - Khai báo hàm (Function Declaration) Với cách này thì hàm sẽ bị `hoisting` và Js cho phép chúng ta gọi hàm trước khi hàm đó được khai báo. ```js hoisted() function hoisted() { console.log('hàm này bị hoisted') } ``` - Biểu thức hàm (Function Expression) Khai báo hàm bằng cách này thì sẽ không được hoisting và sẽ báo lỗi. Ví dụ ```js expression() var expression = function() { console.log('cái này chạy không?') } // Lỗi expression is not a function ``` **Giải thích**: Biến `var` expression vẫn được hoisting và đẩy lên trên cùng của scope nhưng chỉ là khai báo mà không được gán cho hàm. Vì thế nên có lỗi `TypeError` > Lưu ý khi khai báo như bên dưới thì sẽ chạy luôn function và gán handle là giá trị mà function return ```js const handle = (function () { console.log('chạy luôn') }) ``` Ngoài ra ta còn có hàm - IIFE (Immediately Invokable Function Expression) IIFE là khởi tạo một hàm và thực thi ngay lập tức sau đó ```js ;(function () { let a = 1 let b = 2 console.log('a + b = ' + (a + b)) })() ``` Ta nên thêm dấu `;` vào phía trước IIFE để tránh trường hợp làm chạy một function ngoài ý muốn, ví dụ ```js const handle = function () { console.log('hello') } (function () { console.log('IIFE') })() ``` Với ví dụ bên trên chúng ta khai báo hàm `handle` rồi chạy luôn function với tham số đầu vào là một function IIFE sẽ gây ra lỗi không mong muốn. Vì thế nên ta thêm dấu chấm phẩy `;` trước IIFE. - Hàm ẩn danh (Anonymous Function) Hàm ẩn danh là một hàm không tên. Nếu ta để ý thì vế bên phải một biểu thức hàm là một hàm ẩn danh, hay IIFE cũng là một hàm ẩn danh. Ngoài ra hàm ẩn danh còn xuất hiện ở callback. Function bên trong hàm `setTimeout()` là một hàm ẩn danh. ```js setTimeout(function() { console.log('Sau 2s thì in ra dòng này') },2000) ``` - Hàm rút gọn - hàm mũi tên (Arrow Function) Hàm rút gọn ngắn hơn biểu thức hàm và không phụ thuộc vào `this`. Áp dụng tốt cho hàm ẩn danh nhưng thể dùng làm hàm khởi tạo ```js const handleClick = () => { console.log('bạn clicked') } ``` Lưu ý với arrow function - Không có `this` - Không được gọi với `new` - Cũng không có `super` khi kế thừa class ### Phân biệt parameter, argument - Parameter (tham số) đóng vai trò là giữ chỗ cho giá trị sẽ được đưa vào hàm khi tạo hàm - Argument (đối số) đóng vai trò là giá trị đưa vào hàm khi gọi hàm Ví dụ cụ thể ```js function sum (a, b) { // a, b là tham số return a + b } sum(4,5) // 4,5 là đối số ``` - Tham số mặc định (default parameter) ```js function dpara(x, y = x) { // nghĩa là mặc định y có giá trị bằng x nếu như khi gọi hàm không đưa đối số vào console.log(x, y) } dpara(3) // 3 3 dpara(3, 5) // 3 5 ``` - Rest parameter ```js function sum(...restArg) { console.log(restArg) } console.log(sum(1,2,3)) console.log(sum(1,2,3,4,5)) ``` ## 9. This và function nâng cao ### this `this` trong Js là từ khóa đề cập đến object mà nó thuộc về #### this trong method Trong method, `this` sẽ đề cập đến objec chủ quản ```js var web = { name: 'doidev', skill: 'chém gió', full: function () { return this.name + ' ' + this.skill } } var handleFull = web.full console.log(web.full())// doidev chém gió console.log(handleFull()) // undefined undefined ``` #### this khi đứng một mình `this` khi đứng một mình đề cập đến **global object**. Nếu là trình duyệt thì sẽ là **`[Window Object]`** ```js var x = this console.log(x) // [object window] ``` #### this trong function Nếu mặc định thì `this` sẽ đề cập đến **global object** ```js function myFunc () { return this } console.log (myFunc()) // [object window] ``` #### this trong 'strict mode' `this` trong `use strict` sẽ là `undefined` ```js 'use strict' function myFunc () { return this } console.log (myFunc()) // [object window] ``` Tiếp tục xem thêm ví dụ trong **constructor** function ```js 'use strict' function Car(name) { this.name = name this.printName = function () { console.log(this.name) } } Car('bmw') // Lỗi vì không thể truy cập thuộc tính name của undefined const bmw = new Car('BMW') bmw.printName() // log ra BMW ``` #### this trong event handlers Trong một HTML event handler, `this` đề cập đến HTML element mà nó nhận event Ví dụ ```html // Khi nhấn vào button dưới đây thì nó sẽ được set display:none ``` {{< admonition >}} Hiển thị kết quả {{< /admonition >}} #### this trong callback `this` trong callback thì không đề cập đến function chứa callback đó. Lưu ý chỗ này. Ví dụ ```js const delay = { name:'doidev', print(){ setTimeout(function () { console.log(this.name) // undefined },3000) } } delay.print() ``` Để sửa vấn đề này có thể dùng arrow function viết lại ```js const delay = { name:'doidev', print(){ setTimeout (()=>{ console.log(this.name) // undefined },3000) } } delay.print() ``` ví dụ thêm bên dưới `this` không đề cập đến `broke` mà đề cập đến `obj` ```js function broke(func) { const obj = { name: 'doidev', func } return obj.func() } broke(function () { console.log(this) //obj }) ``` Vì thế để biết `this` trong callback đề cập đến cái nào thì phải hiểu được hàm chứa callback gọi callback như thế nào. ### High order function High order function (HOC) là một function mà nhận vào tham số là **function** hoặc return về một function ```js const tinhTong = (a) => (b) => a + b const ketQua = [1,2,3,4,5].map((item) => item * item) console.log(tinhTong(1)(2)) // 3 console.log(ketQua) // [1, 4, 9, 16, 25] ``` #### callback Là một function mà được truyền vào một function khác như một tham số ```js const num = [2,4,6,8] num.forEach((item, index) => { console.log('STT: ', index, 'là ', item) }) const result = num.map((item, index) => `STT: ${index} là ${item}` ) ``` #### Closure Là cách một function cha return về một function con bên trong nó. Ở trong function con đó thể truy cập và thực thi các biến của function cha. Phải đủ 2 điều kiện này mới gọi là `closure` function. Ví dụ ```js const increase = () => { let x = 0 const increaseInner = () => ++x return increaseInner } const myFunction = increase() console.log(increase()()) console.log(increase()()) console.log(myFunction()) console.log(myFunction()) console.log(myFunction()) ``` #### Currying Là một kỹ thuật cho phép chuyển đổi một function nhiều tham số thành những function liên tiếp có một tham số ```js const findNumber = (num) => (func) => { const result = [] for (let i = 0; i < num; i++) { if(func(i)){ result.push(i) } } return result } findNumber(10) ((number) => number % 2 === 1) findNumber(20) ((number) => number % 2 === 0) findNumber(30) ((number) => number % 3 === 2) ``` ## 10. Call, apply, bind Chỉ có function mới có có thể gọi 3 hàm này ### call Gọi hàm cho phép bạn truyền vào một object và các đối số phân cách nhau bởi dấu phẩy ### apply Tương tự call. Gọi hàm, cho phép truyền vào một object và tiếp theo là các đối số thông qua mảng ### bind Trả về một hàm mới. Cho phép truyền vào một object và các đối số phân cách nhau bởi dấu phẩy > Khi dùng 3 hàm này `this` đề cập đến object được truyền vào ```js let site = {name: 'doidev', skill: "chém gió"} function dev (text1, text2) { console.log(text1 + ' ' + text2 + ' ' + this.name + ' ' + this.skill ) } dev.call(site, 'Xin', 'Chào call') dev.apply(site,['Xin', 'Chào apply']) const newDev = dev.bind(site,'Xin','Chào bind') newDev() ``` ## 11. Number & Method Không như nhiều ngôn ngữ lập trình khác, kiểu số thường được chia ra nhiều loại như `integer`, `short`, `long`, `float` ... Riêng với Js thì chỉ có **number** ### Số nguyên - Số nguyên trong Js có độ chính xác đến 15 chữ số ```js var x = 999999999999999 // x sẽ là 999999999999999 var y = 9999999999999999 // y sẽ là 10000000000000000 ``` ### Số thập phân - Số thập phân giới hạn 17 chữ số. Nhưng không phải lúc nào tính toán số thập phân cũng chính xác 100%. Ví dụ ```js var x = 0.2 + 0.1 console.log(x) // 0.30000000000000004 Boolean(x === 0.3) // false ``` Để giải quyết vấn đề này chúng ta nhân với `10` để làm tròn nó thành số nguyên rồi thực hiện phép tính. Ví dụ bên trên sửa lại là ```js var x = (0.2*10 + 0.1*10) /10 console.log(x) // 0.3 Boolean(x === 0.3) // true ``` Hoặc làm tròn đến phần thập phân cho phép. Ví dụ trên viết lại là ```js let x = 0.2 + 0.1 console.log(Number(x.toFixed(1))) // 0.3 ``` Hoặc dùng thư viện ngoài như [math.js](https://mathjs.org/) ```js math.add(math.fraction(0.1), math.fraction(0.2)) // 0.3 ``` ### Tính toán số Nếu cộng hai hay nhiều số thì kết quả ra số. Nhưng nếu công hai hay nhiều số lẫn lộn cả số và chuỗi thì kết quả sẽ ghép chung số chuỗi vào với nhau. Do đó có lưu ý là trước khi làm phép cộng, tính toán thì chuyển hết về số. ```js var x = '10' var y = 20 var z = x + y // z sẽ là một string 1020 ``` ### NaN - NaN (Not a Number) NaN là một giá trị trong Js để xác định một số không phải là một số hợp lệ. Đồng thời có phương thức `isNaN` để xác định xem đó có phải là số hay không ```js var x = 100 / 'doidev' // return NaN typeof NaN // return Numbers isNaN(x) // return true -> x không phải là number isNaN(1) // return false -> vì 1 là số ``` ### Infinity - Infinity - Dương vô cực còn `-Infinity` là âm vô cực ```js var x = 2/0 // infinity dương vô cực var y = -2/0 // infinity âm vô cực typeof Infinity // return number ``` ### Hexadecimal (Hệ thập lục phân) Nếu ta biểu diễn số bắt đầu bằng `0x` thì Js sẽ hiểu đây là hệ thập lục phân ```js var x = 0xff // x là 255 ``` ### Methods (Phương thức) Cùng tìm hiểu một số phương thức thường dùng khi làm việc với Number #### toString() Phương thức này ép kiểu số về chuỗi ```js var x = 123 x.toString() console.log(x) // "123" ``` #### toFixed() Dùng `toFixed` làm tròn số. Ví dụ ```js var x = 9.565 x.toFixed(0) x.toFixed(1) x.toFixed(2) x.toFixed(3) ``` #### Ép kiểu về số Chúng ta có 3 cách - Number(): chuyển đổi giá trị về kiểu số - parseInt(): chuyển đổi giá trị về kiểu số nguyên - parseFloat(): chuyển đổi giá trị về kiểu số thập phân Ví dụ nào ```js Number(true) Number(false) Number('10') Number('10.33') Number('10,33') Number('10 33') Number('doidev') parseInt('10') parseInt('10.33') parseInt('10 20 30') parseInt('10 years') // returns 10 parseInt('years 10') // returns NaN parseFloat('10') // returns 10 parseFloat('10.33') // returns 10.33 parseFloat('10 20 30') // returns 10 parseFloat('10 years') // returns 10 parseFloat('years 10') // returns NaN ``` ## 12. String & Method Chuỗi có thể chứa trong nháy đơn `'chuỗi'` hoặc nháy kép `"chuỗi"`. Vị trí đầu tiên của chuỗi là `0`. Ví dụ ```js var web = 'doidev' var site = "doidev.com" ``` ### Methods #### length Trả về độ dài chuỗi ```js var txt = 'doidev.com' console.log(txt.length) // 10 ``` #### indexOf `indexOf()` trả về vị trí đầu tiên của từ khóa trong một chuỗi. Nếu không trả về `-1` ```js var doidev = 'Trang web chuyên chém gió nhé' console.log(doidev.indexOf('chém')) // 17 ``` #### Tách chuỗi 1. slice(start, end) nghĩa là tách từ vị trí start tới vị trí end ```js var str = 'Apple, Banana, Kiwi' var res = str.slice(7, 13) // Banana //Nếu tham số là giá trị âm thì vị trí sẽ được đếm từ phía sau str.slice(-12, -6) // Banana //Nếu không có tham số thứ 2 thì coi như đếm đến cuối str.slice(7) // Banana, Kiwi str.slice(-12) // Banana, Kiwi ``` 2. substring(start, end) tương tự slice nhưng không thể nhận số âm ```js var str = 'Apple, Banana, Kiwi' var res = str.substring(7, 13) // Banana ``` 3. substr() tương tự slice() nhưng tham số thứ 2 là độ dài chuỗi ```js var str = 'Apple, Banana, Kiwi' var res = str.substr(7, 6) // Banana str.substr(7) // Banana, Kiwi str.substr(-4) // Kiwi ``` #### Replace Thay thế chuỗi, cấu trúc `replace(find, replace)`. Ví dụ ```js var doidev = "chuyên đi chém gió và chém bão" var oneWord = doidev.replace("chém", "hút") var allWord = doidev.replace(/chém/g, "hút") console.log(oneWord) console.log(allWord) ``` #### Chuyển đổi Phương thức chuyển đổi từ chữ hoa sang chữ thường và ngược lại ```js var site = "Chém Gió lung tung" var chuHoa = site.toUpperCase() var chuThuong = site.toLowerCase() console.log(chuHoa) console.log(chuThuong) ``` #### concat() Nối chuỗi ví dụ ```js var site = 'chém' var web = 'gió ' console.log(web.concat(site)) // gió chém ``` #### trim() Xóa khoảng trắng hai bên chuỗi. ví dụ ```js var doidev = " ăn vạ " console.log(doidev.trim()) ``` #### charAt() hoặc [] Lấy ký tự từ một chuỗi. Ví dụ ```js let chemGio = 'luôn luôn gây bão' chemGio.charAt(2) chemGio[2] ``` #### charCodeAt() Lấy UTF-16 code tại vị trí bất kỳ trong chuỗi ```js let chemGio = 'luôn luôn gây bão' chemGio.charCodeAt(2) // 244 ``` #### split() Chuyển chuỗi sang mảng và tham số là chuỗi ngăn cách ```js let chemGio = 'luôn luôn gây bão' let array1 = chemGio.split(' ') console.log(array1) ``` ## 13. Array & Methods Mảng có thể chứa nhiều kiểu dữ liệu bên trong ```js const cars = [{ name: 'BMW', location: 'germany' }, 'Toyota', 24] console.log(cars[1]) // Toyota cars[0].name = 'Mercedes' console.log(cars) // [ { name: 'Mercedes', location: 'germany' }, 'Toyota', 24 ] ``` ### Phương thức #### length return độ dài mảng ```javascript const num = [1, 2, 3, 4] num.length // 4 ``` #### Array.isArray() hoặc instanceof() Để nhận biết một biến có phải là mảng hay không ```javascript const fruits = ['Banana', 'Orange', 'Apple', 'Mango'] Array.isArray(fruits) // true fruits instanceof Array // true ``` #### toString() hoặc join() Để chuyển mảng sang chuỗi ```javascript const fruits = ['Banana', 'Orange', 'Apple', 'Mango'] const str1 = fruits.toString() // Banana,Orange,Apple,Mango const str2 = fruits.join('-') // Banana-Orange-Apple-Mango ``` #### push() Thêm 1 phần từ vào cuối mảng, return lại chiều dài mảng mới ```javascript const fruits = ['Banana', 'Orange', 'Apple'] const x = fruits.push('Mango') // giá trị của x là 4 ``` #### pop() Xóa phần tử cuối cùng của mảng, return lại phần tử vừa xóa ```javascript const fruits = ['Banana', 'Orange', 'Apple', 'Mango'] const x = fruits.pop() // giá trị của x là Mango ``` #### shift() Xóa phần tử đầu tiên của mảng, return lại phần tử vừa xóa ```javascript const fruits = ['Banana', 'Orange', 'Apple', 'Mango'] const x = fruits.shift() // giá trị của x là Banana ``` #### unshift() Thêm 1 phần tử vào đầu mảng, return lại chiều dài mảng mới ```javascript const fruits = ['Orange', 'Apple', 'Mango'] const x = fruits.unshift('Banana') // giá trị của x là 4 ``` Lưu ý khi dùng **delete**, phần tử sẽ bị mất khỏi mảng nhưng để lại 1 khoảng trống. Khi truy cập đến khoảng trống này thì giá trị của nó là **undefined** ```javascript const fruits = ['Banana', 'Orange', 'Apple', 'Mango'] delete fruits[0] console.log(fruits) // [ <1 empty item>, 'Orange', 'Apple', 'Mango' ] console.log(fruits.length) // 4 ``` #### splice splice(start, itemDelete_1, itemDelete_2, … itemAdd_1, itemAdd_2) Hàm **splice** dùng để thêm hoặc xóa nhiều phần tử trong 1 mảng. Return về mảng với những phần tử vừa được xóa ```javascript const fruits = ['Banana', 'Orange', 'Apple', 'Mango'] // Thêm vào vị trí số 2 const x = fruits.splice(2, 0, 'Lemon', 'Kiwi') console.log(x) // [] vì không xóa phần tử nào mà chỉ thêm console.log(fruits) // [ 'Banana', 'Orange', 'Lemon', 'Kiwi', 'Apple', 'Mango' ] ``` ```javascript const fruits = ['Banana', 'Orange', 'Apple', 'Mango'] // Xóa 1 phần tử tại vị trí số 0 const x = fruits.splice(0, 1) console.log(x) // [ 'Banana' ] console.log(fruits) // [ 'Orange', 'Apple', 'Mango' ] ``` #### slice(start, end) Tách ra một mảng mới từ mảng cũ ```javascript const fruits = ['Banana', 'Orange', 'Apple', 'Mango'] // tách ra 1 mảng mới bắt đầu tại vị trí đầu tiên đến vị trí cuối const newFruits = fruits.slice(1) console.log(newFruits) // [ 'Orange', 'Apple', 'Mango' ] console.log(fruits) // [ 'Banana', 'Orange', 'Apple', 'Mango' ] // tách ra 1 mảng mới bắt đầu tại vị trí 1 đến 2 (3-1) const newFruits2 = fruits.slice(1, 3) console.log(newFruits2) //[ 'Orange', 'Apple' ] ``` #### concat() Tạo mới một mảng bằng cách nối các mảng cũ ```javascript const myGirls = ['Cecilie', 'Lone'] const myBoys = ['Emil', 'Tobias', 'Linus'] const myChildren = myGirls.concat(myBoys) // [ 'Cecilie', 'Lone', 'Emil', 'Tobias', 'Linus' ] ``` #### spread operator Phân rã mảng (object) thành từng phần tử nhỏ ( tưởng tượng [1,2,3] => 1,2,3) ```javascript const cars1 = [1, 2, 3] const cars2 = [3, 4, 5] // Nối mảng const newCars = [...cars1, ...cars2] // [ 1, 2, 3, 3, 4, 5 ] // Tạo thành 1 mảng mới const cars3 = [...cars1] // [1, 2, 3] console.log(cars1 !== cars3) // true ``` #### forEach() Lặp qua từng phần tử trong mảng Tham số là một **callback function** với 3 đối số: - giá trị phần tử - index phần tử - mảng đang thực hiện ```javascript const numbers = [1, 2, 3, 4, 5, 6, 7] const newNumbers = [] numbers.forEach((value, index, array) => { newNumbers.push(value) }) console.log(newNumbers) // [1, 2, 3, 4, 5, 6, 7] ``` #### map() Tạo một mảng mới bằng cách thực hiện tính toán trên mỗi phần tử. **map()** không thay đổi mảng cũ ```javascript const numbers = [1, 2, 3] const newNumbers = numbers.map((value, index, array) => { return value * 2 }) console.log(newNumbers) // [2, 4, 6] ``` #### filter() Tạo một mảng mới với những phần tử thỏa điều kiện ```javascript const numbers = [1, 2, 3] const newNumbers = numbers.filter((value, index, array) => { return value >= 2 }) console.log(newNumbers) // [2, 3] ``` #### find() Trả về phần tử thỏa điều kiện đầu tiên. Nếu không có sẽ return undefined ```javascript const numbers = [1, 2, 3] const result = numbers.find((value, index, array) => { return value >= 2 }) console.log(result) // 2 ``` #### findIndex() Trả về index của phần tử thỏa điều kiện đầu tiên. Nếu không có sẽ return **-1** ```javascript const cars = ['BMW', 'Toyota', 'Hyundai'] const result = cars.findIndex((value, index, array) => { return value === 'Toyota' }) console.log(result) // 1 ``` #### indexOf() Trả về index của phần tử trong mảng. Nếu không có sẽ return -1 ```javascript const cars = ['BMW', 'Toyota', 'Hyundai'] const index = cars.indexOf('Toyota') console.log(index) // 1 ``` #### every() Nếu mọi phần tử thỏa điều kiện sẽ return true, còn không sẽ return false ```javascript const numbers = [1, 2, 3] const check = numbers.every((value, index, array) => { return value > 2 }) console.log(check) // false ``` #### some() Nếu có một phần tử thỏa điều kiện sẽ return true, còn không sẽ return false ```javascript const numbers = [1, 2, 3] const check = numbers.some((value, index, array) => { return value > 2 }) console.log(check) // true ``` #### includes() Kiểm tra một phần tử có trong mảng hay không. return true/false ```javascript const numbers = [1, 2, 3, 4, 5] const check = numbers.includes(5) // true Thường thì các method với tring, array không thay đổi giá trị gốc. Ngoại trừ: pop, push, shift, unshift, delete ``` #### reverse() Đảo ngược thứ tự các item trong array ```js var fruits = ['Banana', 'Orange', 'Apple', 'Mango'] fruits.reverse() console.log(fruits) // ["Mango", "Apple", "Orange", "Banana"] ``` #### sort() Mặc định sẽ sắp xếp các giá trị như là **string** Điều này hoạt động ổn cho string ("Apple" thì đứng trước "Banana"). Tuy nhiên, nếu số cũng bị cho là string, `25` thì bị cho là lớn hơn `100`, bởi vì `2` thì lớn hơn `1`. Bởi vì điều này, phương thức `sort()` sẽ thực thi không chính xác khi sắp xếp number. Bạn có thể fix điều này bằng cách cung cấp một **compare function**. **compare function** sẽ có dạng như thế này. ```js function(a, b){return a - b} ``` Nếu kết quả là âm, `a` sẽ được xếp trước `b`. Nếu kết quả là dương, `a` sẽ được xếp sau `b`. Nếu kết quả là 0, sẽ không có sự thay đổi nào giữa 2 giá trị. Ví dụ sắp xếp tăng dầng: ```js var points = [40, 100, 1, 5, 25, 10] points.sort(function (a, b) { return a - b }) console.log(points) // [1, 5, 10, 25, 40, 100] ``` Ví dụ sắp xếp giảm dần: ```js var points = [40, 100, 1, 5, 25, 10] points.sort(function (a, b) { return b - a }) console.log(points) // [100, 40, 25, 10, 5, 1] ``` #### reduce() Dùng để "xào nấu" một array, ví dụ tính tổng 1 array hay từ 1 array chuyển thành 1 array, object khác. ```js arr.reduce((total, current, currentIndex) => { return something }, initialValue) ``` `initialValue` là option, có hoặc không vẫn được. Nếu có thì ở vòng lặp đầu tiên, `total = initalValue` và `current = arr[0]`. Tức là currentIndex sẽ chạy từ 0 cho đến hết. ```js var points = [1, 2, [3, 4], 5, 6] // flat array const result = points.reduce((total, current) => { return total.concat(current) }, []) console.log(result) // [1, 2, 3, 4, 5, 6] ``` Còn nếu không dùng `inititalValue` thì đầu tiên `total = arr[0]` và `current = arr[1]`. Tức là currentIndex sẽ chạy từ 1 cho đến hết. ```js var points = [1, 2, 3, 4, 5, 6] const result = points.reduce((total, current) => { return total + current }) console.log(result) // 21 ``` ## 14. Object & Methods - entry của object là cặp key, value tương ứng - key thì luôn luôn là một string hoặc number - value có thể là bất cứ giá trị nào của Javascript, kể cả function - method (phương thức) là những thuộc tính mà value của nó là function ```javascript const user = { name: 'Duoc', age: 24, greet() { console.log('Hello, Toi ten la ' + this.name) } } user.greet() // Hello, Toi ten la Duoc ``` ### Methods #### Đọc – thêm – sửa – xóa ```javascript const person = { name: 'Doidev' } // đọc thuộc tính name person.name // thêm thuộc tính vào person person.ability = ['Chém gió', 'ăn vạ'] // sửa thuộc tính name person.name = 'Devdoi' // xóa thuộc tính name delete person.name ``` #### Object.assign() Dùng để merge object ```javascript const person = { name: 'DoiDev', ability: ['chém gió'] } const person2 = Object.assign(person, { ability: ['ăn vạ' ] }) console.log(person2) // {name: 'DoiDev', ability:['ăn vạ]} ``` #### Spread operator: Dùng để shallow copy (copy nông) hoặc merge object ```javascript const person = { name: 'DoiDev', ability: ['chém gió'] } const person2 = { ...person, ability: ['code', 'ăn vạ'] } console.log(person2) // {name: 'DoiDev', ability:['code','ăn vạ]} ``` #### Object.keys() Trả về mảng các key của object ```javascript const person = { name: { firstName: 'Doi', lastName: 'Dev' }, skill: "chém gió" } console.log(Object.keys(person)) // ['name', 'skill'] ``` #### Object.values() Trả về mảng các value của object ```javascript const person = { name: { firstName: 'Doi', lastName: 'Dev' }, skill: "chém gió" } console.log(Object.values(person)) // [{firstName: 'Doi', lastName: 'Dev'},"chém gió"] ``` #### Lặp object với for in ```javascript const object = { a: 1, b: 2, c: 3 } for (const property in object) { console.log(`${property}: ${object[property]}`) } // output: // "a: 1" // "b: 2" // "c: 3" ``` ## 15. Date & time object Date được dựa trên giá trị thời gian là số mili giây kể từ ngày 1 tháng 1 năm 1970 UTC. Để tạo một object Date ta có 4 cách ### Tạo Date ```javascript var d = new Date() var d = new Date(milliseconds) var d = new Date(dateString) var d = new Date(year, month, day, hours, minutes, seconds, milliseconds) ``` Tham khảo get current mili giây [tại đây](https://currentmillis.com/) Ví dụ ```javascript var today = new Date() // tạo bằng mili giây var day1 = new Date(1608734177983) // tạo bằng chuỗi ngày, lưu ý là việc tạo bằng chuỗi ngày rất không chính xác // do sự khác nhau của các trình duyệt var birthday = new Date('October 30, 1996 15:27:08') var birthday = new Date('1996-10-30T15:27:08') //Tạo bằng các tham số năm tháng ngày var birthday = new Date(1996, 10, 30, 15, 27, 8) ``` ### Methods Một số phương thức hay dùng của object Date - `getDate()` Return về 1 ngày trong tháng (1-31) - `getDay()` Return về 1 ngày trong tuần (0-6) - `getFullYear()` Return về năm - `getHours()` Return về giờ (0-23) - `getMilliseconds()` Return về milli giây (0-999) - `getMinutes()` Return về phút (0-59) - `getMonth()` Return về tháng (0-11) - `getSeconds()` Return về giây (0-59) - `toISOString()` Return về định dạng thời gian chuẩn ISO ## 16. Object Window ### Window object Được hỗ trợ trên mọi trình duyệt. Nó đại diện cho cửa sổ trình duyệt. Tất cả [global Javascript object](https://javascript.info/global-object), function, biến (phải khai báo bằng `var` nha, không phải `let/const`) đều tự động trở thành thuộc tính (hoặc phương thức) của `window` object. Ngay cả `document` object (của HTML DOM) cũng là một thuộc tính của `window` object ```javascript alert('Hello') // tương đương window.alert('Hello') ``` ```javascript var gVar = 5 alert(window.gVar) ``` ```javascript window.document.getElementById('header') // tương đương document.getElementById('header') ``` Vậy nên nếu nó là global object, function, biến thì bạn có thể không cần tiền tố window phía trước **Một số thuộc tính và phương thức của window object** - `window.innerHeight`: Chiều cao bên trong của cửa sổ trình duyệt (pixel) - `window.innerWidth`: Chiều rộng bên trong của cửa sổ trình duyệt (pixel) - `window.open()`: mở một cửa sổ - `window.close()`: đóng cửa sổ hiện tại - `window.resizeTo()`: resize cửa sổ hiện tại ### Window location `window.location` (location) object có thể được sử dụng để lấy thông tin địa chỉ url hiện tại và chuyển hướng trình duyệt sang 1 trang mới. - `window.location.href` return về URL trang hiện tại - `window.location.hostname` return tên domain - `window.location.pathname` return pathmane của trang hiện tại - `window.location.protocol` return giao thức trang web (http: hoặc https:) - `window.location.assign()` chuyển hướng sang một trang mới Ví dụ: ```javascript console.log(location.href) // https://www.xdevclass.com/phan-5-thao-tac-html-css-dom-va-browser-bom-voi-javascript console.log(location.hostname) // www.xdevclass.com console.log(location.pathname) // /phan-5-thao-tac-html-css-dom-va-browser-bom-voi-javascript console.log(location.protocol) // https: location.assign('https://www.google.com/') // Chuyển đến trang google location.href = 'https://www.google.com/' // Tương đương location.assign() ``` ### Window Navigator `window.navigator` có thể cho ta biết thông tin trình duyệt của người dùng Ví dụ - `navigator.language`: return ngôn ngữ trình duyệt - `navigator.userAgent`: return **user-agent header** được gửi bởi trình duyệt đến server Nói chung thì **navigator** còn cực nhiều thuộc tính và phương thức, liệt kê không hết luôn, mọi người có thể thao khảo hình dưới ### Window History `window.history` object chứa lịch sử của trình duyệt **Một số phương thức phổ biến**: - `history.back()` dùng để trở về trang trước - `history.forward()` dùng để đi đến trang tiếp theo ## 17. Popup Chúng ta có 3 loại popup mà javascript trên trình duyệt cung cấp: **Alert**, **Confirm**, **Prompt** ### Alert Alert thường được sử dụng để thông báo cho người dùng về một việc gì đó, người dùng phải nhấn “OK” để hoàn thành quá trình ```javascript alert('I am an alert box!') ``` ### Confirm Confirm thường sử dụng để xác nhận người dùng có chập nhận làm một điều gì đó. `confirm()` sẽ return về giá trị boolean ```javascript if (confirm('Press a button!')) { txt = 'You pressed OK!' } else { txt = 'You pressed Cancel!' } ``` ### Prompt Prompt thường được dùng nếu bạn muốn người dùng nhập vào một giá trị và bạn sẽ dùng giá trị đó để thực hiện một hành động gì đó. Nếu người dùng nhấn **OK** sẽ return về giá trị nhập, nếu nhấn **Cancel** sẽ return `null` ```javascript var person = prompt('Please enter your name', 'Harry Potter') if (person == null || person == '') { txt = 'User cancelled the prompt.' } else { txt = 'Hello ' + person + '! How are you today?' } ```