Hỏi và Đáp Phỏng Vấn Frontend - ES6

1. Phân biệt ES5 và ES6, trình bày những điểm mới của ES6

ECMAScript5 (ES5) là phiên bản thứ năm của ECMAScript, được chuẩn hóa vào năm 2009

ECMAScript6 (ES6) là phiên bản thứ sáu, hoàn thành vào năm 2015, còn được gọi là ES2015

ES6 là một cải tiến so với ES5, ngắn gọn hơn và giúp tăng hiệu suất phát triển

Một số tính năng mới của ES6:

  1. let và const: let để khai báo biến, const cho hằng số, cả hai đều có phạm vi khối ES5 không có khối phạm vi, và var có nâng biến, trong khi với let, biến phải được khai báo trước khi sử dụng

  2. Hàm mũi tên ES6 cho phép định nghĩa hàm không dùng từ khóa function(), mà sử dụng ()=>

  3. Chuỗi mẫu Chuỗi mẫu là phiên bản cải tiến của chuỗi thông thường, sử dụng dấu ngoặc ngược (`), có thể dùng như chuỗi thông thường hoặc định nghĩa chuỗi nhiều dòng

  4. Phân cấu trúc gán ES6 cho phép trích giá trị từ mảng và đối tượng theo một mẫu cụ thể để gán cho biến

  5. Vòng lặp for...of Vòng lặp for...of có thể duyệt qua mảng, Set, Map, một số đối tượng giống mảng, đối tượng và chuỗi

  6. Import, export ES6 hỗ trợ mô-đun (module) nguyên bản. Chia mã JS thành các phần nhỏ chức năng, viết các chức năng khác nhau vào các tệp riêng biệt, mỗi mô-đun chỉ xuất phần giao diện công cộng, sau đó có thể sử dụng ở nơi khác qua import

  7. Cấu trúc dữ liệu Set Set giống mảng, tất cả giá trị là duy nhất không trùng lặp. Nó là một hàm tạo

  8. Toán tử trải ... Có thể mở rộng giá trị trong mảng hoặc đối tượng; cũng có thể gom nhiều giá trị thành một biến

  9. Decorator @ Decorator là một hàm dùng để sửa hành vi của lớp hoặc phương thức. Về bản chất, decorator là hàm thực thi tại thời gian biên dịch

  10. Kế thừa lớp ES6 không sử dụng chuỗi nguyên mẫu như ES5 để thực hiện kế thừa, mà giới thiệu khái niệm Class

  11. async, await Sử dụng async/await kết hợp với promise, có thể xử lý luồng bất đồng bộ bằng cách viết mã giống đồng bộ, tăng tính ngắn gọn và dễ đọc async dùng để khai báo một hàm là bất đồng bộ, trong khi await dùng để chờ một phương thức bất đồng bộ hoàn thành

  12. Promise Promise là một giải pháp lập trình bất đồng bộ, hợp lý và mạnh mẽ hơn các giải pháp truyền thống (hàm callback và sự kiện)

  13. Symbol Symbol là một kiểu nguyên bản. Symbol được tạo bằng cách gọi hàm symbol, nhận một tham số tên tùy chọn, giá trị trả về là duy nhất

  14. Proxy Sử dụng proxy để giám sát thao tác trên đối tượng, sau đó có thể thực hiện các hành động tương ứng

2. Khác biệt giữa var, let, const

var có thể khai báo lại, trong khi let không được

var không bị giới hạn bởi khối, trong khi let bị giới hạn

var ánh xạ với window (thuộc tính được gắn), trong khi let không

var có thể truy cập biến ở trên khai báo, trong khi let có vùng chết tạm thời, truy cập ở trên khai báo sẽ báo lỗi

const phải được gán giá trị sau khi khai báo, nếu không sẽ báo lỗi

const định nghĩa hằng số, thay đổi sẽ báo lỗi

const và let không ánh xạ với window, hỗ trợ phạm vi khối, truy cập biến ở trên khai báo sẽ báo lỗi

3. Cần lưu ý gì khi sử dụng hàm mũi tên?

(1) Dùng hàm mũi tên, this không còn trỏ đến window mà trỏ đến cấp cha (có thể thay đổi)

(2) Không thể sử dụng đối tượng arguments

(3) Không thể dùng làm hàm tạo, tức là không thể dùng lệnh new, nếu không sẽ ném ra lỗi

(4) Không thể sử dụng lệnh yield, do đó hàm mũi tên không thể dùng làm Generator function

4. Chuỗi mẫu của ES6 có tính năng mới nào? và thực hiện một chức năng tương tự chuỗi mẫu

Chuỗi mẫu cơ bản. Nhúng biểu thức vào chuỗi để nối. Dùng ${} để phân định

Trong ES5, chúng ta dùng dấu gạch chéo ngược () để làm chuỗi nhiều dòng hoặc nối chuỗi từng dòng. ES6 dùng dấu ngoặc ngược (``) để giải quyết

        let name = 'web';
        let age = 10;
        let str = 'Xin chào, ${name} đã ${age} tuổi'
        str = str.replace(/\$\{([^}]*)\}/g,function(){
            return eval(arguments[1]);
        })
        console.log(str);//Xin chào, web đã 10 tuổi    

5. Giới thiệu sự khác biệt giữa Set và Map?

Ứng dụng: Set dùng để tái tổ chức dữ liệu, Map dùng để lưu trữ dữ liệu

Set:

(1) Thành viên không được trùng lặp (2) Chỉ có giá trị không có khóa, giống mảng (3) Có thể duyệt, có các phương thức add, delete, has

Map:

(1) Về bản chất là tập hợp cặp khóa-giá trị, giống tập hợp (2) Có thể duyệt, có thể chuyển đổi với các định dạng dữ liệu khác nhau

6. Cách viết class trong ECMAScript 6, tại sao lại xuất hiện class?

Class trong ES6 có thể coi là một cú pháp đường hóa, hầu hết các chức năng của nó đều có thể thực hiện được bằng ES5, cách viết class mới chỉ làm cho cách viết nguyên mẫu đối tượng rõ ràng hơn, giống với cú pháp lập trình hướng đối tượng hơn

//Định nghĩa lớp
class Diem { 
  constructor(x,y) { 
      //Phương thức khởi tạo
       this.x = x; //this đại diện cho đối tượng thực thể
       this.y = y; 
  } toString() {
       return '(' + this.x + ',' + this.y + ')'; 
  }
}

7. Constructor của Promise thực thi đồng bộ hay bất đồng bộ, vậy phương thức thì sao?

Constructor của Promise thực thi đồng bộ, phương thức then thực thi bất đồng bộ

8. Sự khác biệt giữa setTimeout, Promise, Async/Await

Trong vòng lặp sự kiện, có hàng đợi tác vụ macro và hàng đợi tác vụ micro

Callback của setTimeout được đặt vào hàng đợi tác vụ macro, chờ khi stack thực thi trống rồi mới thực hiện

Callback trong promise.then được đặt vào hàng đợi tác vụ micro của tác vụ macro tương ứng, sau khi code đồng bộ trong tác vụ macro hoàn thành thì thực hiện

Hàm async có thể chứa phương thức bất đồng bộ, await theo sau một biểu thức

Khi thực thi hàm async, gặp await sẽ thực thi biểu thức ngay lập tức, sau đó đặt code sau biểu thức vào hàng đợi tác vụ micro, nhường stack thực thi để code đồng bộ thực hiện trước

9. Promise có bao nhiêu trạng thái, khi nào vào catch?

Ba trạng thái: pending, fulfilled, reject

Hai quá trình: padding -> fulfilled, padding -> rejected

Khi pending là rejectd, sẽ vào catch

10. Kết quả xuất đoạn code sau là gì

const promise = new Promise((resolve, reject) => {
    console.log(1);
    resolve();
    console.log(2);
})

promise.then(() => {
    console.log(3);
})

console.log(4);

1 2 4 3

Promise được tạo ra sẽ thực thi ngay lập tức, nên xuất 1,2 trước, trong khi code trong Promise.then() sẽ thực thi ngay sau cùng của vòng lặp sự kiện hiện tại, nên tiếp tục xuất 4, cuối cùng xuất 3

11. Sử dụng phân cấu trúc gán, thực hiện hoán giá trị của hai biến

let a = 1;let b = 2;
[a,b] = [b,a];

12. Thiết kế một đối tượng, ít nhất một khóa có kiểu Symbol, và duyệt tất cả các key

 let ten = Symbol('ten');
 let sanPham = {
    [ten]: "Máy giặt",    
    "gia":799
  };
  Reflect.ownKeys(sanPham);

13. Với cấu trúc Set sau, giá trị size in ra là bao nhiêu

let s = new Set();
s.add([1]);
s.add([1]);console.log(s.size);

Đáp án: 2

Hai mảng [1] không phải cùng một giá trị, chúng được định nghĩa riêng biệt, trong bộ nhớ tương ứng với các địa chỉ lưu trữ khác nhau, do đó không phải là giá trị giống nhau

Đều có thể lưu trong cấu trúc Set, nên size là 2

14. Sự khác biệt trong xử lý giữa reject và catch trong Promise

reject dùng để ném ra ngoại lệ, catch dùng để xử lý ngoại lệ

reject là phương thức của Promise, còn catch là phương thức của thực thể Promise

Sau reject, chắc chắn sẽ vào callback thứ hai của then, nếu then không viết callback thứ hai, sẽ vào catch

Lỗi mạng (như mất kết nối), sẽ vào catch trực tiếp mà không vào callback thứ hai của then

15. Viết một Promise bằng class

  //Tạo một lớp Promise
  class Promise{
    constructor(thucThi){//Hàm khởi tạo constructor chứa một thực thi
      this.status = 'pending';//Trạng thái mặc định pending
      this.value = undefined//Giá trị thành công mặc định undefined
      this.reason = undefined//Giá trị thất bại mặc định undefined
      //Chỉ khi ở trạng thái pending thì mới có thể thay đổi
      let resolveFn = value =>{
        //Chỉ khi đang chờ mới có thể resolve thành công
        if(this.status == pending){
          this.status = 'resolve';
          this.value = value;
        }
      }
      //Chỉ khi đang chờ mới có thể reject thất bại
      let rejectFn = reason =>{
        if(this.status == pending){
          this.status = 'reject';
          this.reason = reason;
        }
      }    
      try{
        //Truyền hai hàm resolve và reject cho thực thi executer
        thucThi(resolve,reject);
      }catch(e){
        reject(e);//Thất bại thì vào catch
      }
    }
    then(thanhCong,thatBai){
      //Nếu trạng thái thành công gọi thanhCong
      if(this.status = 'resolve'){
        thanhCong(this.value);
      }
      //Nếu trạng thái thất bại gọi thatBai
      if(this.status = 'reject'){
        thatBai(this.reason);
      }
    }
  } 

16. Cách sử dụng Set để loại bỏ trùng lặp

let arr = [12,43,23,43,68,12];
let item = [...new Set(arr)];
console.log(item);//[12, 43, 23, 68]

17. Chuyển vòng lặp for thành for of

let arr = [11,22,33,44,55];
let sum = 0;
for(let i=0;i<arr.length;i++){
    sum += arr[i];
}

Đáp án:

let arr = [11,22,33,44,55];
let sum = 0;
for(giaTri of arr){
    sum += giaTri;
}

18. Hiểu về async/await và ưu điểm so với Generator

async await dùng để giải quyết bất đồng bộ, hàm async là cú pháp đường hóa của Generator function

Sử dụng từ khóa async để biểu thị, trong hàm sử dụng await để biểu thị bất đồng bộ

Hàm async trả về một đối tượng Promise, có thể sử dụng phương thức then để thêm hàm callback

Khi thực thi hàm, gặp await sẽ trả về ngay, đợi thao tác bất đồng bộ hoàn thành rồi mới tiếp tục thực thi các câu lệnh sau trong hàm

Ưu điểm của async so với Generator:

(1) Có trình thực thi tích hợp. Hàm Generator phải dựa vào trình thực thi, trong khi hàm Async có trình thực thi riêng, cách gọi giống như hàm thông thường

(2) Ngữ nghĩa tốt hơn. async và await so với * và yield có ngữ nghĩa rõ ràng hơn

(3) Tính ứng dụng rộng hơn. Lệnh yield sau chỉ có thể là hàm Thunk hoặc đối tượng Promise, trong khi await sau trong hàm async có thể là Promise hoặc giá trị kiểu nguyên bản

(4) Trả về Promise. Hàm async trả về đối tượng Promise, tiện hơn đối tượng Iterator trả về của hàm Generator, có thể sử dụng trực tiếp phương thức then()

19. Sự khác biệt giữa forEach, for in, for of

forEach chủ yếu dùng để duyệt mảng

for in thường dùng để duyệt đối tượng hoặc json

for of có thể duyệt cả mảng và đối tượng, duyệt đối tượng cần kết hợp với Object.keys()

for in duyệt ra key, for of duyệt ra value

20. Giải thích về nhập/xuất mô-đun trong ES6

Nhập bằng từ khóa import

// Chỉ nhập một
import {tong} from "./viDu.js"

// Nhập nhiều
import {tong,nhan,thoiGian} from "./xuatViDu.js"

// Nhập toàn bộ mô-đun
import * as viDu from "./xuatViDu.js"

Xuất bằng từ khóa export

//Có thể đặt export trước bất kỳ biến, hàm hoặc khai báo lớp nào
export var ho = 'Michael';
export var ten = 'Jackson';
export var namSinh = 1958;

//Cũng có thể dùng ngoặc nhọn để chỉ định một tập hợp biến cần xuất
var ho = 'Michael';
var ten = 'Jackson';
var namSinh = 1958;
export {ho, ten, namSinh};

//Khi sử dụng export default, câu lệnh import tương ứng không cần dùng ngoặc nhọn
let ham = function(){}
export default ham;
import hamMoi from 'ham';

//Khi không sử dụng export default, câu lệnh import tương ứng cần dùng ngoặc nhọn
let ham = function(){}
export ham;
import {ham} from 'ham';

Thẻ: es6 JavaScript frontend promise Async

Đăng vào ngày 31 tháng 5 lúc 06:33