Bộ câu hỏi phỏng vấn Javascript phần 7

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

Làm thế nào để so sánh hai object trong JavaScript?


Các giá trị non-primitive, như các object (bao gồm cả hàm và mảng) được lưu dưới dạng tham chiếu, vì vậy cả hai phép so sánh ===== sẽ chỉ kiểm tra xem các tham chiếu có khớp nhau hay không, chứ không phải kiểm tra bất kỳ điều gì về các giá trị cơ bản.

Ví dụ: theo mặc định, mảng được ép thành chuỗi bằng cách chỉ cần nối tất cả các giá trị bằng dấu phẩy (,) ở giữa. Vì vậy, hai mảng có cùng nội dung sẽ không là true khi so sánh bằng ==

var a = [1, 2, 3];
var b = [1, 2, 3];
var c = "1,2,3";
a == c; // true
b == c; // true
a == b; // false

Để deep-comparison các object, hãy sử dụng các thư viện bên ngoài như deep-equal hoặc triển khai một thuật toán của riêng bạn. Dưới đây là một ví dụ

function deepObjectEqual(object1, object2) {
  const keys1 = Object.keys(object1);
  const keys2 = Object.keys(object2);

  if (keys1.length !== keys2.length) {
    return false;
  }

  for (const key of keys1) {
    const val1 = object1[key];
    const val2 = object2[key];
    const areObjects = isObject(val1) && isObject(val2);

    if (
      (areObjects && !deepObjectEqual(val1, val2)) ||
      (!areObjects && val1 !== val2)
    ) {
      return false;
    }
  }

  return true;
}

function isObject(object) {
  return object != null && typeof object === "object";
}

Như bạn thấy bên trên, đoạn areObjects && !deepObjectEqual(val1, val2) ta thực hiện đệ quy để có thể lặp hết vào trong các thuộc tính của object. Cùng xem ví dụ thử nhé:

const object1 = {
  value: 1,
  name: "Test",
  address: {
    city: "Ha Noi",
  },
};

const object2 = {
  value: 1,
  name: "Test",
  address: {
    city: "Ha Noi",
  },
};

deepObjectEqual(object1, object2); // => true

So sánh sâu sẽ giúp ta xác định một cách chính xác 2 object có thực sự bằng nhau về mặt nội dung hay không.

Output của đoạn code dưới đây là gì?

var x = 1;
var output = (function () {
  delete x;
  return x;
})();
console.log(output);

Đoạn mã trên có output là 1. Toán tử delete được sử dụng để xóa thuộc tính khỏi object. x ở đây không phải là một object mà là biến toàn cục (global variable) có kiểu là number.

Anonymous Function thường dùng cho trường hợp nào?


Anonymous Function có thể được sử dụng trong IIFE để đóng gói một số mã trong phạm vi cục bộ để các biến được khai báo trong đó không bị rò rỉ ra phạm vi toàn cục.

(function () {
  // Some code here.
})();

Anonymous Function như một callback được sử dụng một lần và không cần sử dụng ở bất kỳ nơi khác. Code sẽ có vẻ tự kiểm soát và dễ đọc hơn khi các trình xử lý được xác định ngay bên trong code gọi chúng, thay vì phải tìm kiếm ở nơi khác để tìm phần thân hàm.

setTimeout(function () {
  console.log("Hello world!");
}, 1000);

Đối số đến cấu trúc functional programming hoặc Lodash (tương tự như callback).

const arr = [1, 2, 3];
const double = arr.map(function (el) {
  return el * 2;
});
console.log(double); // [2, 4, 6]

Yêu cầu bạn hãy xóa các phần tử duplicate trong mảng, bạn sẽ làm như thế nào?


Hãy sử dụng đối tượng Set(), Sets là một kiểu đối tượng mới trong ES6 (ES2015) cho phép tạo các collections có các giá trị không trùng nhau.

Các giá trị trong một set có thể là các giá trị nguyên thủy đơn giản như chuỗi hoặc số nguyên, hoặc các kiểu đối tượng phức tạp hơn như các đối tượng hoặc mảng. Ví dụ

const array = [1, 4, 99, 3, 1, 4, 15];
const noDups = Array.from(new Set(array));
console.log(noDups); //[1, 4, 99, 3, 15]

Generator trong Javascript là gì?


Generator là các chức năng có thể được exitre-entered sau đó. Context của chúng sẽ được lưu qua các lần truy cập lại. Các hàm Generator được viết bằng cú pháp

function* nameYourFuntion([param[, param[, ... param]]]) {
   statements
}

Trong đó thì:

  • nameYourFuntion: tên hàm
  • param: tham số đầu vào của hàm, tối đa 255 tham số
  • statements: phần thân chứa nội dung của hàm.

Khi chúng ta gọi Generator function nameYourFuntion() , nó không trả về các kiểu dữ liệu cơ bản mà đẩy về một iterator object. Hàm next() của iterator object được sử dụng để truy xuất các node dữ liệu sau mỗi bước resume lại generator function. Khi đó generator function sẽ thực thi hàm cho đến khi gặp từ khóa yield , hoặc return kế tiếp chưa được duyệt ở bước trước. (iterator định nghĩa một chuẩn để tạo ra list các giá trị hữu hạn hoặc thậm chí vô hạn. Nó giúp bạn lấy ra giá trị mong muốn khi list kết quả đã được khởi tạo xong toàn bộ.)

Yield được sử dụng ở một vài nơi và có vẻ khái niệm cũng hơi khác nhau. Về cơ bản, yeild là từ khóa dùng để tạm dừng và cũng để tiếp tục việc thực thi bên trong generator function. Ví dụ

function* generatorFunc(index) {
  while (index < 2) {
    yield index++;
  }
}

const iterator = generatorFunc(0);

console.log(iterator.next());
// log output: {value : 0, done : false}

console.log(iterator.next());
// log output: {value : 1, done : false}

console.log(iterator.next());
// log output: {value : underfined, done : true}

Như đã đề cập ở trên thì iterator được khởi tạo bằng generatorFunc với index bắt đầu bằng 0. Bạn có thể thấy yeild ở đây, trong ví dụ này chính là một phiên bản khác giống như return vậy. Nó trả về một đối tượng IteratorResult với hai thuộc tính là "value" và "done".

value : kết quả của biểu thức trả về.
done : nhận giá trị false nếu quá trình generator chưa hoàn thành, true nếu ngược lại.

Giá trị của index được giữ lại sau mỗi lần chúng ta gọi next() và tất nhiên là cả ngữ cảnh của hàm generator cũng thế cho đến khi toàn bộ yield, return đã được duyệt qua..

Làm thế nào có thể ghi ra giá trị giống như trong comment khi console.log?

function* startGame() {
  const answer = yield "Do you love JavaScript?";
  if (answer !== "Yes") {
    return "Oh wow... Guess we're gone here";
  }
  return "JavaScript loves you back ❤️";
}

const game = startGame();
console.log(/* 1 */); // Do you love JavaScript?
console.log(/* 2 */); // JavaScript loves you back ❤️
  • A: game.next("Yes").value and game.next().value
  • B: game.next.value("Yes") and game.next.value()
  • C: game.next().value and game.next("Yes").value
  • D: game.next.value() and game.next.value("Yes")

Đáp án: C

Một generator sẽ "tạm dừng" khi nhìn thấy từ khóa yield. Đầu tiên ra sẽ đưa ra chuỗi "Do you love JavaScript?", bằng cách gọi game.next().value.

Chương trình sẽ chạy từng dòng, cho tới khi nó tìm thấy từ khóa yield. Có một từ khóa yield tại dòng đầu tiên của hàm: chương trình sẽ dừng tại đâ! Điều đó có nghĩa là biến answer chưa hề được định nghĩa!

Khi ta gọi game.next("Yes").value, yield trước đó sẽ được thay thế bởi giá trị được truyền vào hàm next(), trong trường hợp này là"Yes". Theo đó giá trị của biến answer giờ sẽ là "Yes". Điều kiện if sẽ trả về false, và JavaScript loves you back ❤️ sẽ được ghi ra.

Symbol trong ES6 là gì?


Symbol là một loại đối tượng mới, đặc biệt, có thể được sử dụng như một tên thuộc tính duy nhất trong các đối tượng.

Sử dụng Symbol thay vì string cho phép các modules khác nhau tạo ra các thuộc tính không xung đột với nhau. Các symbols cũng có thể được đặt ở chế độ riêng tư, để các thuộc tính của chúng không thể bị truy cập bởi những ai chưa có quyền truy cập trực tiếp vào Symbol.

Symbol là một loại primitive mới. Cũng giống như các primitive khác như number, string và boolean, Symbol có một hàm được sử dụng để tạo chúng. Không giống như các primitive khác, Symbols không có literal syntax (Literal là một giá trị mà nó thể hiện chính nó, ví dụ: số 12 -- thể hiện là một giá trị kiểu number, hay “Javascript” -- đại diện cho một giá trị kiểu string) - cách duy nhất để tạo chúng là với hàm tạo Symbol theo cách sau:

let symbol = Symbol();

Trên thực tế, các Symbol chỉ là một cách hơi khác để gắn các thuộc tính vào một đối tượng - bạn có thể dễ dàng cung cấp các Symbol thường dùng dưới dạng các phương thức chuẩn, giống như Object.prototype.hasOwnProperty xuất hiện trong mọi thứ kế thừa từ Object.

Kết quả đoạn code sau là gì?

function* generator(i) {
yield i;
yield i * 2;
}
const gen = generator(10);
console.log(gen.next().value);
console.log(gen.next().value);
  • A: [0, 10], [10, 20]
  • B: 20, 20
  • C: 10, 20
  • D: 0, 10 and 10, 20

Đáp án: C

Một hàm bình thường không thể bị dừng giữa chừng khi được gọi. Tuy nhiên một generator thì khác, nó có thể "dừng lại" được, và sau đó nó sẽ tiếp tục từ vị trí nó dừng lại. Mỗi khi một generator gặp một từ khóa yield, nó sẽ sinh ra giá trị ngay phía sau nó. Chú ý là generator không trả về giá trị, nó sinh ra giá trị.

Đầu tiên, chúng ta khởi tạo generator với giá trị i10. Generator được gọi bằng cách sử dụng phương thức next(). Khi lần đầu gọi thì i vẫn là 10. Khi nó bắt gặp từ khóa yield: nó sẽ sinh ra giá trị i. Generator sẽ được "tạm dừng" tại đây, và ghi ra giá trị 10.

Sau đó chung ta tiếp tục gọi generator bằng cách sử dụng tiếp phương thức next(). Nó sẽ bắt đầu từ vị trí nó tạm dừng lúc trước, khi i vẫn đang là 10. Và khi nó bắt gặp từ khóa yield, nó sẽ sinh ra giá trị i * 2. i10, nên nó sẽ sinh ra 10 * 2, tức 20. Vậy kết quả cuối cùng là 10, 20.

Giá trị trả về là gì?

const firstPromise = new Promise((res, rej) => {
  setTimeout(res, 500, "one");
});

const secondPromise = new Promise((res, rej) => {
  setTimeout(res, 100, "two");
});

Promise.race([firstPromise, secondPromise]).then(res => console.log(res));
  • A: "one"
  • B: "two"
  • C: "two" "one"
  • D: "one" "two"

Đáp án: B

Khi chúng ta đưa các promise vào trong một hàm Promise.race, nó sẽ chỉ resolves hay rejects promise đầu tiên được resolves/rejects. Với hàm setTimeout, chúng ta đưa vào một khoảng thời gian: 500 mili giây cho promise đầu tiên (firstPromise), và 100 mili giây cho promise thứ hai (secondPromise). Nó có nghĩa là secondPromise sẽ hoàn thành trước và trả về giá trị 'two'. res khi này sẽ nhận giá trị 'two' và được in ra console.

Kết quả đoạn code sau là gì?

let person = { name: "Lydia" };
const members = [person];
person = null;
console.log(members);
  • A: null
  • B: [null]
  • C: [{}]
  • D: [{ name: "Lydia" }]

Đáp án: D

Đầu tiên, chúng ta khai báo một biến person là một object có thuộc tính name.

Sau đó chúng ta khai báo một biến members. Ta set giá trị đầu tiên của mảng là giá trị của biến person. Khi sử dụng gán bằng, object sẽ được tham chiếu tới object mà nó được gán. Khi ta gán tham chiếu từ một biến sang biến khác, ta tạo ra một bản sao của tham chiếu đó. (nên nhớ rằng đó vẫn là 2 tham chiếu hoàn toàn khác nhau!)

Sau đó ta set giá trị của person bằng null.

Chúng ta chỉ đơn thuần là thay đổi giá trị của biến person mà thôi, chứ không phải giá trị của phần tử đầu tiên ở trong mảng, vì chúng ta có một tham chiếu khác đến object đó. Phần tử đầu tiên của mảng members vẫn giữ tham chiêu đến object gốc. Do vậy, khi chúng ta in ra mảng members, phần tử đầu tiên sẽ vẫn in ra giá trị của objet gốc.

Avatar Techmely Team
VIẾT BỞI

Techmely Team