Tổng hợp những câu hỏi phỏng vấn về Javascript
Những lý thuyết này cung cấp cho bạn danh sách các câu hỏi phỏng vấn JavaScript thường được hỏi với câu trả lời cho người mới bắt đầu.
🔖 Gợi ý từ Admin
📝 Tài liệu phỏng vấn kiến thức lập trình: Xem tại đây!!!
📌 Tìm hiểu về thuật toán: Xem tại đây!!!
📌 Roadmaps - Lộ trình trở thành một lập trình viên: Xem tại đây!!!
⚡️ Cheatsheet các ngôn ngữ lập trình: Xem tại đây!!!
⚡️ Handbook lập trình: Xem tại đây!!!
I. javascript-interview-questions-developer
Danh sách những câu hỏi trong phỏng vấn Javascript. Hãy ủng hộ cho tác giả bằng cách tặng họ 1 ⭐️ nhé !
🔗 Nguồn: javascript-interview-questions-developer
✍️ Tác giả: tuantvk
Những lý thuyết này cung cấp cho bạn danh sách các câu hỏi phỏng vấn JavaScript thường được hỏi với câu trả lời cho người mới bắt đầu. Các câu hỏi không sắp xếp theo thứ tự khó dần lên bạn có thể lướt hết qua mọi thứ để thử thách bản thân mình.
Câu 1: Javascript là gì?
JavaScript, theo phiên bản hiện hành, là một ngôn ngữ lập trình thông dịch được phát triển từ các ý niệm nguyên mẫu. Ngôn ngữ này được dùng rộng rãi cho các trang web (phía người dùng) cũng như phía máy chủ (với Nodejs).
Câu 2: Sự khác nhau giữa JavaScript và JScript là gì?
Đơn giản bạn có thể nói JScript giống như JavaScript, nhưng nó được cung cấp bởi Microsoft.
Câu 3: Trong javascript đối tượng window được sử dụng để làm gì?
Đối tượng window được tạo tự động bởi trình duyệt đại diện cho một cửa sổ trình duyệt. Nó được sử dụng để hiển thị hộp thoại bật lên như hộp thoại alert, confirm, v.v. và mọi thứ trong Javascript như object, functions, variables đều có thể trở thành window object bao gồm cả HTML DOM.
Ví dụ
window.document.getElementById("header");
hoặc:
document.getElementById("header");
Câu 4: Sự khác nhau giữa == và === là gì?
Toán tử == chỉ kiểm tra tính bằng nhau, còn === kiểm tra tính bằng nhau và giá trị kiểu dữ liệu tức là phải cùng kiểu dữ liệu.
Ví dụ:
var number1 = 12;
var number2 = '12';
console.log('log 1: ', number1 == number2);
console.log('log 2: ', number1 === number2);
Đáp án:
log 1: true
log 2: false
Câu 5: Negative Infinity là gì?
Negative Infinity là một số trong JavaScript có thể được bắt nguồn bằng cách chia số âm cho 0. Khi nào sử dụng nó, khi nó là một số hoặc Number object và nó sẽ return về undefined.
Ví dụ:
var my_number = 100;
my_number.NEGATIVE_INFINITY;
console.log(my_number);
Đáp án:
undefined
Câu 6: Cách để xử lý các ngoại lệ trong JavaScript?
Sử dụng khối try/catch, chúng ta có thể xử lý các ngoại lệ trong JavaScript. JavaScript hỗ trợ các từ khóa try, catch, finally, throw để xử lý ngoại lệ.
Ví dụ
function check(x) {
try {
if (x == '') throw 'empty';
if (isNaN(x)) throw 'not a number';
x = Number(x);
if (x < 5) throw 'too low';
if (x > 10) throw 'too high';
} catch (err) {
console.log('catch ', err);
}
}
console.log(check(''));
console.log(check('test'));
console.log(check(55));
Đáp án:
catch empty
undefined
catch not a number
undefined
catch too high
undefined
Câu 7: Hàm isNaN() là gì?
Hàm isNaN() trả về true nếu giá trị của biến không phải là một số.
Ví dụ
console.log(isNaN(123));
console.log(isNaN(0));
console.log(isNaN('Hello'));
console.log(isNaN('2005/12/12'));
console.log(isNaN(''));
console.log(isNaN(true));
console.log(isNaN(undefined));
Đáp án:
false
false
true
true
false
false
Câu 8: Cách để comment trong Javascript?
Sử dụng // cho một dòng hoặc sử dụng /* nội dung */ cho nhiều dòng
Ví dụ
// Khai báo biến
var number = 2;
/*
Đây là cách
để comment
nhiều dòng
*/
var girl_friend = null;
Câu 9: Tại sao 0.1 + 0.2 không bằng 0.3 ?
Vấn đề này liên quan đến việc Javascript lưu trữ dữ liệu float ở dạng nhị phân chính xác tới từng con số sau dấu phẩy.
Giải pháp:
- Sử dụng hàm toFixed()
- Mẹo nhỏ là nhân với 10 và chia cho 10
- Tham khảo các hàm làm tròn như round(), v.v.
Ví dụ
console.log(0.1 + 0.2);
// Amazing !
var x = (0.2 * 10 + 0.1 * 10) / 10;
console.log(x);
// Sử dụng toFixed()
var number = 0.1 + 0.2;
console.log(number.toFixed(2));
Đáp án:
0.30000000000000004
0.3
0.3
Câu 10: Phân biệt giữa Function Declaration và Function Expression.
Function declaration sử dụng từ khóa function rồi đến tên hàm. Còn Function expression bắt đầu bằng var, let hoặc const, theo sau là tên của hàm và toán tử =.
Ví dụ
// Function Declaration
function sum(x, y) {
return x + y;
}
// Function Expression: ES5
var sum = function (x, y) {
return x + y;
};
// Function Expression: ES6+
const sum = (x, y) => {
return x + y;
};
Câu 11: Tại sao Math.max() lại nhỏ hơn Math.min().
Khi chạy code Math.max() > Math.min(), giá trị trả về là False, nghe có vẻ không hợp lý. Tuy nhiên, nếu không có tham số nào được truyền vào, Math.min() trả về Infinity và Math.max() trả về -Infinity. Vậy nên Math.max() < Math.min().
Nếu tham số xuất hiện là infinity và một số nào khác, kết quả trả về sẽ là số có giá trị đó.
Ví dụ
var infinity = 5;
var value1 = Math.min(1);
var value2 = Math.min(1, infinity);
var value3 = Math.min(1, -infinity);
console.log(value1);
console.log(value2);
console.log(value3);
Đáp án:
1
1
-5
Câu 12: Closure trong Javascript là gì?
Dể hiểu, closure là 1 hàm nội truy cập đến các biến bên ngoài phạm vi của nó. Closure có thể được sử dụng để implement privacy và tạo ra các function factory.
Ví dụ
const arr = [1, 2, 3, 4];
for (var i = 0; i < arr.length; i++) {
setTimeout(function () {
console.log(i);
}, 10);
}
Đáp án:
4
4
4
4
Lý do là bởi vì hàm setTimeout sẽ tạo ra 1 function (closure) có thể truy cập phạm vi bên ngoài nó, vòng loop sẽ chứa index i. Sau 10ms, hàm được thực thi và nó sẽ log ra giá trị của i, là giá trị cuối cùng của vòng lặp (4).
Câu 13: Hàm encodeURI() là gì?
Hàm này mã hóa các ký tự đặc biệt, ngoại trừ :, /? : @ & = + $ #
Để mã hóa ngược chuỗi đó lại mình sử dụng hàm decodeURI().
Ví dụ
var uri = 'my test.asp?name=ståle&car=saab';
var res = encodeURI(uri);
console.log(res);
Đáp án:
my%20test.asp?name=st%C3%A5le&car=saab
Câu 14: Array() khác với [] như nào trong khi tạo ra một array trong JavaScript?
Nếu sử dụng cách tạo array initializer nó sẽ tạo ra danh sách các phần tử trong mảng và được ngăn cách bởi dấu phẩy.
Ví dụ
var arr1 = [5];
var arr2 = new Array(5);
console.log(arr1);
console.log(arr2);
Đáp án:
[ 5 ]
[ empty items x5 ]
Câu 15: Strict mode trong JavaScript là gì?
Strict theo nghĩa tiếng Việt là "nghiêm khắc". Strict Mode là một quy mẫu nghiêm khắc trong Javascript. Nếu như việc viết code bình thường là Normal mode, thì Strict Mode sẽ có thêm các quy định khác so với Normal mode.
Ví dụ
'use strict';
function foo() {
var bar = 0;
return bar;
}
bar = 1;
Đáp án:
Xảy ra lỗi:
ReferenceError: bar is not defined
Câu 16: Variable typing trong JavaScript là gì?
JavaScript là một ngôn ngữ rất lỏng lẻo. Biến chỉ được xác định khi giá trị được gán và có thể thay đổi khi biến xuất hiện trong các ngữ cảnh khác nhau. Đơn giản nó là kiểu dữ liệu của biến đó.
Để kiểm tra kiểu dữ liệu của biến đó ta dùng typeof trong Javascript.
Ví dụ
var length = 16;
var lastName = 'Johnson';
var x = { firstName: 'John', lastName: 'Doe' };
console.log(typeof length);
console.log(typeof lastName);
console.log(typeof x);
Đáp án:
number
string
object
Câu 17: Các kiểu dữ liệu trong Javascript là gì?
Javascript có những kiểu dữ liệu sau:
- Number
- String
- Boolean
- Object
- Undefined
Ví dụ
var a = 5;
var b = 'Hello';
var c = true;
var d = { id: 1, name: 'Lyly' };
console.log(typeof a);
console.log(typeof b);
console.log(typeof c);
console.log(typeof d);
console.log(typeof e);
Đáp án:
number
string
boolean
object
undefined
Câu 18: this
trong Javascript là gì?
Từ khóa this dùng để chỉ đối tượng từ nơi nó được gọi.
Ví dụ
var Student = {
name: 'Lyly',
age: 20,
getName: function () {
return this.name;
},
};
console.log(Student.getName());
Đáp án:
Lyly
Câu 19: Khác nhau giữa ViewState và SessionState là gì?
-
ViewState là dành riêng cho một trang trong phiên.
-
SessionState dành riêng cho dữ liệu cụ thể của người dùng có thể được truy cập trên tất cả các trang trong ứng dụng web.
Câu 20: Làm sao để thay đổi style/class của element?
Có thể sử dụng document để thay đổi style/class.
Ví dụ
document.getElementById('myId').style.fontSize = '20px';
// or
document.getElementById('myId').className = 'newclass';
Câu 21: Các cấu trúc lặp trong Javascript là gì?
Có các vòng lặp sau:
- for
- while
- do-while loops
Ví dụ
var arr = ['apple', 'banana', 'mango', 'cherry'];
// Sử dụng for
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
// Sử dụng while
let j = 0;
while (j < arr.length) {
console.log(arr[j]);
j++;
}
// Sử dụng do-while loops
let k = 0;
do {
console.log(arr[k]);
k++;
} while (k < arr.length);
Đáp án:
cherry
apple
banana
mango
Câu 22: Kết quả của 5 + 2 + "7"
là gì?
Vì 3 và 2 là số nguyên, chúng sẽ cộng vào với nhau và kết quả là số. Còn 7 là một chuỗi, nên Javascipt sẽ hiểu thành nối chuỗi. Vì vậy, kết quả sẽ là 77.
Ví dụ
console.log(5 + 2 + '7');
Đáp án:
77
Câu 23: Chức năng của delete
là gì?
delete dùng để xóa các property cũng như các giá trị.
Ví dụ
var student = { name: 'Lyly', age: 20 };
delete student.age;
console.log(student);
Đáp án:
{ name: 'Lyly' }
Câu 24: Hàm pop()
trong Javascript để làm gì?
pop() trong Javascript dùng để lấy phần tử cuối cùng trong mảng. Điều này thì trái ngược với hàm shift().
Ví dụ
var number = ['one', 'two', 'three', 'four'];
console.log(number.pop());
console.log(number.shift());
Đáp án:
four
one
Câu 25: Kết quả in ra là gì?
Ví dụ
var myArray = [[[]]];
console.log(myArray);
Đáp án:
Là một mảng 3 chiều [ [ [] ] ]
Câu 26: let
và const
trong Javascript là gì?
Từ khóa let & const được giới thiệu trong phiên bản ES6 với tầm nhìn tạo ra hai loại biến khác nhau trong javascript, một loại là bất biến và loại khác là có thể thay đổi.
-
const: Nó được sử dụng để tạo ra một biến bất biến. Biến không thay đổi là các biến có giá trị không bao giờ thay đổi trong vòng đời hoàn chỉnh của chương trình.
-
let: let được sử dụng để tạo một biến có thể thay đổi. Các biến có thể thay đổi là các biến bình thường như var có thể thay đổi bất kỳ số lượng thời gian nào.
Ví dụ
let name = 'Lyly';
const age = 18;
name = 'John';
age = 20;
console.log(name);
console.log(age);
Đáp án:
John
TypeError: Assignment to constant variable.
Câu 27: Làm sao để thêm hoặc xóa sửa trong object Javascript?
Ta có thể thêm một thuộc tính vào một đối tượng bằng object.property_name = value, delete object.property_name để xóa một thuộc tính.
Ví dụ
let user = new Object();
user.name = 'Lyly';
user.age = 20;
console.log(user);
delete user.age;
console.log(user);
Đáp án:
{ name: 'Lyly', age: 20 }
{ name: 'Lyly' }
Câu 28: Cách để xóa các phần tử giống nhau trong mảng sử dụng ES6?
Dưới đây là một số cách:
Ví dụ
var array = [1, 2, 6, 5, 3, 2, 6];
// Sử dụng Set
console.log(...new Set(array));
// Sử dụng filter
console.log(array.filter((item, index) => array.indexOf(item) === index));
// Sử dụng reduce
console.log(array.reduce((uniq, item) => (uniq.includes(item) ? uniq : [...uniq, item]), []));
Đáp án:
1 2 6 5 3
[ 1, 2, 6, 5, 3 ]
[ 1, 2, 6, 5, 3 ]
Câu 29: Khác nhau giữa từ khóa undefined
và null
là gì?
Khi bạn khởi tạo ra một biến nhưng không gán giá trị cho nó thì sẽ là undefined. Còn null là một object.
Ví dụ
var a;
console.log(typeof a);
console.log(typeof null);
Đáp án:
undefined
object
Câu 30: Một số Framework để test Javascript là gì?
Các framework phổ biến nhất hiện nay:
- Unit.js
- Jasmine
- Karma
- Chai
- AVA
- Mocha
- JSUnit
- QUnit
- Jest
Ví dụ
// Sử dụng Chai
var answer = 43;
expect(answer).to.equal(42);
Đáp án:
AssertionError: expected 43 to equal 42.
Câu 31: export
và import
là gì?
export hay import là cách để ta tạo ra các module trong Javascript. Bằng cách đó, ta có thể chia các phần nhỏ trong dự án để dễ quản lý. import cho phép ta lấy một số biến hoặc một phương thức nào đó của file. Còn export là biến một file thành một module. Xem ví dụ để hiểu hơn.
Ví dụ
// person.js
let name = 'Lyly',
occupation = 'developer',
age = 20;
export { name, age };
//index.js
import { name, age } from './person';
console.log(name);
console.log(age);
Đáp án:
Lyly
20
Câu 32: Làm sao để chuyển đổi ngày trong Javascript thành tiêu chuẩn ISO?
Hàm toISOString() được sử dụng để chuyển đổi ngày javascript thành tiêu chuẩn ISO. Nó chuyển đổi đối tượng Ngày JavaScript thành một chuỗi, sử dụng tiêu chuẩn ISO.
Ví dụ
var date = new Date();
var n = date.toISOString();
console.log(n);
// YYYY-MM-DDTHH:mm:ss.sssZ
Câu 33: Cách để clone một object trong Javascript?
Hàm Object.assign() được sử dụng để clone một đối tượng trong Javascript. Ngoài ra bạn cũng có thể sử dụng clone của lodash. Lodash là một framework sử lý mạnh mẽ mảng và object. Xem thêm tại Lodash
Ví dụ
var x = { name: 'Lyly' };
var y = Object.assign({}, x);
console.log(y);
Đáp án:
{ name: 'Lyly' }
Câu 34: Cách để tạo mảng trong Javascript?
Có 3 cách khác nhau để tạo mảng trong Javascript. Xem ví dụ
Ví dụ
var arr1 = [1, 2, 3, 4];
var arr2 = new Array();
var arr3 = new Array(1, 2, 3, 4);
console.log(arr1);
console.log(arr2);
console.log(arr3);
Đáp án:
[ 1, 2, 3, 4 ]
[]
[ 1, 2, 3, 4 ]
Câu 35: Các sự kiện chuột HTML DOM là gì?
Một số sự kiện chuột trong DOM như:
- onclick
- ondblclick
- mousemove
- mousedown
- mouseover
- mouseout
- mouseup
Câu 36: Giá trị in ra màn hình là gì?
Ví dụ
console.log(undefined * 2);
console.log(null * 2);
console.log('' * 2);
Đáp án:
NaN
0
0
Câu 37: Tại sao 018 - 017 = 3
trong Javascript?
Việc 018 - 017 trả về 3 là kết quả của chuyển đổi loại im lặng. Trong trường hợp này, ta nói về số bát phân.
Ví dụ
console.log(018 - 017);
Đáp án:
3
Câu 38: Phân biệt giữa test() và exec()?
Cả test() và exec() đều là biểu thức RegExp. Xem chi tiết
-
Sử dụng test() sẽ search chuỗi trong theo giá trị ta truyền vô, nếu chuỗi đó tồn tại thì sẽ return về Boolean giá trị là 'true' hoặc 'false'.
-
Sử dụng exec() sẽ search chuỗi trong theo giá trị ta truyền vô, nếu chuỗi đó tồn tại thì sẽ return về chuỗi đó, nếu không sẽ return về giá trị 'null'.
Ví dụ
var str = 'The best things in life are free';
var patt = new RegExp('b');
var res_test = patt.test(str);
var res_exec = patt.exec(str);
console.log(res_test);
console.log(res_exec);
Đáp án:
true
[ 'b', index: 4, input: 'The best things in life are free' ]
Câu 39: Kết quả in ra là gì?
Ví dụ
setTimeout(function () {
console.log('first line');
}, 0);
console.log('second line');
console.log('third line');
Đáp án:
second line
third line
first line
Khi có setTimeout() tiến trình trở thành bất đồng bộ. Ta cần chờ mọi thứ trong stack hoàn thành trước.
Câu 40: Các cách để accessed vào HTML element trong Javascript?
Có những cách sau:
- getElementById() lấy một element bằng tên id.
- getElementsByClass() lấy một element bằng tên class.
- getElementsByTagName() lấy một element bằng tên của tag name.
- querySelector() đây là function css style selector và sẽ return về giá trị đầu tiên.
Ví dụ
<!DOCTYPE html>
<html>
<body>
<p id="demo">Click the button to change the text in this paragraph.</p>
<p class="demo">Click the button to change the text in this paragraph.</p>
<h5>Click the button to change the text in this paragraph.</h5>
<p class="example">Click the button to change the text in this paragraph.</p>
<script>
// Sử dụng getElementById()
document.getElementById('demo').innerHTML = 'Hello World';
// Sử dụng getElementsByClassName()
document.getElementsByClassName('demo')[0].innerHTML = 'Hello World!';
// Sử dụng getElementsByTagName()
document.getElementsByTagName('h5')[0].innerHTML = 'Hello World!';
// Sử dụng querySelector()
document.querySelector('.example').style.backgroundColor = 'red';
</script>
</body>
</html>
Câu 41: Có mấy cách sử dụng Javascript trong HTML?
Có 3 cách sau:
- Inline
- Internal
- External
Ví dụ
<!-- Inline -->
<a href="#" onclick="(function(){alert('Hello world');})()">Click Me</a>
<!-- Internal -->
<script>
console.log('Hello world')
</script>
<!-- External -->
<script type="text/javascript" src="external.js">
// file external.js
console.log('Hello world')
Câu 42: Một số framework của Javascript là gì?
Một số framework nổi tiếng của Javascript hiện nay là:
- React
- Angular
- Vue
- NextJs
- NuxtJS
- Meteor
- Ember
Ví dụ
// React
class HelloMessage extends React.Component {
render() {
return <div>Hello {this.props.name}</div>;
}
}
ReactDOM.render(<HelloMessage name='Taylor' />, document.getElementById('hello-example'));
Câu 43: Kết quả in ra là gì?
Ví dụ
var a = !+[] + [] + ![];
console.log(a.length);
Đáp án:
9
Đây là điều thú vị của Javascript.
Câu 44: Sự khác nhau giữa .forEach và .map trong Javascript là gì?
.forEach
- Vòng lặp dựa vào các phần tử có trong mảng.
- Thực hiện callback cho mỗi vòng lặp.
- Không trả về giá trị.
.map
- Vòng lặp dựa vào các phần tử có trong mảng.
- Hàm map sẽ lặp qua từng phần tử nhưng sẽ tạo ra một mảng mới dựa trên các giá trị trong vòng lặp.
Ví dụ
const a = [1, 2, 3];
const ex1 = a.forEach((num, index) => {
// Làm 1 điều gì đó
});
const ex2 = a.map((num) => {
return num * 2;
});
console.log(ex1);
console.log(ex2);
Đáp án:
undefined
[ 2, 4, 6 ]
Câu 45: JSON là gì và cách sử dụng?
JSON là một định dạng dữ liệu dựa trên văn bản theo cú pháp đối tượng JavaScript.
Ví dụ
// Chuyển object qua JSON
var obj1 = [
{ id: 1, name: 'Lyly' },
{ id: 2, name: 'May' },
];
console.log(JSON.stringify(obj1));
// Chuyển JSON về object
var obj2 = '{ "id": 9, "name": "Lyly", "age": "20", "city": "New York" }';
console.log(JSON.parse(obj2));
Đáp án:
[{"id":1,"name":"Lyly"},{"id":2,"name":"May"}]
{ id: 9, name: 'Lyly', age: '20', city: 'New York' }
Câu 46: Sự khác nhau giữa slice
và splice
là gì?
slice | splice |
---|---|
Không làm thay đổi mảng ban đầu | Có thể bị thay đổi mảng ban đầu |
Trả về tập hợp con của mảng ban đầu | Trả về các phần tử bị xóa khỏi mảng ban đầu |
Sử dụng để lấy các phần tử con trong mảng | Sử dụng để thêm hoặc xóa phần tử của mảng |
Ví dụ
// Sử dụng slice
var fruits = ['Banana', 'Orange', 'Lemon', 'Apple', 'Mango'];
var citrus = fruits.slice(1, 3);
console.log(fruits);
console.log(citrus);
// Sử dụng splice
var fruits = ['Banana', 'Orange', 'Apple', 'Mango'];
fruits.splice(2, 0, 'Lemon', 'Kiwi');
console.log(fruits);
Đáp án:
[ 'Banana', 'Orange', 'Lemon', 'Apple', 'Mango' ]
[ 'Orange', 'Lemon' ]
[ 'Banana', 'Orange', 'Lemon', 'Kiwi', 'Apple', 'Mango' ]
Câu 47: Higher order function trong Javascript là gì?
Higher order function là hàm chấp nhận hàm khác làm đối số hoặc trả về hàm dưới dạng giá trị trả về.
Ví dụ
const higherOrderFunc = () => console.log('Hello world !');
const higherOrder = (ReturnHigherOrderFunc) => ReturnHigherOrderFunc();
higherOrder(higherOrderFunc);
Đáp án:
Hello world !
Câu 48: Hàm unary
trong Javascript là gì?
Hàm unary (monadic) là một hàm chấp nhận chính xác một đối số. Nó là viết tắt của một đối số được chấp nhận bởi một hàm.
Ví dụ
const unaryFunction = (a) => console.log(a + 10);
unaryFunction(5);
Đáp án:
15
Câu 49: IIFE (Immediately Invoked Function Expression) trong Javascript là gì?
IIFE (Immediately Invoked Function Expression) là một hàm JavaScript chạy ngay khi được định nghĩa.
Lý do chính để sử dụng IIFE là để có được quyền riêng tư dữ liệu vì bất kỳ biến nào được khai báo trong IIFE đều không thể được truy cập bởi bên ngoài. Tức là, nếu bạn cố gắng truy cập các biến bằng IIFE thì nó sẽ xuất hiện một lỗi như dưới đây:
Ví dụ
// Ví dụ IIFE
(function () {
// logic here
})();
// Báo lỗi khi chạy
(function () {
var message = 'IIFE';
console.log(message);
})();
console.log(message);
Đáp án:
ReferenceError: message is not defined
Câu 50: Làm thể nào để nhận notification từ server-send?
Ta có thể sử dụng EventSource để nhận thông báo sự kiện do máy chủ gửi. Xem chi tiết EventSource
Ví dụ
// Ví dụ mẫu
if (typeof EventSource !== 'undefined') {
var source = new EventSource('sse_generator.js');
source.onmessage = function (event) {
document.getElementById('output').innerHTML += event.data + '<br>';
};
}
Câu 51: Promise.all
trong Javascript là gì?
Promise.all là một hàm sẽ lấy một loạt các Promises làm đầu vào (có thể lặp lại). Phương thức này nhận vào một mảng các promises và chỉ resolve khi tất cả các promises này hoàn thành, hoặc reject khi một trong số chúng xảy ra lỗi.
Ví dụ
Promise.all([Promise1, Promise2, Promise3])
.then((result) => {
console.log(result);
})
.catch((error) => {
console.log(`Error in promises ${error}`);
});
Câu 52: Promise.race
trong Javascript là gì?
Promise.race nghĩa là hàm promise chạy đua (LOL). Phương thức này nhận vào một mảng các promises và sẽ resolve/reject ngay khi một trong số các promises này hoàn thành/xảy ra lỗi.
Ví dụ
var promise1 = new Promise(function (resolve, reject) {
setTimeout(resolve, 500, 'one');
});
var promise2 = new Promise(function (resolve, reject) {
setTimeout(resolve, 100, 'two');
});
Promise.race([promise1, promise2]).then(function (value) {
console.log(value);
});
Đáp án:
two
Câu 53: Hàm eval() trong Javascript là gì?
Hàm eval() dùng để tính toán một chuỗi trong Javascript. Nó sẽ nhận vào một chuỗi và biến nó qua phép tính. Chuỗi có thể là biểu thức JavaScript, biến, câu lệnh hoặc chuỗi câu lệnh.
Ví dụ
var a = '1 + 5 - 3';
var b = '10 / 2' + 6;
console.log(eval(a));
console.log(eval(b));
Đáp án:
3
0.38461538461538464
Câu 54: Ví dụ đơn giản để so sánh 2 object với nhau?
Để so sánh 2 object có khá nhiều cách khác nhau. Tuy nhiên có một cách rất đơn giản đó là parse qua JSON bằng cách sử dụng JSON.sstringify().
Ví dụ
var user1 = { name: 'Lyly', org: 'dev' };
var user2 = { name: 'Lyly', org: 'dev' };
var dog = { name: 'dog', age: 10 };
var cat = { name: 'cat', age: 10 };
var compare_user = JSON.stringify(user1) === JSON.stringify(user2);
var compare_animal = JSON.stringify(dog) === JSON.stringify(cat);
console.log(compare_user);
console.log(compare_animal);
Đáp án:
true
false
Câu 55: Khác nhau giữa parameter và argument là gì?
Các parameter là tên biến của định nghĩa hàm, trong khi các argument là các giá trị được cung cấp cho hàm khi nó được gọi.
Ví dụ
function myFunction(parameter1, parameter2) {
console.log(arguments[0]);
}
myFunction('argument1', 'argument2');
Đáp án:
argument1
Câu 56: Kết quả trả về là gì?
Ví dụ
const a = [1, 2, 3];
const b = [1, 2, 3];
const c = '1,2,3';
console.log(a == c);
console.log(a == b);
Đáp án:
true
false
console.log đầu tiên sẽ trả về là đúng vì trình biên dịch của JavaScript thực hiện chuyển đổi type của biến và do đó, nó so sánh với các chuỗi theo giá trị của chúng. Mặt khác, console.log thứ hai sẽ trả về sai vì Mảng là object và object được so sánh bằng tham chiếu.
Câu 57: Kết quả trả về là gì?
Ví dụ
function greet() {
return;
{
message: 'hello';
}
}
var a = greet();
console.log(a);
Đáp án:
undefined
Do tính năng chèn dấu chấm phẩy tự động (ASI) của JavaScript, trình biên dịch đặt dấu chấm phẩy sau từ khóa trả về và do đó, nó trả về undefined mà không bị lỗi.
Câu 58: Kết quả trả về là gì?
Ví dụ
console.log(typeof typeof 0);
Đáp án:
string
Do typeof 0 là "number" nên typeof của "number" sẽ là chuỗi. Javascript thật đáng sợ !!!
Câu 59: Kể tên một số thư viện Javascript xử lí array và object?
Có 2 thư viện xử lí array và object nổi tiếng nhất hiện nay là:
- lodash Xem chi tiết
- underscore.js Xem chi tiết
Ví dụ
// underscore.js
_.map([1, 2, 3], (num) => num * 3);
// lodash
_.map([4, 8], (x) => x * 2);
// Cách sử dụng khá giống nhau
Đáp án:
[3, 6, 9]
[8, 16]
Câu 60: Kết quả trả về là gì?
Khi xét thuộc tính cho object, JavaScript sẽ ngầm định stringify parameter. Trong trường hợp này, vì b và c là cả hai là object, nên sẽ được chuyển đổi thành "[Object Object]". Kết quả là, cả [b] và [c] đều tương đương với ["[Object Object]"] và có thể được sử dụng thay thế cho nhau. Do đó, khi ta tham chiếu [c] cũng giống như là tham chiếu [b].
Ví dụ
var a = {},
b = { key: 'b' },
c = { key: 'c' };
a[b] = 123;
a[c] = 456;
console.log(a[b]);
Đáp án:
456
Câu 61: Kết quả trả về là gì?
Ví dụ
var a = [1, 2, 3];
a[10] = 99;
console.log(a);
console.log(a[6]);
Đáp án:
[ 1, 2, 3, <7 empty items>, 99 ]
undefined
Câu 62: Kết quả trả về là gì?
Ví dụ
var output = (function (x) {
delete x;
return x;
})(0);
console.log(output);
Đáp án:
Kết quả trả về là 0. Bởi vì delete dùng để xóa property của object. Nhưng ở đây x không phải là object, nó là biến local variable nên delete không thể xóa được.
Câu 63: Kết quả trả về là gì?
Ví dụ
var x = 1;
var output = (function () {
delete x;
return x;
})();
console.log(output);
Đáp án:
Kết quả trả về là 1. Bởi vì delete dùng để xóa property của object. Nhưng ở đây x không phải là object, nó là biến global variable có typeof number.
II. fullstack-interviews
Được phát triển bởi Brendan Eich vào năm 1995, JavaScript là một trong những ngôn ngữ lập trình phổ biến nhất với các web developer.
Nó được tạo ra cho phát triển các trang web động. Một chương trình JS được gọi là script, nó có thể được đính kèm vời bất kỳ trang web HTML nào. Các script sẽ tự động chạy khi trang được tải.
Ở hiện tại JavaScript cũng có thể chạy được trên cả server hay bất kỳ thiết bị nào có JavaScript Engine.
Dưới đây là các câu hỏi phỏng vấn JavaScript thường gặp.
Câu 1. Sự khác biệt giữa các kiểu dữ liệu trong JavaScript?
Trong JavaScript các kiểu dữ liệu được chia làm hai loại là kiểu nguyên thuỷ và đối tượng. Để biết kiểu dữ liệu của các biến JavaScript, ta có thể sử dụng typeof.
Kiểu nguyên thuỷ
String - biểu diễn một mảng ký tự hay một chuỗi. Kiểu chuỗi trong javascript có thể sử dụng một cặp dấu ngoặc kép hoặc dấu ngoặc kép đơn.
var str = 'Vivek Singh Bisht'; //sử dụng dấu ngoặc kép
var str2 = 'John Doe'; // sử dụng dấu ngoặc đơn
Number - biểu diễn cả số nguyên và số thực.
var x = 3; // số nguyên
var y = 3.6; // số thực
BigInt - kiểu dữ liệu này được sử dụng để lưu trữ các số vượt quá giới hạn của kiểu dữ liệu Number. Nó có thể lưu trữ các số nguyên lớn và được biểu diễn bằng cách thêm “n” vào một chữ số nguyên.
var bigInteger = 234567890123456789012345678901234567890;
Boolean - kiểu luận lý, có hai giá trị là true và false. Thường được dùng với điều kiện.
var a = 2;
var b = 3;
var c = 2;
(a == b)(
// trả về false
a == c,
); // trả về true
undefined - khi giá trị của một biến là không xác định.
var x; // giá trị của x là undefined
var y = undefined; // ta cũng có thể gán một biến là undefined
null - biểu diễn giá trị null. Vì JavaScript là case-sensitive, null sẽ không giống với Null
, NULL
, hoặc bất kỳ biến thể khác.
var z = null;
Symbol - mới được giới thiệu trong ES6. Nó lưu trữ các giá trị duy nhất và ẩn danh.
var symbol1 = Symbol('symbol');
Sử dụng typeof
để tìm kiểu nguyên thuỷ:
typeof 'John Doe'; // Returns "string"
typeof 3.14; // Returns "number"
typeof true; // Returns "boolean"
typeof 234567890123456789012345678901234567890n; // Returns bigint
typeof undefined; // Returns "undefined"
typeof null; // Returns "object" (đặc trưng của JavaScript)
typeof Symbol('symbol'); // Returns Symbol
Trong JavaScript nếu dữ liệu không phải là kiểu nguyên thuỷ thì tất cả đều là object.
Object dùng để lưu trữ tập hợp dữ liệu
// Tập hợp dữ liệu dạng key-value
var obj1 = {
x: 43,
y: 'Hello world!',
z: function () {
return this.x;
},
};
// Tập hợp dữ liệu dạng danh sách
var array1 = [5, 'Hello', true, 4.1];
Câu 2. Giải thích về Hoisting trong Javascript?
Hoisting là một hành vi mặc định trong Javascript, nó sẽ chuyển tất cả khai báo biến và hàm lên trên cùng.
Điều này có nghĩa là bất kể hàm và biến được khai báo ở đâu, chúng cũng sẽ đuọc chuyển lên đầu scope. Scope có thể là toàn cục hoặc cục bộ.
Ví dụ 1:
hoistedVariable = 3;
console.log(hoistedVariable);
// output là 3 vì biến được khởi tạo trước khi khai báo.
var hoistedVariable;
Ví dụ 2:
hoistedFunction();
// Outputs " Hello world! " kể cả khi hàm được khai báo sau khi gọi.
function hoistedFunction() {
console.log(' Hello world! ');
}
Ví dụ 3:
// Hoisting takes place in the local scope as well
function doSomething() {
x = 33;
console.log(x);
var x;
}
Lưu ý: Khai báo biến được hoisting chứ phép gán biến thì không.
var x;
console.log(x); // Output sẽ trả về "undefined" vì phép gán không được hoisting
x = 23;
Lưu ý: Để tránh hoisting bạn có thể dùng "use strict"
'use strict';
x = 23; // Báo lỗi x chưa được khai báo
var x;
Câu 3. Sự khác biệt giữa toán tử "==" và "===" là như thế nào?
Cả hai đều dùng để so sánh, nhưng "==" có thể dùng với bất kỳ kiểu dữ liệu nào còn "===" chỉ dành cho cùng kiểu.
Ví dụ:
var x = 2;
var y = '2';
(x == y)(
// Trả về true vì cả hai cùng giá trị
x === y,
); // Trả về false vì typeof x là "number" còn typeof y là "string
Câu 4. Giải thích về ép kiểu ngầm trong JavaScript?
Ép kiểu ngầm trong javascript là sự chuyển đổi tự động của giá trị từ kiểu dữ liệu này sang kiểu khác. Nó xảy ra khi thực hiện một biểu thức với các kiểu dữ liệu khác nhau.
Ép kiểu String
Ép kiểu string xảy ra khi dùng toán tử +
. Một số cộng với một chuỗi, kiểu số sẽ bị ép thành kiểu chuỗi.
Ví dụ:
var x = 3;
var y = '3';
x + y; // Returns "33"
var x = 24;
var y = 'Hello';
x + y; // Returns "24Hello";
Để hiểu về hai ví dụ khi ta cộng một số vào chuỗi, thì khi JavaScript thấy biểu thức x+y
với hai kiểu khác nhau (một số và một chuỗi), nó chuyển đổi kiểu số thành chuỗi để thực hiện hành động. Sau khi chuyển đổi, cả hai biến đều là kiểu chuỗi, thao tác +
lúc này sẽ thành phép nối chuỗi kết quả là ra chuỗi "33" và "24Hello".
Ngược lại, khi thực hiện phép toán -
, thì chuỗi lại bị ép kiểu ngầm thành số. Ví dụ:
var x = 3;
var y = '3';
x - y; //Returns 0 since the variable y (string type) is converted to a number type
Ép kiểu Boolean
Ép kiểu boolean xảy ra khi sử dụng các toán tử logic, lệnh if hay kiểm tra vòng lặp. Để hiểu về ép kiểu logic, ta cần hiểu về giá trị truthy và falsy.
Giá trị truthy là cái sẽ được ép kiểu thành true. Còn falsy sẽ được ép kiểu thành false.
Tất cả các giá trị ngoại trừ 0
, 0n
, -0
, ""
, null
, undefined
, và NaN
thì đều là truthy.
Câu lệnh If:
var x = 0;
var y = 23;
if (x) {
console.log(x);
} // The code inside this block will not run since the value of x is 0(Falsy)
if (y) {
console.log(y);
} // The code inside this block will run since the value of y is 23 (Truthy)
Toán tử Logic:
Toán tử logic trong javascript không giống các ngôn ngữ lập trình khác, nó không trả về true hay false, mà nó trả về một toán hạng.
OR ( || ) - Nếu giá trị đầu tiên là truthy, giá trị đầu tiên sẽ được trả về, ngược lại thì nó trả về giá trị thứ hai.
AND ( && ) - Nếu hai giá trị đều là truthy, giá trị thứ hai sẽ được trả về. Nếu giá trị đầu là falsy sẽ trả về giá trị đầu hoặc giá trị hai là falsy sẽ trả về giá trị hai.
Ví dụ:
var x = 220;
var y = 'Hello';
var z = undefined;
x || y; // Returns 220 since the first value is truthy
x || z; // Returns 220 since the first value is truthy
x && y; // Returns "Hello" since both the values are truthy
y && z; // Returns undefined since the second value is falsy
if (x && y) {
console.log('Code runs'); // This block runs because x && y returns "Hello" (Truthy)
}
if (x || z) {
console.log('Code runs'); // This block runs because x || y returns 220(Truthy)
}
Ép kiểu dấu bằng
Xảy ra khi thực hiện phép "==". Nhớ lại thì phép "==" được dùng để so sánh hai giá trị khác kiểu.
Thực tế khi sử dụng "==" một ép kiểu ngầm đã xảy ra, chuyển đổi tất cả toán hạng về cùng kiểu và so sánh chúng.
Ví dụ:
var a = 12;
var b = '12';
a == b; // Returns true because both 'a' and 'b' are converted to the same type and then compared. Hence the operands are equal.
Ép kiểu ngầm không xảy ra khi dùng "===".
var a = 226;
var b = '226';
a === b; // Returns false because coercion does not take place and the operands are of different types. Hence they are not equal.
Câu 5. JavaScript là ngôn ngữ kiểu tĩnh hay kiểu động?
JavaScript là ngôn ngữ kiểu động. Trong ngôn ngữ kiểu động, kiểu dữ liệu của biến được kiểm tra trong khi đang chạy chương trình ngược lại với kiểu tĩnh, nơi kiểu dữ liệu của biến được kiểm tra khi biên dịch.
Vì Javascript là ngôn ngữ kiểu động, nên một biến có thể giữ bất kỳ kiểu dữ liệu nào. Ví dụ:
var a = 23;
var a = 'Hello World!';
Câu 6. Thuộc tính NaN trong JavaScript là gì?
Thuộc tính NaN biểu diễn một giá trị Not-a-Number. Nó biểu thị một giá trị không phải là số.
typeof
của NaN trả về Number
. Muốn kiểm tra một giá trị có phải NaN không, có thể dùng hàm isNaN()
.
Ví dụ:
isNaN('Hello'); // Returns true
isNaN(345); // Returns false
isNaN('1'); // Returns false, since '1' is converted to Number type which results in 0 ( a number)
isNaN(true); // Returns false, since true converted to Number type results in 1 ( a number)
isNaN(false); // Returns false
isNaN(undefined); // Returns true
Câu 7. Giải thích về phép gán quá giá trị và phép gán qua tham chiếu?
Trong JavaScript, kiểu dữ liệu nguyên thuỷ được gán với giá trị, còn kiểu đối tượng được gán bằng tham chiếu.
Trước tiên, ta cần hiểu về điều gì xảy ra khi ta tạo một biến và gán giá trị cho nó.
var x = 2;
Trong ví dụ trên, ta tạo một biến x
và gán nó giá trị là "2". Phép "=" chỉ định một vài không gian trong bộ nhớ, để lưu trữ giá trị là "2" và trả về vị trí được chỉ định trong bộ nhớ. Do đó, biến x
ở trên trỏ đến vị trí trong bộ nhớ thay vì trỏ trực tiếp đến giá trị 2.
Phép gán thực hiện hành vi khác nhau khi làm việc với kiểu nguyên thuỷ và kiểu đối tượng.
Phép gán với kiểu nguyên thuỷ
var y = 234;
var z = y;
Ở ví dụ này, dòng đầu phép gán giá trị cho y
là kiểu nguyên thuỷ, sau đó ở dòng thứ hai, giá trị của y
được gán cho z
. Phép gán chỉ định một vùng không gian mới trong bộ nhớ và trả về địa chỉ của nó. Do đó, biến z
không chỉ đến vị trí của biến y
thay vào đó nó chỉ đến vùng không gian mới trong bộ nhớ.
var y = #8454; // y pointing to address of the value 234
var z = y;
var z = #5411; // z pointing to a completely new address of the value 234
// Changing the value of y
y = 23;
console.log(z); // Returns 234, since z points to a new address in the memory so changes in y will not effect z
Từ ví dụ trên, ta có thể thấy rằng các kiểu dữ liệu nguyên thủy khi được truyền cho một biến khác sẽ được truyền theo giá trị. Thay vì chỉ gán cùng một địa chỉ cho một biến khác, giá trị sẽ được gán và không gian bộ nhớ mới được tạo ra.
Phép gán với kiểu đối tượng
var obj = { name: 'Vivek', surname: 'Bisht' };
var obj2 = obj;
Trong ví dụ trên, phép gán truyền trực tiếp vị trí của biến obj
đến biến obj2
. Nói cách khác, tham chiếu của biến obj
được chuyển cho biến obj2
.
var obj = #8711; // obj pointing to address of { name: "Vivek", surname: "Bisht" }
var obj2 = obj;
var obj2 = #8711; // obj2 pointing to the same address
// changing the value of obj1
obj1.name = "Akki";
console.log(obj2);
// Returns {name:"Akki", surname:"Bisht"} since both the variables are pointing to the same address.
Từ ví dụ trên, ta có thể thấy rằng trong khi truyền các kiểu dữ liệu đối tượng, phép gán trực tiếp truyền địa chỉ (tham chiếu).
Do đó, các kiểu dữ liệu đối tượng luôn được truyền bằng tham chiếu.
Câu 8. IIFE là gì trong JavaScript?
Immediately Invoked Function (IIFE) là một hàm được chạy ngay sau khi nó được định nghĩa.
Cú pháp của IIFE:
(function () {
// Do something;
})();
Để hiểu về IIFE, trước hết cần hiểu về hai dấu ngoặc đơn được thêm vào để tạo IIFE.
Dấu ngoặc đơn đầu tiên:
(function () {
//Do something;
});
Khi thực thi code javascript, bất cứ khi nào trình biên dịch bắt gặp từ "function" nó cũng sẽ cho rằng ta đang khai báo một function. Do đó, nếu ta không dùng dấu ngoặc đơn, trình biên dịch sẽ báo lỗi vì nó nghĩa ta đang khái báo một function và theo cú pháp thì các function buộc phải có tên.
Để tránh lỗi ta thêm dấu ngoặc đơn vào để trình biên dịch biết đây không phải là khai báo function mà là một biểu thức function.
Dấu ngoặc đơn thứ hai:
(function () {
//Do something;
})();
Từ định nghĩa IIFE, ta biết rằng code sẽ chạy ngay sai khi khai báo. Một function chỉ chạy khi nó được gọi. Nếu ta không gọi, thì chúng ta chỉ nhận về được khai báo hàm.
Do đó để gọi function ta sử dụng dấu ngoặc đơn thứ hai.
Câu 9. Giải thích về HOC(Higher Order Functions) trong JavaScript?
Các hàm hoạt động trên các hàm khác bằng cách sử dụng chúng làm tham số hoặc kết quả trả về thì được gọi là Higher Order Functions - HOC. Ví dụ:
function higherOrder(fn) {
fn();
}
higherOrder(function () {
console.log('Hello world');
});
function higherOrder2() {
return function () {
return 'Do something';
};
}
var x = higherOrder2();
x(); // Returns "Do something"
Câu 10. Giải thích về "this"?
Từ khoá "this" trong javascript tham chiếu đến một đối tượng có thuộc tính là một hàm.
Giá trị của "this" phụ thuộc vào đối tượng đang gọi hàm.
Giả sử ta có code sau:
function doSomething() {
console.log(this);
}
doSomething();
Như vậy theo định nghĩa, this tham chiếu đến một đối tượng có hàm là thuộc tính. Vậy trong đoạn code trên hàm là thuộc tính của đối tượng nào?
Vì hàm được gọi từ ngữ cảnh tổng thể, nên hàm sẽ là thuộc tính của đối tượng toàn cục. Do đó, nếu ta chạy đoạn code trên trình duyệt kết quả sẽ là window object.
Ví dụ 2:
var obj = {
name: 'vivek',
getName: function () {
console.log(this.name);
},
};
obj.getName();
Trong đoạn code này, hàm getName
là thuộc tính của obj
. Do đó, this sẽ tham chiếu đến đối tượng obj
, và output sẽ là "vivek".
Ví dụ 3:
var obj = {
name: 'vivek',
getName: function () {
console.log(this.name);
},
};
var getName = obj.getName;
var obj2 = { name: 'akshay', getName };
obj2.getName();
Output sẽ là "akshay". Mặc dù hàm getName
được khai báo trong đối tượng obj
, nhưng ở thời điểm gọi thì getName()
lại là thuộc tính của obj2
, do đó "this" sẽ tham chiếu đến obj2
.
Cách ngớ ngẩn để hiểu "this" là, bất cứ khi nào hàm được gọi, hãy kiểm tra đối tượng trước dấu chấm. Giá trị của this sẽ luôn là đối tượng trước dấu chấm.
Nếu không có đối tượng nào như ở ví dụ 1, giá trị sẽ là đối tượng toàn cục.
Ví dụ 4:
var obj1 = {
address: 'Mumbai,India',
getAddress: function () {
console.log(this.address);
},
};
var getAddress = obj1.getAddress;
var obj2 = { name: 'akshay' };
obj2.getAddress();
Kết quả sẽ là lỗi, vì từ khóa this tham chiếu đến đối tượng obj2
, nhưng obj2
không có thuộc tính “address” ‘, do đó hàm getAddress
sẽ xảy ra lỗi.
Câu 11. Giải thích về phương thức call(), aplly() và bind()?
call()
Đó là một phương thức được xác định trước trong javascript.
Phương thức này gọi một phương thức (hàm) bằng cách chỉ định đối tượng sở hữu.
Ví dụ 1:
function sayHello() {
return 'Hello ' + this.name;
}
var obj = { name: 'Sandy' };
sayHello.call(obj);
// Returns "Hello Sandy"
Phương thức call()
cho phép một đối tượng sử dụng phương thức của đối tượng khác
Ví dụ 2:
var person = {
age: 23,
getAge: function () {
return this.age;
},
};
var person2 = { age: 54 };
person.getAge.call(person2);
// Returns 54
call()
chấp nhận tham số:
function saySomething(message) {
return this.name + ' is ' + message;
}
var person4 = { name: 'John' };
saySomething.call(person4, 'awesome');
// Returns "John is awesome"
apply()
Tương tự như phương thức call()
. Nhưng khác ở điểm phương thức call()
nhận các tham số riêng biệt, trong khi apply()
nhận tham số là một mảng.
function saySomething(message) {
return this.name + ' is ' + message;
}
var person4 = { name: 'John' };
saySomething.apply(person4, ['awesome']);
bind()
Phương thức này trả về một hàm mới, trong đó giá trị của "this" sẽ được liên kết với đối tượng sở hữu, được cung cấp dưới dạng một tham số.
Ví dụ:
var bikeDetails = {
displayDetails: function (registrationNumber, brandName) {
return this.name + ' , ' + 'bike details: ' + registrationNumber + ' , ' + brandName;
},
};
var person1 = { name: 'Vivek' };
var detailsOfPerson1 = bikeDetails.displayDetails.bind(person1, 'TS0122', 'Bullet');
// Binds the displayDetails function to the person1 object
detailsOfPerson1();
// Returns Vivek, bike details: TS0452, Thunderbird
Câu 12. Currying trong JavaScript là gì?
Currying là một kỹ thuật nâng cao để biến đổi một hàm với n tham số, thành n hàm với một tham số duy nhất.
Ví dụ:
function add(a) {
return function (b) {
return a + b;
};
}
add(3)(4);
Trong ví dụ trên, ta có hàm f(a,b)
sau khi currying ta đã biến đổi nó thành f(a)(b)
.
Bằng cách sử dụng kỹ thuật currying, chúng ta không thay đổi chức năng của một hàm, mà chỉ thay đổi cách nó được gọi.
function multiply(a, b) {
return a * b;
}
function currying(fn) {
return function (a) {
return function (b) {
return fn(a, b);
};
};
}
var curriedMultiply = currying(multiply);
multiply(4, 3); // Returns 12
curriedMultiply(4)(3); // Also returns 12
Như ta có thể thấy trong đoạn code trên, chúng ta đã chuyển đổi hàm multiply(a, b)
thành một hàm curriedMultiply
, nhận một tham số tại một thời điểm.
Câu 13. Giải thích về Scope và Scope Chain?
Scope trong JS, xác định khả năng truy cập của các biến, hàm ở các phần khác nhau trong một đoạn code.
Nói chung, Scope cho biết phạm vi mà biến và hàm của ta có thể hay không thể truy cập. Có 3 loại scope trong JS:
- Global Scope
- Local hay Function Scope
- Block Scope
Global Scope
Các biến hoặc hàm được khai báo trong namespace global đều có global scope, có nghĩa là tất cả các biến và hàm có global scope có thể được truy cập từ bất kỳ đâu bên trong code.
var globalVariable = 'Hello world';
function sendMessage() {
return globalVariable; // can access globalVariable since it's written in global space
}
function sendMessage2() {
return sendMessage(); // Can access sendMessage function since it's written in global space
}
sendMessage2(); // Returns “Hello world”
Function Scope
Bất kỳ biến hoặc hàm nào được khai báo bên trong một hàm đều có function scope, có nghĩa là tất cả các biến và hàm được khai báo bên trong một hàm, có thể được truy cập từ bất cứ đâu bên trong hàm chứ không phải bên ngoài nó.
function awesomeFunction() {
var a = 2;
var multiplyBy2 = function () {
console.log(a * 2); // Can access variable "a" since a and multiplyBy2 both are written inside the same function
};
}
console.log(a); // Throws reference error since a is written in local scope and cannot be accessed outside
multiplyBy2(); // Throws reference error since multiplyBy2 is written in local scope
Block Scope
Block Scope liên quan đến các biến được khai báo bằng let và const. Các biến được khai báo với var không có block scope.
Block scope cho chúng ta biết rằng bất kỳ biến nào được khai báo bên trong một khối {}
, chỉ có thể được truy cập bên trong khối đó và không thể được truy cập bên ngoài khối đó.
{
let x = 45;
}
console.log(x); // Gives reference error since x cannot be accessed outside of the block
for (let i = 0; i < 2; i++) {
// do something
}
console.log(i); // Gives reference error since i cannot be accessed outside of the for loop block
Scope Chain
JavaScript Engine cũng sử dụng scope để tìm biến. Ví dụ:
var y = 24;
function favFunction() {
var x = 667;
var anotherFavFunction = function () {
console.log(x); // Does not find x inside anotherFavFunction, so looks for variable inside favFunction, outputs 667
};
var yetAnotherFavFunction = function () {
console.log(y); // Does not find y inside yetAnotherFavFunction, so looks for variable inside favFunction and does not find it, so looks for variable in global scope, finds it and outputs 24
};
anotherFavFunction();
yetAnotherFavFunction();
}
favFunction();
Như bạn có thể thấy trong đoạn code trên, nếu javascript engine không tìm thấy biến trong function scope, nó sẽ cố gắng kiểm tra biến ở phạm vi bên ngoài. Nếu biến không tồn tại trong phạm vi bên ngoài, nó sẽ cố gắng tìm biến trong global scope.
Nếu biến cũng không được tìm thấy trong không gian chung, thì lỗi tham chiếu sẽ được đưa ra.
Câu 14. Giải thích về Closures trong JavaScript?
Closures
là khả năng của một hàm ghi nhớ các biến và hàm được khai báo bên ngoài phạm vi của nó.
var Person = function (pName) {
var name = pName;
this.getName = function () {
return name;
};
};
var person = new Person('Neelesh');
console.log(person.getName());
Hiểu closure qua ví dụ sau:
function randomFunc() {
var obj1 = { name: 'Vivian', age: 45 };
return function () {
console.log(obj1.name + ' is ' + 'awesome'); // Has access to obj1 even when the randomFunc function is executed
};
}
var initialiseClosure = randomFunc(); // Returns a function
initialiseClosure();
Trong đoạn code trên:
Hàm randomFunc()
được thực thi và trả về một hàm khi ta thực hiện phép gán:
var initialiseClosure = randomFunc();
Hàm được trả về được thực thi khi ta gọi initialiseClosure
:
initialiseClosure();
Kết quả sẽ là "Vivian is awesome" điều này xảy ra là do closure.
Khi hàm randomFunc()
chạy, nó sẽ thấy rằng hàm trả về đang sử dụng biến obj1
bên trong nó:
console.log(obj1.name + ' is ' + 'awesome');
Do đó, randomFunc()
, thay vì hủy giá trị của obj1
sau khi thực thi, sẽ lưu giá trị lại vào trong bộ nhớ để tham khảo thêm. Đây là lý do tại sao hàm trả về có thể sử dụng biến được khai báo bên ngoài phạm vi ngay cả sau khi hàm đã được thực thi.
Khả năng này của một hàm nhằm lưu trữ một biến để tham khảo thêm ngay cả sau khi nó được thực thi, được gọi là Closure.
Câu 15. Prototype là gì?
Tất cả các đối tượng javascript đều kế thừa các thuộc tính từ một prototype.
Ví dụ:
Đối tượng Date kế thừa các thuộc tính từ prototype Date.
Đối tượng Math kế thừa các thuộc tính từ prototype Math.
Đối tượng Array kế thừa các thuộc tính từ prototype Array.
Trên đầu chuỗi là Object.prototype
. Mọi prototype đều kế thừa các thuộc tính và phương thức từ Object.prototype
.
Prototype là một bản thiết kế của một đối tượng. Prototype cho phép chúng ta sử dụng các thuộc tính và phương thức trên một đối tượng ngay cả khi các thuộc tính và phương thức không tồn tại trên đối tượng hiện tại.
Ví dụ:
var arr = [];
arr.push(2);
console.log(arr); // Outputs [2]
Trong đoạn code trên, có thể thấy ta chưa xác định bất kỳ thuộc tính hoặc phương thức nào được gọi là push trên mảng arr
nhưng javascript engine không đưa ra lỗi.
Lý do là việc sử dụng các prototype. Như đã thảo luận trước đây, các đối tượng Array kế thừa các thuộc tính từ prototype Array.
Javascript engine thấy rằng phương thức push
không tồn tại trên đối tượng mảng hiện tại, do đó nó tìm kiếm phương thức push bên trong prototype Array và nó tìm thấy phương thức.
Bất cứ khi nào thuộc tính hoặc phương thức không được tìm thấy trên đối tượng hiện tại, javascript engine sẽ luôn tìm kiếm trong prototype của nó và nếu nó vẫn không tồn tại, nó sẽ tìm bên trong prototype của prototype, v.v.
Câu 16. Callback là gì?
Callback là một hàm sẽ được thực thi sau khi một hàm khác được thực thi.
Trong javascript, các hàm được coi như là first-class citizens, chúng có thể được sử dụng như một tham số của một hàm khác, có thể được trả về bởi một hàm khác và có thể được sử dụng như một thuộc tính của một đối tượng.
Các hàm được sử dụng làm tham số cho một hàm khác được gọi là hàm callback.
Ví dụ:
function divideByHalf(sum) {
console.log(Math.floor(sum / 2));
}
function multiplyBy2(sum) {
console.log(sum * 2);
}
function operationOnSum(num1, num2, operation) {
var sum = num1 + num2;
operation(sum);
}
operationOnSum(3, 3, divideByHalf); // Outputs 3
operationOnSum(5, 5, multiplyBy2); // Outputs 20
Trong đoạn code trên, ta đang thực hiện các phép toán cộng hai số.
Hàm operationOnSum
nhận 3 tham số, num1
, num2
và operator
sẽ được thực hiện trên tổng của chúng (callback).
Cả hai hàm splitByHalf
và MultiBy2
đều được sử dụng làm hàm callback trong đoạn code trên.
Các hàm callback này chỉ được thực thi sau khi thực thi hàm operationOnSum
.
Do đó, callback là một hàm sẽ được thực thi sau khi một hàm khác được thực thi.
Câu 17. Memoization là gì?
Memoization là một dạng bộ nhớ đệm trong đó giá trị trả về của một hàm được lưu vào bộ đệm dựa trên các tham số của nó. Nếu tham số của hàm đó không được thay đổi, phiên bản được lưu trong bộ nhớ cache của hàm sẽ được trả về.
Chúng ta có thể hiểu memoization, bằng cách chuyển đổi một hàm đơn giản thành một hàm được ghi nhớ:
function addTo256(num) {
return num + 256;
}
addTo256(20); // Returns 276
addTo256(40); // Returns 296
addTo256(20); // Returns 276
Trong đoạn code trên, chúng ta đã viết một hàm thêm tham số vào 256 và trả về nó.
Khi gọi lại hàm addTo256
với cùng một tham số (“20” trong trường hợp trên), ta đang tính toán lại kết quả cho cùng một tham số.
Tính toán kết quả với cùng một tham số không phải là vấn đề lớn trong trường hợp trên, nhưng hãy tưởng tượng nếu hàm thực hiện một số công việc nặng nhọc, thì việc tính toán kết quả lặp đi lặp lại với cùng một tham số sẽ dẫn đến lãng phí thời gian.
Đây là lúc memoization xuất hiện, bằng cách sử dụng memoization, chúng ta có thể lưu trữ (bộ nhớ đệm) các kết quả được tính toán dựa trên các tham số. Nếu cùng một tham số được sử dụng lại trong khi gọi hàm, thay vì tính toán kết quả, chúng ta trực tiếp trả về giá trị đã lưu trữ (đã lưu trong bộ nhớ cache).
Hãy chuyển đổi hàm addTo256
ở trên, thành một hàm được ghi nhớ:
function memoizedAddTo256() {
var cache = {};
return function (num) {
if (num in cache) {
console.log('cached value');
return cache[num];
} else {
cache[num] = num + 256;
return cache[num];
}
};
}
var memoizedFunc = memoizedAddTo256();
memoizedFunc(20); // Normal return
memoizedFunc(20); // Cached return
Trong đoạn code trên, nếu ta chạy hàm memoizedFunc
với cùng một tham số, thay vì tính toán lại kết quả, nó sẽ trả về kết quả được lưu trong bộ nhớ cache.
Lưu ý Mặc dù sử dụng memoization tiết kiệm thời gian, nhưng nó dẫn đến tiêu thụ bộ nhớ lớn hơn vì ta đang lưu trữ tất cả các kết quả được tính toán.
Câu 18. Đệ quy là gì?
Đệ quy là một kỹ thuật lặp đi lặp lại một hoạt động bằng cách tự gọi hàm lặp đi lặp lại cho đến khi nó đi đến kết quả.
function add(number) {
if (number <= 0) {
return 0;
} else {
return number + add(number - 1);
}
}
add(3) => 3 + add(2)
3 + 2 + add(1)
3 + 2 + 1 + add(0)
3 + 2 + 1 + 0 = 6
Ví dụ về một hàm đệ quy:
Hàm sau đây tính tổng của tất cả các phần tử trong một mảng bằng cách sử dụng đệ quy:
function computeSum(arr) {
if (arr.length === 1) {
return arr[0];
} else {
return arr.pop() + computeSum(arr);
}
}
computeSum([7, 8, 9, 99]); // Returns 123
Câu 19. Hàm khởi tạo trong JavaScript
Các hàm khởi tạo được sử dụng để tạo các đối tượng trong javascript.
Khi nào chúng ta sử dụng các hàm khởi tạo?
Nếu chúng ta muốn tạo nhiều đối tượng có các thuộc tính và phương thức giống nhau, các hàm khởi tạo được sử dụng.
Lưu ý - Tên của một hàm khởi tạo phải luôn được viết bằng Ký hiệu Pascal: mọi từ phải bắt đầu bằng một chữ cái viết hoa.
Ví dụ:
function Person(name, age, gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
var person1 = new Person('Vivek', 76, 'male');
console.log(person1);
var person2 = new Person('Courtney', 34, 'female');
console.log(person2);
Trong đoạn code trên, ta đã tạo một hàm khởi tạo có tên là Person.
Bất cứ khi nào chúng ta muốn tạo một đối tượng mới kiểu Person,
Chúng ta cần tạo nó bằng cách sử dụng từ khóa mới:
var person3 = new Person('Lilly', 17, 'female');
Dòng code trên sẽ tạo một đối tượng mới kiểu Person.
Các hàm khởi tạo cho phép chúng ta nhóm các đối tượng tương tự nhau.
Câu 20. DOM là gì?
DOM là viết tắt của Document Object Model.
DOM là interface lập trình của tài liệu XML và HTML.
Khi trình duyệt cố gắng hiển thị một tài liệu HTML, nó sẽ tạo một đối tượng dựa trên tài liệu HTML được gọi là DOM. Sử dụng DOM này, chúng ta có thể thao tác hoặc thay đổi các phần tử khác nhau bên trong tài liệu HTML.
Ví dụ về cách code HTML được chuyển đổi thành DOM:
Câu 21. Hàm arrow là gì?
Arrow function được giới thiệu từ phiên bản ES6 của javascript.
Nó cung cấp một cú pháp mới và ngắn hơn cho khai báo hàm. Hàm arrow có thể xử dụng như là một biểu thức hàm. Ta sẽ so sánh khai báo hàm thông thường với hàm arrow.
// Traditional Function Expression
var add = function (a, b) {
return a + b;
};
// Arrow Function Expression
var arrowAdd = (a, b) => a + b;
Hàm arrow khai báo mà không cần từ khoá function
. Nếu nó chỉ trả về một biểu thức hàm thì ta không cần sử dụng từ khoá return
như trong ví dụ trên. Ngoài ra, đối với các hàm chỉ có một dòng, có thể bỏ qua dấu ngoặc nhọn {}
.
// Traditional function expression
var multiplyBy2 = function (num) {
return num * 2;
};
// Arrow function expression
var arrowMultiplyBy2 = (num) => num * 2;
Nếu hàm chỉ nhận một tham số, ta có thể bỏ dấu ngoặc đơn xung quanh nó như ví dụ trên.
var obj1 = {
valueOfThis: function () {
return this;
},
};
var obj2 = {
valueOfThis: () => {
return this;
},
};
obj1.valueOfThis(); // Will return the object obj1
obj2.valueOfThis(); // Will return window/global object
Sự khác biệt lớn nhất giữa nhất giữa hàm truyền thống với arrow, là ở từ khoá this.
Như định nghĩa, từ khoá this tham chiếu đến đối tượng chứa hàm được gọi.
Như bạn có thể thấy, obj1.valueOfThis()
trả về obj1
, vì từ khoá this
tham chiếu đến đối tượng chứa hàm đang gọi.
Còn ở hàm arrow, không có ràng buộc nào của từ khóa this.
Từ khoá this trong hàm arrow, không tham chiếu đến đối tượng đang gọi nó. Nó kế thừa giá trị của nó từ phạm vi cha là window object
trong trường hợp này.
Do đó, trong đoạn code trên, obj2.valueOfThis()
trả về window object.
Câu 22. Sự khác biết của biến dùng var, let và const
Từ phiên bản ES6, từ khoá let và const được thêm vào cho khai báo biến.
keyword | const | let | var |
---|---|---|---|
global scope | no | no | yes |
function scope | yes | yes | yes |
block scope | yes | yes | no |
can be reassigned | no | yes | yes |
Ta thử xem các ví dụ:
var variable1 = 23;
let variable2 = 89;
function catchValues() {
console.log(variable1);
console.log(variable2);
// Both the variables can be accessed anywhere since they are declared in the global scope
}
window.variable1; // Returns the value 23
window.variable2; // Returns undefined
Các biến được khai báo với từ khóa let
trong global scope sẽ hoạt động giống như biến được khai báo với từ khóa var
trong global scope.
Các biến được khai báo trong global scope với từ khóa var và let có thể được truy cập từ bất kỳ đâu trong code.
Nhưng, có một sự khác biệt!
Các biến được khai báo với từ khóa var trong global scope được thêm vào đối tượng window/global. Do đó, chúng có thể được truy cập bằng window.variableName
.
Trong khi đó, các biến được khai báo với từ khóa let không được thêm vào đối tượng toàn cục, do đó, việc cố gắng truy cập các biến đó bằng cách sử dụng window.variableName
sẽ dẫn đến lỗi.
var và let ở function scope
function varVsLetFunction() {
let awesomeCar1 = 'Audi';
var awesomeCar2 = 'Mercedes';
}
console.log(awesomeCar1); // Throws an error
console.log(awesomeCar2); // Throws an error
Các biến được khai báo trong function scope bằng cách sử dụng var
và let
hoạt động hoàn toàn giống nhau, nghĩa là chúng không thể được truy cập từ bên ngoài phạm vi.
{
var variable3 = [1, 2, 3, 4];
}
console.log(variable3); // Outputs [1,2,3,4]
{
let variable4 = [6, 55, -1, 2];
}
console.log(variable4); // Throws error
for (let i = 0; i < 2; i++) {
//Do something
}
console.log(i); // Throws error
for (var j = 0; j < 2; i++) {
// Do something
}
console.log(j); // Outputs 2
Trong javascript, một khối có nghĩa là code được viết bên trong dấu ngoặc nhọn {}
.
Các biến được khai báo với từ khóa var không có block scope. Nó có nghĩa là một biến được khai báo trong block scope {}
với từ khóa var cũng giống như khai báo biến trong global scope.
Các biến được khai báo với từ khóa let bên trong block scope không thể được truy cập từ bên ngoài khối.
const
Các biến với từ khóa const hoạt động chính xác như một biến được khai báo với từ khóa let chỉ có một điểm khác biệt, bất kỳ biến nào được khai báo với từ khóa const đều là hằng số, tức là không thể được gán lại.
const x = { name: 'Vivek' };
x = { address: 'India' }; // Throws an error
x.name = 'Nikhil'; // No error is thrown
const y = 23;
y = 44; // Throws an error
Trong đoạn code trên, mặc dù chúng ta có thể thay đổi giá trị của một thuộc tính bên trong biến được khai báo với từ khóa const, nhưng chúng ta không thể gán lại hoàn toàn chính biến đó.
Câu 23. Tham số còn lại (rest) và toán tử spread là gì?
Cả hai đều được giới thiệu ở phiên bản ES6
Tham số còn lại rest (...)
Nó cung cấp một cách cải tiến để xử lý các tham số của một hàm.
Sử dụng cú pháp tham số còn lại, chúng ta có thể tạo ra hàm có thể nhận một số đối số thay đổi.
Bất kỳ số lượng đối số nào sẽ được chuyển đổi thành một mảng bằng cách sử dụng tham số còn lại.
Nó cũng giúp giải nén tất cả hoặc một số phần của các tham số.
Tham số còn lại có thể được sử dụng bằng cách áp dụng ba dấu chấm (...) trước các tham số.
function extractingArgs(...args) {
return args[1];
}
// extractingArgs(8,9,1); // Returns 9
function addAllArgs(...args) {
let sumOfArgs = 0;
let i = 0;
while (i < args.length) {
sumOfArgs += args[i];
i++;
}
return sumOfArgs;
}
addAllArgs(6, 5, 7, 99); // Returns 117
addAllArgs(1, 3, 4); // Returns 8
Lưu ý Tham số còn lại nên được dùng như là tham số cuối cùng của hàm
// Incorrect way to use rest parameter
function randomFunc(a,...args,c){
//Do something
}
// Correct way to use rest parameter
function randomFunc2(a,b,...args){
//Do something
}
Toán tử spread (...)
Mặc dù cú pháp của toán tử spread hoàn toàn giống với tham số còn lại, nhưng toán tử spread được sử dụng để trải ra các phần tử của một đối tượng có thể lặp lại. Toán tử spread được sử dụng trong các câu lệnh, biểu thức hoặc khi gọi hàm.
function addFourNumbers(num1, num2, num3, num4) {
return num1 + num2 + num3 + num4;
}
let fourNumbers = [5, 6, 7, 8];
addFourNumbers(...fourNumbers);
// Spreads [5,6,7,8] as 5,6,7,8
let array1 = [3, 4, 5, 6];
let clonedArray1 = [...array1];
// Spreads the array into 3,4,5,6
console.log(clonedArray1); // Outputs [3,4,5,6]
let obj1 = { x: 'Hello', y: 'Bye' };
let clonedObj1 = { ...obj1 }; // Spreads and clones obj1
console.log(obj1);
let obj2 = { z: 'Yes', a: 'No' };
let mergedObj = { ...obj1, ...obj2 }; // Spreads both the objects and merges it
console.log(mergedObj);
// Outputs {x:'Hello', y:'Bye',z:'Yes',a:'No'};
Lưu ý sự khác biệt giữa tham số còn lại và toán tử spread.
- Tham số còn lại được dùng để nhận một lượng tham số có thể thay đổi và chuyển nó thành mảng, trong khi toán tử spread lấy một mảng/đối tượng và trải nó.
- Tham số còn lại dùng cho khai báo hàm còn toán tử spread dùng cho gọi hàm
Câu 24. Sử dụng Promise trong JavaScript
Promise dùng cho xử lý bất đồng bộ trong JavaScript
Trước promise, callback được dùng cho các thao tác bất đồng bộ. Nhưng callback có giới hạn của nó, nếu sử dụng quá nhiều callback code sẽ trở nên khó quản lý.
Đối tượng promise có 4 trạng thái:
- Pending: trạng thái bắt đầu, biểu diễn promise không phải là fulfilled, cũng không phải là rejected mà đang ở trạng thái pending.
- Fulfilled: trạng thái này có nghĩa là thao tác bất đồng bộ đã hoàn tất.
- Rejected: trạng này này có nghĩa là thao tác đã thất bại vì một vài lý do nào đó.
- Settked: trạng thái này thể hiện promise đã rejected hoặc fulfilled.
Một promise được tạo bằng cách sử dụng phương thức khởi tạo Promise, hàm này nhận một hàm callback với hai tham số, resolve
và reject
tương ứng.
- resolve hàm được gọi, khi thao tác bất đồng bộ thực hiện thành công.
- reject hàm được gọi, khi thao tác thất bại bởi một vài lỗi nào đó.
Ví dụ:
Promise được dùng cho các thao tác bất đồng bộ như yêu cầu của server, để dễ hiểu ta lấy ví dụ với một phép toán để tính tổng của ba phần tử.
function sumOfThreeElements(...elements) {
return new Promise((resolve, reject) => {
if (elements.length > 3) {
reject('Only three elements or less are allowed');
} else {
let sum = 0;
let i = 0;
while (i < elements.length) {
sum += elements[i];
i++;
}
resolve('Sum has been calculated: ' + sum);
}
});
}
Trong đoạn code trên, ta đang tính tổng của ba phần tử, nếu độ dài của mảng phần tử lớn hơn 3, thì promise sẽ bị rejected, ngược lại thì promise sẽ được resolved và tổng được trả về.
Chúng ta có thể sử dụng bất kỳ promise nào bằng cách gắn các phương thức then()
và catch()
vào đối tượng sử dụng.
- then() phương này được truy cập khi kết quả của promise là fulfilled.
- catch() phương này được truy cập khi kết quả của promise là rejected.
Ví dụ:
sumOfThreeElements(4, 5, 6)
.then((result) => console.log(result))
.catch((error) => console.log(error));
// In the code above, the promise is fulfilled so the then() method gets executed
sumOfThreeElements(7, 0, 33, 41)
.then((result) => console.log(result))
.catch((error) => console.log(error));
// In the code above, the promise is rejected hence the catch() method gets executed
Câu 25. Lớp trong JavaScript?
Được thêm vào ở phiên bản ES6, các lớp không là gì ngoài các cú pháp tuyệt vời cho các hàm khởi tạo.
Chúng cung cấp một cách mới để khai báo các hàm khởi tạo trong javascript.
Dưới đây là các ví dụ về cách các lớp được khai báo và sử dụng:
// Before ES6 version, using constructor functions
function Student(name, rollNumber, grade, section) {
this.name = name;
this.rollNumber = rollNumber;
this.grade = grade;
this.section = section;
}
// Way to add methods to a constructor function
Student.prototype.getDetails = function () {
return 'Name: ${this.name}, Roll no: ${this.rollNumber}, Grade: ${this.grade}, Section:${this.section}';
};
let student1 = new Student('Vivek', 354, '6th', 'A');
student1.getDetails();
// Returns Name: Vivek, Roll no:354, Grade: 6th, Section:A
// ES6 version classes
class Student {
constructor(name, rollNumber, grade, section) {
this.name = name;
this.rollNumber = rollNumber;
this.grade = grade;
this.section = section;
}
// Methods can be directly added inside the class
getDetails() {
return 'Name: ${this.name}, Roll no: ${this.rollNumber}, Grade:${this.grade}, Section:${this.section}';
}
}
let student2 = new Student('Garry', 673, '7th', 'C');
student2.getDetails();
// Returns Name: Garry, Roll no:673, Grade: 7th, Section:C
Các điều cần nhớ về lớp:
- Không như function, các lớp không được hoisting. Chúng cần khai báo trước khi sử dụng.
- Một lớp có thể kế thừa thuộc tính và phương thức từ lớp khác bằng từ khoá
extends
. - Tất cả cú pháp trong lớp phải theo tuần thủ chế độ strict (
use strict
). Lỗi sẽ xuất hiện nếu các quy tắc trong chế độ strict không được tuân thủ.
Câu 26. Hàm generator là gì?
Được giới thiệu ở phiên bản ES6, generator function là một lớp đặc biệt của hàm.
Chúng có thể được dừng lại giữa chừng và sau đó tiếp tục từ nơi nó đã dừng lại.
Hàm generator được khai báo với từ khoá function *:
function* genFunc() {
// Perform operation
}
Trong các hàm thông thường, ta sử dụng return
để trả về giá trị, và ngay sau khi khi lệnh return
được thực thi, việc thực thi hàm sẽ dừng lại.
function normalFunc() {
return 22;
console.log(2); // This line of code does not get executed
}
Còn với các hàm generator, khi được gọi nó sẽ không thực thi code thay vào đó nó trả về một đối tượng generator. Đối tượng generator này xử lý việc thực thi:
function* genFunc() {
yield 3;
yield 4;
}
genFunc(); // Returns Object [Generator] {}
Đối tượng generator bao gồm một phương thức được gọi là next()
, phương thức này khi được gọi sẽ thực thi code cho đến câu lệnh yield
gần nhất và trả về giá trị yield
.
Ví dụ: nếu ta chạy phương thức next()
trên đoạn code trên:
genFunc().next(); // Returns {value: 3, done:false}
Như ta có thể thấy phương thức next()
trả về một đối tượng bao gồm thuộc tính value
và done
.
- Thuộc tính value đại diện cho giá trị thu được.
- Thuộc tính done cho ta biết code của hàm đã hoàn thành hay chưa. (Trả về true nếu kết thúc).
Các hàm generator được sử dụng để trả về các vòng lặp. Hãy xem một ví dụ trong đó một vòng lặp được trả về:
function* iteratorFunc() {
let count = 0;
for (let i = 0; i < 2; i++) {
count++;
yield i;
}
return count;
}
let iterator = iteratorFunc();
console.log(iterator.next()); // {value:0,done:false}
console.log(iterator.next()); // {value:1,done:false}
console.log(iterator.next()); // {value:2,done:true}
Như bạn có thể thấy trong đoạn code trên, dòng cuối cùng trả về done: true
, vì code này đạt đến cuối hàm.
Câu 27. Giải thích WeakSet trong JavaScript
Trong Javascript, Set là tập hợp các phần tử đơn nhất và có thứ tự.
Giống như Set, WeakSet cũng là tập hợp các phần tử đơn nhất và có thứ tự. Nhưng có vài điểm khác:
- Weakset chỉ chứa các đối tượng và không có loại nào khác.
- Một đối tượng bên trong weakset có tham chiếu yếu. Điều này có nghĩa là, nếu đối tượng bên trong weakset không có tham chiếu, nó sẽ được bộ dọn dẹp thu gom.
- Không giống như Set, WeakSet chỉ có ba phương thức, add(), delete() và has().
const newSet = new Set([4, 5, 6, 7]);
console.log(newSet); // Outputs Set {4,5,6,7}
const newSet2 = new WeakSet([3, 4, 5]); //Throws an error
let obj1 = { message: 'Hello world' };
const newSet3 = new WeakSet([obj1]);
console.log(newSet3.has(obj1)); // true
Câu 28. Giải thích về WeakMap trong JavaScript
Trong JS, Map được dùng để lưu các cặp key-value. Các cặp key-value có thể là kiểu nguyên thuỷ hoặc đối tượng.
WeakMap gần giống Map với vài điểm khác biệt:
- Key và value trong weakmap phải là kiểu đối tượng.
- Nếu không có tham chiếu đến đối tượng, đối tượng sẽ bị dọn dep.
const map1 = new Map();
map1.set('Value', 1);
const map2 = new WeakMap();
map2.set('Value', 2.3); // Throws an error
let obj = { name: 'Vivek' };
const map3 = new WeakMap();
map3.set(obj, { age: 23 });
Câu 29. Cấu trúc đối tượng là gì?
Cấu trúc đối tượng là một cách mới để trích xuất các phần tử từ một đối tượng hoặc một mảng.
Trước ES6:
const classDetails = {
strength: 78,
benches: 39,
blackBoard: 1,
};
const classStrength = classDetails.strength;
const classBenches = classDetails.benches;
const classBlackBoard = classDetails.blackBoard;
Ví dụ tương tự sử dụng cấu trúc đối tượng:
const classDetails = {
strength: 78,
benches: 39,
blackBoard: 1,
};
const { strength: classStrength, benches: classBenches, blackBoard: classBlackBoard } = classDetails;
console.log(classStrength); // Outputs 78
console.log(classBenches); // Outputs 39
console.log(classBlackBoard); // Outputs 1
Như mọi người có thể thấy, bằng cách sử dụng cấu trúc đối tượng, ta đã trích xuất tất cả các phần tử bên trong một đối tượng trong một dòng code.
Nếu chúng ta muốn biến mới của mình có cùng tên với thuộc tính của một đối tượng, chúng ta có thể bỏ dấu hai chấm:
const { strength: strength } = classDetails;
// The above line of code can be written as:
const { strength } = classDetails;
Cấu trúc mảng:
Trước ES6:
const arr = [1, 2, 3, 4];
const first = arr[0];
const second = arr[1];
const third = arr[2];
const fourth = arr[3];
Sau có ES6:
const arr = [1, 2, 3, 4];
const [first, second, third, fourth] = arr;
console.log(first); // Outputs 1
console.log(second); // Outputs 2
console.log(third); // Outputs 3
console.log(fourth); // Outputs 4
Câu 30. Temporal Dead Zone là gì?
Temporal Dead Zone là hành vi xảy ra với khai báo biến sử dụng let hoặc const.
Nó là hành vi xảy ra khi ta truy cập một biến trước khi khởi tạo.
Ví dụ:
x = 23; // Gives reference error
let x;
function anotherRandomFunc() {
message = 'Hello'; // Throws a reference error
let message;
}
anotherRandomFunc();
Trong đoạn code trên, cả trong global scope và function scope, ta đang cố gắng truy cập các biến chưa được khai báo. Đây được gọi là Temporal Dead Zone.